[syslinux:master] Update gPXE to version 1.0.0

syslinux-bot for H. Peter Anvin hpa at zytor.com
Tue Feb 2 22:42:12 PST 2010


Commit-ID:  702c7fbe7342403472e887508716add0c58c385c
Gitweb:     http://syslinux.zytor.com/commit/702c7fbe7342403472e887508716add0c58c385c
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Tue, 2 Feb 2010 22:41:00 -0800
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Tue, 2 Feb 2010 22:41:00 -0800

Update gPXE to version 1.0.0



---
 gpxe/gpxe.diff                                     |   15 +
 gpxe/src/Makefile                                  |   24 +-
 gpxe/src/Makefile.housekeeping                     |  220 ++-
 gpxe/src/README.pixify                             |   90 -
 gpxe/src/arch/i386/Makefile                        |   33 +-
 gpxe/src/arch/i386/Makefile.pcbios                 |   29 +-
 gpxe/src/arch/i386/core/basemem_packet.c           |    2 +
 gpxe/src/arch/i386/core/etherboot.prefix.lds       |  100 -
 gpxe/src/arch/i386/core/pic8259.c                  |    2 +
 gpxe/src/arch/i386/core/prefixudata.lds            |    8 -
 gpxe/src/arch/i386/core/prefixzdata.lds            |    8 -
 gpxe/src/arch/i386/core/rdtsc_timer.c              |    2 +
 gpxe/src/arch/i386/core/realmode.c                 |   23 -
 gpxe/src/arch/i386/core/relocate.c                 |    2 +
 gpxe/src/arch/i386/core/setjmp.S                   |    2 +
 gpxe/src/arch/i386/core/stack.S                    |    2 +
 gpxe/src/arch/i386/core/stack16.S                  |    2 +
 gpxe/src/arch/i386/core/start16.lds                |    8 -
 gpxe/src/arch/i386/core/start16z.lds               |   65 -
 gpxe/src/arch/i386/core/timer2.c                   |    2 +
 gpxe/src/arch/i386/core/virtaddr.S                 |    2 +
 gpxe/src/arch/i386/core/x86_io.c                   |    2 +
 gpxe/src/arch/i386/drivers/net/undi.c              |    4 +-
 gpxe/src/arch/i386/drivers/net/undiisr.S           |    6 +-
 gpxe/src/arch/i386/drivers/net/undiload.c          |    7 +-
 gpxe/src/arch/i386/drivers/net/undinet.c           |  273 +--
 gpxe/src/arch/i386/drivers/net/undionly.c          |    2 +
 gpxe/src/arch/i386/drivers/net/undipreload.c       |    2 +
 gpxe/src/arch/i386/drivers/net/undirom.c           |    2 +
 gpxe/src/arch/i386/firmware/pcbios/basemem.c       |    2 +
 gpxe/src/arch/i386/firmware/pcbios/bios_console.c  |    2 +
 gpxe/src/arch/i386/firmware/pcbios/e820mangler.S   |    5 +-
 gpxe/src/arch/i386/firmware/pcbios/fakee820.c      |    2 +
 gpxe/src/arch/i386/firmware/pcbios/gateA20.c       |    2 +
 gpxe/src/arch/i386/firmware/pcbios/hidemem.c       |    2 +
 gpxe/src/arch/i386/firmware/pcbios/memmap.c        |    2 +
 gpxe/src/arch/i386/firmware/pcbios/pnpbios.c       |    2 +
 gpxe/src/arch/i386/hci/commands/pxe_cmd.c          |   33 +
 gpxe/src/arch/i386/image/bootsector.c              |    2 +
 gpxe/src/arch/i386/image/bzimage.c                 |  483 ++--
 gpxe/src/arch/i386/image/com32.c                   |    2 +
 gpxe/src/arch/i386/image/comboot.c                 |    2 +
 gpxe/src/arch/i386/image/elfboot.c                 |    2 +
 gpxe/src/arch/i386/image/eltorito.c                |    2 +
 gpxe/src/arch/i386/image/multiboot.c               |   44 +-
 gpxe/src/arch/i386/image/pxe_image.c               |   18 +-
 gpxe/src/arch/i386/include/basemem.h               |    2 +
 gpxe/src/arch/i386/include/basemem_packet.h        |    2 +
 gpxe/src/arch/i386/include/bios.h                  |    2 +
 gpxe/src/arch/i386/include/biosint.h               |    2 +
 gpxe/src/arch/i386/include/bits/byteswap.h         |    2 +
 gpxe/src/arch/i386/include/bits/compiler.h         |    2 +
 gpxe/src/arch/i386/include/bits/endian.h           |    2 +
 gpxe/src/arch/i386/include/bits/errfile.h          |    3 +
 gpxe/src/arch/i386/include/bits/io.h               |    2 +
 gpxe/src/arch/i386/include/bits/nap.h              |    2 +
 gpxe/src/arch/i386/include/bits/smbios.h           |    2 +
 gpxe/src/arch/i386/include/bits/stdint.h           |    6 +-
 gpxe/src/arch/i386/include/bits/timer.h            |    2 +
 gpxe/src/arch/i386/include/bits/uaccess.h          |    2 +
 gpxe/src/arch/i386/include/bits/umalloc.h          |    2 +
 gpxe/src/arch/i386/include/bootsector.h            |    2 +
 gpxe/src/arch/i386/include/bzimage.h               |    5 +
 gpxe/src/arch/i386/include/callbacks_arch.h        |  243 --
 gpxe/src/arch/i386/include/comboot.h               |    2 +
 gpxe/src/arch/i386/include/fakee820.h              |    2 +
 gpxe/src/arch/i386/include/gpxe/abft.h             |    2 +
 gpxe/src/arch/i386/include/gpxe/bios_nap.h         |    2 +
 gpxe/src/arch/i386/include/gpxe/bios_smbios.h      |    2 +
 gpxe/src/arch/i386/include/gpxe/bios_timer.h       |    2 +
 gpxe/src/arch/i386/include/gpxe/ibft.h             |    2 +
 gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h   |    2 +
 gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h      |    2 +
 gpxe/src/arch/i386/include/gpxe/sbft.h             |  125 +
 gpxe/src/arch/i386/include/gpxe/timer2.h           |    2 +
 gpxe/src/arch/i386/include/gpxe/x86_io.h           |    2 +
 gpxe/src/arch/i386/include/int13.h                 |    2 +
 gpxe/src/arch/i386/include/librm.h                 |    2 +
 gpxe/src/arch/i386/include/limits.h                |    2 +
 gpxe/src/arch/i386/include/memsizes.h              |    2 +
 gpxe/src/arch/i386/include/multiboot.h             |    2 +
 gpxe/src/arch/i386/include/pic8259.h               |    2 +
 gpxe/src/arch/i386/include/pnpbios.h               |    2 +
 gpxe/src/arch/i386/include/pxe.h                   |    3 +
 gpxe/src/arch/i386/include/pxe_api.h               |   68 +
 gpxe/src/arch/i386/include/pxe_call.h              |   28 +-
 gpxe/src/arch/i386/include/pxe_types.h             |    2 +
 gpxe/src/arch/i386/include/pxeparent.h             |   11 +
 gpxe/src/arch/i386/include/realmode.h              |    2 +
 gpxe/src/arch/i386/include/registers.h             |    2 +
 gpxe/src/arch/i386/include/setjmp.h                |    2 +
 gpxe/src/arch/i386/include/undi.h                  |    2 +
 gpxe/src/arch/i386/include/undiload.h              |    2 +
 gpxe/src/arch/i386/include/undinet.h               |    2 +
 gpxe/src/arch/i386/include/undipreload.h           |    2 +
 gpxe/src/arch/i386/include/undirom.h               |    2 +
 gpxe/src/arch/i386/interface/pcbios/abft.c         |    2 +
 gpxe/src/arch/i386/interface/pcbios/aoeboot.c      |   62 +-
 gpxe/src/arch/i386/interface/pcbios/bios_nap.c     |    2 +
 gpxe/src/arch/i386/interface/pcbios/bios_smbios.c  |    2 +
 gpxe/src/arch/i386/interface/pcbios/bios_timer.c   |    2 +
 gpxe/src/arch/i386/interface/pcbios/biosint.c      |    2 +
 gpxe/src/arch/i386/interface/pcbios/ib_srpboot.c   |   73 +
 gpxe/src/arch/i386/interface/pcbios/ibft.c         |   17 +-
 gpxe/src/arch/i386/interface/pcbios/int13.c        |   79 +-
 gpxe/src/arch/i386/interface/pcbios/iscsiboot.c    |   20 +-
 gpxe/src/arch/i386/interface/pcbios/keepsan.c      |   26 +
 .../arch/i386/interface/pcbios/memtop_umalloc.c    |    2 +
 gpxe/src/arch/i386/interface/pcbios/pcibios.c      |    2 +
 gpxe/src/arch/i386/interface/pcbios/sbft.c         |  105 +
 gpxe/src/arch/i386/interface/pxe/pxe_call.c        |   93 +-
 gpxe/src/arch/i386/interface/pxe/pxe_entry.S       |    2 +
 gpxe/src/arch/i386/interface/pxe/pxe_file.c        |   42 +
 gpxe/src/arch/i386/interface/pxe/pxe_loader.c      |    5 +-
 gpxe/src/arch/i386/interface/pxe/pxe_preboot.c     |   25 +-
 gpxe/src/arch/i386/interface/pxe/pxe_tftp.c        |   17 +-
 gpxe/src/arch/i386/interface/pxe/pxe_udp.c         |    2 +
 gpxe/src/arch/i386/interface/pxe/pxe_undi.c        |  215 ++-
 gpxe/src/arch/i386/interface/pxeparent/pxeparent.c |  201 ++
 .../arch/i386/interface/pxeparent/pxeparent_dhcp.c |   69 +
 gpxe/src/arch/i386/interface/syslinux/com32_call.c |    2 +
 .../arch/i386/interface/syslinux/com32_wrapper.S   |    2 +
 .../arch/i386/interface/syslinux/comboot_call.c    |   31 +-
 .../arch/i386/interface/syslinux/comboot_resolv.c  |    2 +
 gpxe/src/arch/i386/prefix/bImageprefix.S           |  611 -----
 gpxe/src/arch/i386/prefix/bootpart.S               |    2 +
 gpxe/src/arch/i386/prefix/comprefix.S              |   46 -
 gpxe/src/arch/i386/prefix/dskprefix.S              |    6 +-
 gpxe/src/arch/i386/prefix/elf_dprefix.S            |   94 -
 gpxe/src/arch/i386/prefix/elfprefix.S              |   94 -
 gpxe/src/arch/i386/prefix/exeprefix.S              |   41 -
 gpxe/src/arch/i386/prefix/hdprefix.S               |    6 +-
 gpxe/src/arch/i386/prefix/hromprefix.S             |   12 +
 gpxe/src/arch/i386/prefix/kkpxeprefix.S            |    5 +
 gpxe/src/arch/i386/prefix/kpxeprefix.S             |    2 +
 gpxe/src/arch/i386/prefix/libprefix.S              |    2 +
 gpxe/src/arch/i386/prefix/lkrnprefix.S             |    6 +-
 gpxe/src/arch/i386/prefix/lmelf_dprefix.S          |  161 --
 gpxe/src/arch/i386/prefix/lmelf_prefix.S           |  161 --
 gpxe/src/arch/i386/prefix/nbiprefix.S              |    8 +-
 gpxe/src/arch/i386/prefix/pxeprefix.S              |  100 +-
 gpxe/src/arch/i386/prefix/romprefix.S              |  480 +++-
 gpxe/src/arch/i386/prefix/undiloader.S             |   49 +
 gpxe/src/arch/i386/prefix/unnrv2b.S                |    2 +
 gpxe/src/arch/i386/prefix/unnrv2b16.S              |    9 +
 gpxe/src/arch/i386/prefix/xromprefix.S             |    9 +
 gpxe/src/arch/i386/scripts/i386-kir.lds            |    1 +
 gpxe/src/arch/i386/scripts/i386.lds                |   11 +-
 gpxe/src/arch/i386/transitions/libkir.S            |    2 +
 gpxe/src/arch/i386/transitions/librm.S             |    2 +
 gpxe/src/arch/i386/transitions/librm_mgmt.c        |    2 +
 gpxe/src/arch/x86/Makefile                         |    2 +-
 gpxe/src/arch/x86/core/pcidirect.c                 |    2 +
 gpxe/src/arch/x86/core/x86_string.c                |    2 +
 gpxe/src/arch/x86/include/bits/pci_io.h            |    2 +
 gpxe/src/arch/x86/include/bits/string.h            |    2 +
 gpxe/src/arch/x86/include/gpxe/efi/efix86_nap.h    |    2 +
 gpxe/src/arch/x86/include/gpxe/pcibios.h           |    2 +
 gpxe/src/arch/x86/include/gpxe/pcidirect.h         |    2 +
 gpxe/src/arch/x86/interface/efi/efix86_nap.c       |    2 +
 gpxe/src/arch/x86/prefix/efidrvprefix.c            |    2 +
 gpxe/src/arch/x86/prefix/efiprefix.c               |    2 +
 gpxe/src/arch/x86/scripts/efi.lds                  |    1 +
 gpxe/src/arch/x86_64/include/bits/stdint.h         |    2 +-
 gpxe/src/config/config.c                           |  262 ++
 gpxe/src/config/config_net80211.c                  |   50 +
 gpxe/src/config/config_romprefix.c                 |   24 +
 gpxe/src/config/console.h                          |    2 +
 gpxe/src/config/defaults.h                         |    2 +
 gpxe/src/config/defaults/pcbios.h                  |    6 +
 gpxe/src/config/general.h                          |   31 +-
 gpxe/src/config/ioapi.h                            |    2 +
 gpxe/src/config/nap.h                              |    2 +
 gpxe/src/config/serial.h                           |    9 +-
 gpxe/src/config/timer.h                            |    2 +
 gpxe/src/config/umalloc.h                          |    2 +
 gpxe/src/core/acpi.c                               |    2 +
 gpxe/src/core/ansiesc.c                            |    2 +
 gpxe/src/core/asprintf.c                           |    2 +
 gpxe/src/core/base64.c                             |    2 +
 gpxe/src/core/basename.c                           |    2 +
 gpxe/src/core/bitmap.c                             |    2 +
 gpxe/src/core/bitops.c                             |    2 +
 gpxe/src/core/config.c                             |  224 --
 gpxe/src/core/console.c                            |   11 +-
 gpxe/src/core/cpio.c                               |    2 +
 gpxe/src/core/ctype.c                              |   48 +
 gpxe/src/core/cwuri.c                              |    2 +
 gpxe/src/core/debug.c                              |    3 +-
 gpxe/src/core/device.c                             |    9 +-
 gpxe/src/core/downloader.c                         |   27 +-
 gpxe/src/core/exec.c                               |   14 +-
 gpxe/src/core/filter.c                             |    2 +
 gpxe/src/core/gdbstub.c                            |   17 +-
 gpxe/src/core/getkey.c                             |    2 +
 gpxe/src/core/getopt.c                             |    2 +
 gpxe/src/core/image.c                              |   10 +-
 gpxe/src/core/init.c                               |   25 +-
 gpxe/src/core/interface.c                          |    2 +
 gpxe/src/core/iobuf.c                              |    2 +
 gpxe/src/core/job.c                                |   10 +
 gpxe/src/core/linebuf.c                            |    2 +
 gpxe/src/core/main.c                               |    9 +-
 gpxe/src/core/malloc.c                             |    2 +
 gpxe/src/core/misc.c                               |   17 +-
 gpxe/src/core/monojob.c                            |    2 +
 gpxe/src/core/nvo.c                                |    2 +
 gpxe/src/core/open.c                               |   41 +-
 gpxe/src/core/posix_io.c                           |    4 +-
 gpxe/src/core/process.c                            |   24 +-
 gpxe/src/core/random.c                             |    2 +
 gpxe/src/core/refcnt.c                             |    2 +
 gpxe/src/core/resolv.c                             |   53 +-
 gpxe/src/core/serial.c                             |    2 +
 gpxe/src/core/settings.c                           |  500 +++-
 gpxe/src/core/string.c                             |    2 +
 gpxe/src/core/timer.c                              |    2 +
 gpxe/src/core/uri.c                                |  165 +-
 gpxe/src/core/uuid.c                               |    2 +
 gpxe/src/core/vsprintf.c                           |    2 +
 gpxe/src/core/xfer.c                               |    5 +
 gpxe/src/crypto/aes_wrap.c                         |  123 +
 gpxe/src/crypto/arc4.c                             |  131 +
 gpxe/src/crypto/asn1.c                             |    2 +
 gpxe/src/crypto/axtls_aes.c                        |   15 +-
 gpxe/src/crypto/cbc.c                              |    2 +
 gpxe/src/crypto/chap.c                             |    2 +
 gpxe/src/crypto/crandom.c                          |   55 +
 gpxe/src/crypto/crc32.c                            |   54 +
 gpxe/src/crypto/crypto_null.c                      |    2 +
 gpxe/src/crypto/hmac.c                             |    2 +
 gpxe/src/crypto/md5.c                              |   14 +-
 gpxe/src/crypto/sha1extra.c                        |  165 ++
 gpxe/src/crypto/x509.c                             |    2 +
 gpxe/src/doc/pxe_extensions                        |   33 +
 gpxe/src/doxygen.cfg                               |  434 +++-
 gpxe/src/drivers/bitbash/bitbash.c                 |    2 +
 gpxe/src/drivers/bitbash/i2c_bit.c                 |    2 +
 gpxe/src/drivers/bitbash/spi_bit.c                 |   53 +-
 gpxe/src/drivers/block/ata.c                       |   27 +-
 gpxe/src/drivers/block/ramdisk.c                   |    2 +
 gpxe/src/drivers/block/scsi.c                      |   93 +-
 gpxe/src/drivers/block/srp.c                       |  523 ++++
 gpxe/src/drivers/bus/eisa.c                        |    7 +-
 gpxe/src/drivers/bus/isa.c                         |    9 +-
 gpxe/src/drivers/bus/isapnp.c                      |    9 +-
 gpxe/src/drivers/bus/mca.c                         |    9 +-
 gpxe/src/drivers/bus/pci.c                         |    9 +-
 gpxe/src/drivers/bus/pcibackup.c                   |   90 +
 gpxe/src/drivers/bus/pciextra.c                    |    7 +
 gpxe/src/drivers/infiniband/MT25218_PRM.h          |    2 +
 gpxe/src/drivers/infiniband/MT25408_PRM.h          |    8 +-
 gpxe/src/drivers/infiniband/arbel.c                |   18 +-
 gpxe/src/drivers/infiniband/arbel.h                |    2 +
 gpxe/src/drivers/infiniband/hermon.c               |  702 ++++-
 gpxe/src/drivers/infiniband/hermon.h               |   91 +-
 gpxe/src/drivers/infiniband/ib_packet.c            |  234 --
 gpxe/src/drivers/infiniband/ib_sma.c               |  553 ----
 gpxe/src/drivers/infiniband/ib_smc.c               |  166 --
 gpxe/src/drivers/infiniband/linda.c                |   99 +-
 gpxe/src/drivers/infiniband/linda.h                |    5 +
 gpxe/src/drivers/infiniband/linda_fw.c             |    2 +
 gpxe/src/drivers/infiniband/mlx_bitops.h           |   14 +
 gpxe/src/drivers/infiniband/qib_7220_regs.h        |   11 +-
 gpxe/src/drivers/infiniband/qib_genbits.pl         |   29 +-
 gpxe/src/drivers/net/3c509.c                       |    2 +
 gpxe/src/drivers/net/3c509.h                       |    2 +
 gpxe/src/drivers/net/3c515.c                       |    1 +
 gpxe/src/drivers/net/3c529.c                       |    2 +
 gpxe/src/drivers/net/3c595.c                       |   30 +-
 gpxe/src/drivers/net/3c595.h                       |    2 +
 gpxe/src/drivers/net/3c5x9.c                       |    2 +
 gpxe/src/drivers/net/3c90x.c                       | 1837 +++++++-------
 gpxe/src/drivers/net/3c90x.h                       |  302 +++
 gpxe/src/drivers/net/3c90x.txt                     |  307 ---
 gpxe/src/drivers/net/amd8111e.c                    |    4 +-
 gpxe/src/drivers/net/amd8111e.h                    |    2 +
 gpxe/src/drivers/net/ath5k/ath5k.c                 | 1700 ++++++++++++
 gpxe/src/drivers/net/ath5k/ath5k.h                 | 1279 +++++++++
 gpxe/src/drivers/net/ath5k/ath5k_attach.c          |  340 +++
 gpxe/src/drivers/net/ath5k/ath5k_caps.c            |  154 ++
 gpxe/src/drivers/net/ath5k/ath5k_desc.c            |  544 ++++
 gpxe/src/drivers/net/ath5k/ath5k_dma.c             |  631 +++++
 gpxe/src/drivers/net/ath5k/ath5k_eeprom.c          | 1760 ++++++++++++
 gpxe/src/drivers/net/ath5k/ath5k_gpio.c            |  122 +
 gpxe/src/drivers/net/ath5k/ath5k_initvals.c        | 1560 +++++++++++
 gpxe/src/drivers/net/ath5k/ath5k_pcu.c             |  534 ++++
 gpxe/src/drivers/net/ath5k/ath5k_phy.c             | 2586 ++++++++++++++++++
 gpxe/src/drivers/net/ath5k/ath5k_qcu.c             |  394 +++
 gpxe/src/drivers/net/ath5k/ath5k_reset.c           | 1176 ++++++++
 gpxe/src/drivers/net/ath5k/ath5k_rfkill.c          |  107 +
 gpxe/src/drivers/net/ath5k/base.h                  |  145 +
 gpxe/src/drivers/net/ath5k/desc.h                  |  332 +++
 gpxe/src/drivers/net/ath5k/eeprom.h                |  451 ++++
 gpxe/src/drivers/net/ath5k/reg.h                   | 2589 ++++++++++++++++++
 gpxe/src/drivers/net/ath5k/rfbuffer.h              | 1181 ++++++++
 gpxe/src/drivers/net/ath5k/rfgain.h                |  516 ++++
 gpxe/src/drivers/net/atl1e.c                       | 1758 ++++++++++++
 gpxe/src/drivers/net/atl1e.h                       | 1031 +++++++
 gpxe/src/drivers/net/b44.c                         |   20 +-
 gpxe/src/drivers/net/b44.h                         |    3 +
 gpxe/src/drivers/net/bnx2.c                        |   10 +-
 gpxe/src/drivers/net/bnx2.h                        |  108 +-
 gpxe/src/drivers/net/cs89x0.c                      |    2 +
 gpxe/src/drivers/net/cs89x0.h                      |    2 +
 gpxe/src/drivers/net/davicom.c                     |   10 +-
 gpxe/src/drivers/net/depca.c                       |    2 +
 gpxe/src/drivers/net/dmfe.c                        |   10 +-
 gpxe/src/drivers/net/e1000/e1000.c                 |  392 ++--
 gpxe/src/drivers/net/e1000/e1000.h                 |    2 +
 gpxe/src/drivers/net/e1000/e1000_hw.c              |  156 +-
 gpxe/src/drivers/net/e1000/e1000_hw.h              |   20 +-
 gpxe/src/drivers/net/e1000/e1000_osdep.h           |    1 +
 gpxe/src/drivers/net/eepro.c                       |    2 +
 gpxe/src/drivers/net/eepro100.c                    | 1692 +++++++-----
 gpxe/src/drivers/net/eepro100.h                    |  206 ++
 gpxe/src/drivers/net/epic100.c                     |    6 +-
 gpxe/src/drivers/net/epic100.h                     |    2 +
 gpxe/src/drivers/net/etherfabric.c                 |   92 +-
 gpxe/src/drivers/net/etherfabric.h                 |    2 +
 gpxe/src/drivers/net/etherfabric_nic.h             |    3 +
 gpxe/src/drivers/net/forcedeth.c                   |  102 +-
 gpxe/src/drivers/net/hfa384x.h                     |    2 +
 gpxe/src/drivers/net/ipoib.c                       |  878 ++-----
 gpxe/src/drivers/net/legacy.c                      |    6 +-
 gpxe/src/drivers/net/mtd80x.c                      |   74 +-
 gpxe/src/drivers/net/mtnic.c                       |   13 +-
 gpxe/src/drivers/net/mtnic.h                       |    3 +
 gpxe/src/drivers/net/myri10ge.c                    | 1041 +++++++
 gpxe/src/drivers/net/myri10ge_mcp.h                |  514 ++++
 gpxe/src/drivers/net/natsemi.c                     |    8 +-
 gpxe/src/drivers/net/natsemi.h                     |    2 +
 gpxe/src/drivers/net/ne2k_isa.c                    |    2 +
 gpxe/src/drivers/net/ns83820.c                     |    4 +-
 gpxe/src/drivers/net/ns8390.c                      |   24 +-
 gpxe/src/drivers/net/ns8390.h                      |    2 +
 gpxe/src/drivers/net/p80211hdr.h                   |    2 +
 gpxe/src/drivers/net/pcnet32.c                     |   20 +-
 gpxe/src/drivers/net/phantom/nx_bitops.h           |    2 +
 gpxe/src/drivers/net/phantom/nxhal_nic_interface.h |    2 +
 gpxe/src/drivers/net/phantom/phantom.c             |   22 +-
 gpxe/src/drivers/net/phantom/phantom.h             |    2 +
 gpxe/src/drivers/net/phantom/phantom_hw.h          |    2 +
 gpxe/src/drivers/net/pnic.c                        |    6 +-
 gpxe/src/drivers/net/pnic_api.h                    |    2 +
 gpxe/src/drivers/net/prism2.c                      |    2 +
 gpxe/src/drivers/net/prism2_pci.c                  |    8 +-
 gpxe/src/drivers/net/prism2_plx.c                  |   24 +-
 gpxe/src/drivers/net/r8169.c                       |   27 +-
 gpxe/src/drivers/net/r8169.h                       |   83 +-
 gpxe/src/drivers/net/rtl8139.c                     |   63 +-
 gpxe/src/drivers/net/rtl818x/rtl8180.c             |   17 +
 gpxe/src/drivers/net/rtl818x/rtl8180_grf5101.c     |  186 ++
 gpxe/src/drivers/net/rtl818x/rtl8180_max2820.c     |  158 ++
 gpxe/src/drivers/net/rtl818x/rtl8180_sa2400.c      |  217 ++
 gpxe/src/drivers/net/rtl818x/rtl8185.c             |   14 +
 gpxe/src/drivers/net/rtl818x/rtl8185_rtl8225.c     |  804 ++++++
 gpxe/src/drivers/net/rtl818x/rtl818x.c             |  855 ++++++
 gpxe/src/drivers/net/rtl818x/rtl818x.h             |  359 +++
 gpxe/src/drivers/net/sis190.c                      | 1179 ++++++++
 gpxe/src/drivers/net/sis190.h                      |  311 +++
 gpxe/src/drivers/net/sis900.c                      |   12 +-
 gpxe/src/drivers/net/sis900.h                      |    2 +
 gpxe/src/drivers/net/skge.c                        | 2472 +++++++++++++++++
 gpxe/src/drivers/net/skge.h                        | 2623 ++++++++++++++++++
 gpxe/src/drivers/net/sky2.c                        | 2399 +++++++++++++++++
 gpxe/src/drivers/net/sky2.h                        | 2176 +++++++++++++++
 gpxe/src/drivers/net/smc9000.c                     |    3 +
 gpxe/src/drivers/net/smc9000.h                     |   11 +-
 gpxe/src/drivers/net/sundance.c                    |   15 +-
 gpxe/src/drivers/net/tg3.c                         |   67 +-
 gpxe/src/drivers/net/tg3.h                         |  109 +-
 gpxe/src/drivers/net/tlan.c                        |  125 +-
 gpxe/src/drivers/net/tlan.h                        |   37 +-
 gpxe/src/drivers/net/tulip.c                       |   74 +-
 gpxe/src/drivers/net/via-rhine.c                   |   10 +-
 gpxe/src/drivers/net/via-velocity.c                |    2 +-
 gpxe/src/drivers/net/virtio-net.c                  |    2 +-
 gpxe/src/drivers/net/w89c840.c                     |    6 +-
 gpxe/src/drivers/net/wlan_compat.h                 |    2 +
 gpxe/src/drivers/nvs/nvs.c                         |    2 +
 gpxe/src/drivers/nvs/spi.c                         |    2 +
 gpxe/src/drivers/nvs/threewire.c                   |   55 +-
 gpxe/src/hci/commands/autoboot_cmd.c               |    2 +
 gpxe/src/hci/commands/config_cmd.c                 |    2 +
 gpxe/src/hci/commands/dhcp_cmd.c                   |    2 +
 gpxe/src/hci/commands/digest_cmd.c                 |  120 +
 gpxe/src/hci/commands/ifmgmt_cmd.c                 |   21 +-
 gpxe/src/hci/commands/image_cmd.c                  |   25 +-
 gpxe/src/hci/commands/iwmgmt_cmd.c                 |   69 +
 gpxe/src/hci/commands/login_cmd.c                  |    2 +
 gpxe/src/hci/commands/nvo_cmd.c                    |    2 +
 gpxe/src/hci/commands/route_cmd.c                  |    2 +
 gpxe/src/hci/commands/sanboot_cmd.c                |    2 +
 gpxe/src/hci/commands/time_cmd.c                   |   84 +
 gpxe/src/hci/editstring.c                          |    2 +
 gpxe/src/hci/mucurses/ansi_screen.c                |    2 +
 gpxe/src/hci/mucurses/clear.c                      |    2 +
 gpxe/src/hci/mucurses/colour.c                     |    2 +
 gpxe/src/hci/mucurses/cursor.h                     |    2 +
 gpxe/src/hci/mucurses/mucurses.c                   |    2 +
 gpxe/src/hci/mucurses/mucurses.h                   |    2 +
 gpxe/src/hci/mucurses/print.c                      |    2 +
 gpxe/src/hci/mucurses/widgets/editbox.c            |    2 +
 gpxe/src/hci/mucurses/winattrs.c                   |    2 +
 gpxe/src/hci/mucurses/wininit.c                    |    2 +
 gpxe/src/hci/readline.c                            |    2 +
 gpxe/src/hci/shell.c                               |    9 +-
 gpxe/src/hci/shell_banner.c                        |    2 +
 gpxe/src/hci/strerror.c                            |   19 +-
 gpxe/src/hci/tui/login_ui.c                        |    2 +
 gpxe/src/hci/tui/settings_ui.c                     |   12 +-
 gpxe/src/hci/wireless_errors.c                     |  118 +
 gpxe/src/image/efi_image.c                         |    2 +
 gpxe/src/image/elf.c                               |   57 +-
 gpxe/src/image/embedded.c                          |    2 +
 gpxe/src/image/script.c                            |   10 +-
 gpxe/src/image/segment.c                           |   13 +
 gpxe/src/include/assert.h                          |    2 +
 gpxe/src/include/byteswap.h                        |    2 +
 gpxe/src/include/compiler.h                        |  345 +++-
 gpxe/src/include/console.h                         |    7 +-
 gpxe/src/include/ctype.h                           |    4 +
 gpxe/src/include/curses.h                          |    2 +
 gpxe/src/include/debug.h                           |   28 -
 gpxe/src/include/dhcp.h                            |   12 -
 gpxe/src/include/elf.h                             |    2 +
 gpxe/src/include/endian.h                          |    2 +
 gpxe/src/include/errno.h                           |    2 +
 gpxe/src/include/etherboot.h                       |    2 +
 gpxe/src/include/getopt.h                          |    2 +
 gpxe/src/include/gpxe/acpi.h                       |    2 +
 gpxe/src/include/gpxe/aes.h                        |   22 +
 gpxe/src/include/gpxe/ansiesc.h                    |    2 +
 gpxe/src/include/gpxe/aoe.h                        |    2 +
 gpxe/src/include/gpxe/api.h                        |    2 +
 gpxe/src/include/gpxe/arc4.h                       |   22 +
 gpxe/src/include/gpxe/arp.h                        |    9 +-
 gpxe/src/include/gpxe/asn1.h                       |    2 +
 gpxe/src/include/gpxe/ata.h                        |    4 +
 gpxe/src/include/gpxe/base64.h                     |    2 +
 gpxe/src/include/gpxe/bitbash.h                    |    2 +
 gpxe/src/include/gpxe/bitmap.h                     |    2 +
 gpxe/src/include/gpxe/bitops.h                     |    2 +
 gpxe/src/include/gpxe/blockdev.h                   |    2 +
 gpxe/src/include/gpxe/cbc.h                        |    2 +
 gpxe/src/include/gpxe/chap.h                       |    2 +
 gpxe/src/include/gpxe/command.h                    |    6 +-
 gpxe/src/include/gpxe/cpio.h                       |    2 +
 gpxe/src/include/gpxe/crc32.h                      |   10 +
 gpxe/src/include/gpxe/crypto.h                     |    8 +-
 gpxe/src/include/gpxe/device.h                     |    7 +-
 gpxe/src/include/gpxe/dhcp.h                       |   23 +
 gpxe/src/include/gpxe/dhcpopts.h                   |    2 +
 gpxe/src/include/gpxe/dhcppkt.h                    |    2 +
 gpxe/src/include/gpxe/dns.h                        |    2 +
 gpxe/src/include/gpxe/downloader.h                 |    2 +
 gpxe/src/include/gpxe/eapol.h                      |  112 +
 gpxe/src/include/gpxe/editbox.h                    |    2 +
 gpxe/src/include/gpxe/editstring.h                 |    2 +
 gpxe/src/include/gpxe/efi/efi.h                    |   21 +-
 gpxe/src/include/gpxe/efi/efi_io.h                 |    2 +
 gpxe/src/include/gpxe/efi/efi_pci.h                |    2 +
 gpxe/src/include/gpxe/efi/efi_smbios.h             |    2 +
 gpxe/src/include/gpxe/efi/efi_timer.h              |    2 +
 gpxe/src/include/gpxe/efi/efi_uaccess.h            |    2 +
 gpxe/src/include/gpxe/efi/efi_umalloc.h            |    2 +
 gpxe/src/include/gpxe/eisa.h                       |    7 +-
 gpxe/src/include/gpxe/elf.h                        |    2 +
 gpxe/src/include/gpxe/errfile.h                    |   29 +
 gpxe/src/include/gpxe/errortab.h                   |    6 +-
 gpxe/src/include/gpxe/ethernet.h                   |   28 +-
 gpxe/src/include/gpxe/fakedhcp.h                   |    2 +
 gpxe/src/include/gpxe/features.h                   |   14 +-
 gpxe/src/include/gpxe/filter.h                     |    2 +
 gpxe/src/include/gpxe/ftp.h                        |    2 +
 gpxe/src/include/gpxe/gdbserial.h                  |    2 +
 gpxe/src/include/gpxe/gdbstub.h                    |    6 +-
 gpxe/src/include/gpxe/gdbudp.h                     |    2 +
 gpxe/src/include/gpxe/hidemem.h                    |    2 +
 gpxe/src/include/gpxe/hmac.h                       |    2 +
 gpxe/src/include/gpxe/http.h                       |    2 +
 gpxe/src/include/gpxe/i2c.h                        |    2 +
 gpxe/src/include/gpxe/ib_cm.h                      |   72 +
 gpxe/src/include/gpxe/ib_cmrc.h                    |   20 +
 gpxe/src/include/gpxe/ib_mad.h                     |  205 ++-
 gpxe/src/include/gpxe/ib_mcast.h                   |   48 +
 gpxe/src/include/gpxe/ib_mi.h                      |  135 +
 gpxe/src/include/gpxe/ib_packet.h                  |   14 +-
 gpxe/src/include/gpxe/ib_pathrec.h                 |   76 +
 gpxe/src/include/gpxe/ib_sma.h                     |   59 +-
 gpxe/src/include/gpxe/ib_smc.h                     |    2 +
 gpxe/src/include/gpxe/ib_srp.h                     |   79 +
 gpxe/src/include/gpxe/icmp.h                       |    2 +
 gpxe/src/include/gpxe/icmp6.h                      |    2 +
 gpxe/src/include/gpxe/ieee80211.h                  | 1160 ++++++++
 gpxe/src/include/gpxe/if_arp.h                     |    2 +
 gpxe/src/include/gpxe/if_ether.h                   |    3 +
 gpxe/src/include/gpxe/image.h                      |    8 +-
 gpxe/src/include/gpxe/in.h                         |    3 +-
 gpxe/src/include/gpxe/infiniband.h                 |  218 ++-
 gpxe/src/include/gpxe/init.h                       |   13 +-
 gpxe/src/include/gpxe/interface.h                  |    2 +
 gpxe/src/include/gpxe/io.h                         |    2 +
 gpxe/src/include/gpxe/iobuf.h                      |    2 +
 gpxe/src/include/gpxe/ip.h                         |    2 +
 gpxe/src/include/gpxe/ip6.h                        |    2 +
 gpxe/src/include/gpxe/ipoib.h                      |   31 +-
 gpxe/src/include/gpxe/isa.h                        |    7 +-
 gpxe/src/include/gpxe/isa_ids.h                    |    2 +
 gpxe/src/include/gpxe/isapnp.h                     |    7 +-
 gpxe/src/include/gpxe/iscsi.h                      |   17 +-
 gpxe/src/include/gpxe/job.h                        |    4 +
 gpxe/src/include/gpxe/keys.h                       |    2 +
 gpxe/src/include/gpxe/linebuf.h                    |    2 +
 gpxe/src/include/gpxe/linux_compat.h               |    2 +
 gpxe/src/include/gpxe/list.h                       |    2 +
 gpxe/src/include/gpxe/login_ui.h                   |    2 +
 gpxe/src/include/gpxe/malloc.h                     |    2 +
 gpxe/src/include/gpxe/mca.h                        |    7 +-
 gpxe/src/include/gpxe/md5.h                        |    2 +
 gpxe/src/include/gpxe/memmap.h                     |    2 +
 gpxe/src/include/gpxe/monojob.h                    |    2 +
 gpxe/src/include/gpxe/nap.h                        |    2 +
 gpxe/src/include/gpxe/net80211.h                   | 1186 ++++++++
 gpxe/src/include/gpxe/netdevice.h                  |  135 +-
 gpxe/src/include/gpxe/null_nap.h                   |    2 +
 gpxe/src/include/gpxe/nvo.h                        |    2 +
 gpxe/src/include/gpxe/nvs.h                        |    2 +
 gpxe/src/include/gpxe/open.h                       |   14 +-
 gpxe/src/include/gpxe/pci.h                        |   52 +-
 gpxe/src/include/gpxe/pci_ids.h                    |    3 +
 gpxe/src/include/gpxe/pci_io.h                     |    2 +
 gpxe/src/include/gpxe/pcibackup.h                  |   33 +
 gpxe/src/include/gpxe/posix_io.h                   |    2 +
 gpxe/src/include/gpxe/process.h                    |    9 +-
 gpxe/src/include/gpxe/profile.h                    |    2 +
 gpxe/src/include/gpxe/ramdisk.h                    |    2 +
 gpxe/src/include/gpxe/rarp.h                       |    2 +
 gpxe/src/include/gpxe/rc80211.h                    |   19 +
 gpxe/src/include/gpxe/refcnt.h                     |    2 +
 gpxe/src/include/gpxe/resolv.h                     |    8 +-
 gpxe/src/include/gpxe/retry.h                      |    2 +
 gpxe/src/include/gpxe/rotate.h                     |    2 +
 gpxe/src/include/gpxe/rsa.h                        |    2 +
 gpxe/src/include/gpxe/sanboot.h                    |   10 +-
 gpxe/src/include/gpxe/scsi.h                       |   31 +-
 gpxe/src/include/gpxe/sec80211.h                   |   83 +
 gpxe/src/include/gpxe/segment.h                    |    2 +
 gpxe/src/include/gpxe/serial.h                     |    2 +
 gpxe/src/include/gpxe/settings.h                   |   64 +-
 gpxe/src/include/gpxe/settings_ui.h                |    2 +
 gpxe/src/include/gpxe/sha1.h                       |   11 +
 gpxe/src/include/gpxe/shell.h                      |    2 +
 gpxe/src/include/gpxe/shell_banner.h               |    2 +
 gpxe/src/include/gpxe/smbios.h                     |   21 +
 gpxe/src/include/gpxe/socket.h                     |    2 +
 gpxe/src/include/gpxe/spi.h                        |   10 +
 gpxe/src/include/gpxe/spi_bit.h                    |    2 +
 gpxe/src/include/gpxe/srp.h                        |  868 ++++++
 gpxe/src/include/gpxe/tables.h                     |  263 ++-
 gpxe/src/include/gpxe/tcp.h                        |   12 +
 gpxe/src/include/gpxe/tcpip.h                      |   15 +-
 gpxe/src/include/gpxe/tftp.h                       |    2 +
 gpxe/src/include/gpxe/threewire.h                  |   16 +
 gpxe/src/include/gpxe/timer.h                      |    2 +
 gpxe/src/include/gpxe/tls.h                        |    2 +
 gpxe/src/include/gpxe/uaccess.h                    |    2 +
 gpxe/src/include/gpxe/udp.h                        |    2 +
 gpxe/src/include/gpxe/umalloc.h                    |    2 +
 gpxe/src/include/gpxe/uri.h                        |   46 +-
 gpxe/src/include/gpxe/uuid.h                       |    2 +
 gpxe/src/include/gpxe/vsprintf.h                   |    2 +
 gpxe/src/include/gpxe/wpa.h                        |  503 ++++
 gpxe/src/include/gpxe/x509.h                       |    2 +
 gpxe/src/include/gpxe/xfer.h                       |    2 +
 gpxe/src/include/hci/ifmgmt_cmd.h                  |   30 +
 gpxe/src/include/igmp.h                            |   42 -
 gpxe/src/include/lib.h                             |   42 -
 gpxe/src/include/libgen.h                          |    2 +
 gpxe/src/include/little_bswap.h                    |    2 +
 gpxe/src/include/mii.h                             |  122 +-
 gpxe/src/include/nfs.h                             |   63 -
 gpxe/src/include/nic.h                             |    4 +-
 gpxe/src/include/nmb.h                             |   22 -
 gpxe/src/include/readline/readline.h               |    2 +
 gpxe/src/include/stdarg.h                          |    2 +
 gpxe/src/include/stddef.h                          |    2 +
 gpxe/src/include/stdint.h                          |   12 +
 gpxe/src/include/stdio.h                           |    2 +
 gpxe/src/include/stdlib.h                          |    2 +
 gpxe/src/include/string.h                          |    2 +
 gpxe/src/include/strings.h                         |    2 +
 gpxe/src/include/unistd.h                          |    2 +
 gpxe/src/include/usr/autoboot.h                    |    6 +-
 gpxe/src/include/usr/dhcpmgmt.h                    |    2 +
 gpxe/src/include/usr/ifmgmt.h                      |    2 +
 gpxe/src/include/usr/imgmgmt.h                     |    2 +
 gpxe/src/include/usr/iwmgmt.h                      |   17 +
 gpxe/src/include/usr/route.h                       |    2 +
 gpxe/src/interface/efi/efi_console.c               |    2 +
 gpxe/src/interface/efi/efi_init.c                  |   18 +-
 gpxe/src/interface/efi/efi_io.c                    |    2 +
 gpxe/src/interface/efi/efi_pci.c                   |    2 +
 gpxe/src/interface/efi/efi_smbios.c                |    2 +
 gpxe/src/interface/efi/efi_snp.c                   |   21 +-
 gpxe/src/interface/efi/efi_strerror.c              |    2 +
 gpxe/src/interface/efi/efi_timer.c                 |    2 +
 gpxe/src/interface/efi/efi_uaccess.c               |    2 +
 gpxe/src/interface/efi/efi_umalloc.c               |    2 +
 gpxe/src/interface/smbios/smbios.c                 |    2 +
 gpxe/src/interface/smbios/smbios_settings.c        |   27 +-
 gpxe/src/libgcc/icc.c                              |    8 +
 gpxe/src/net/80211/net80211.c                      | 2829 ++++++++++++++++++++
 gpxe/src/net/80211/rc80211.c                       |  371 +++
 gpxe/src/net/80211/sec80211.c                      |  503 ++++
 gpxe/src/net/80211/wep.c                           |  303 +++
 gpxe/src/net/80211/wpa.c                           |  973 +++++++
 gpxe/src/net/80211/wpa_ccmp.c                      |  528 ++++
 gpxe/src/net/80211/wpa_psk.c                       |  125 +
 gpxe/src/net/80211/wpa_tkip.c                      |  586 ++++
 gpxe/src/net/aoe.c                                 |   15 +-
 gpxe/src/net/arp.c                                 |   13 +-
 gpxe/src/net/cachedhcp.c                           |   76 +
 gpxe/src/net/dhcpopts.c                            |   21 +-
 gpxe/src/net/dhcppkt.c                             |   14 +-
 gpxe/src/net/eapol.c                               |   85 +
 gpxe/src/net/ethernet.c                            |   56 +-
 gpxe/src/net/fakedhcp.c                            |    2 +
 gpxe/src/net/icmp.c                                |    2 +
 gpxe/src/net/infiniband.c                          |  405 +++-
 gpxe/src/net/infiniband/ib_cm.c                    |  413 +++
 gpxe/src/net/infiniband/ib_cmrc.c                  |  436 +++
 gpxe/src/net/infiniband/ib_mcast.c                 |  218 ++
 gpxe/src/net/infiniband/ib_mi.c                    |  406 +++
 gpxe/src/net/infiniband/ib_packet.c                |  244 ++
 gpxe/src/net/infiniband/ib_pathrec.c               |  296 ++
 gpxe/src/net/infiniband/ib_sma.c                   |  369 +++
 gpxe/src/net/infiniband/ib_smc.c                   |  179 ++
 gpxe/src/net/infiniband/ib_srp.c                   |  406 +++
 gpxe/src/net/iobpad.c                              |    2 +
 gpxe/src/net/ipv4.c                                |   41 +-
 gpxe/src/net/mii.c                                 |  147 +
 gpxe/src/net/netdev_settings.c                     |   38 +-
 gpxe/src/net/netdevice.c                           |   66 +-
 gpxe/src/net/nullnet.c                             |    2 +
 gpxe/src/net/rarp.c                                |    2 +
 gpxe/src/net/retry.c                               |    3 +
 gpxe/src/net/tcp.c                                 |  141 +-
 gpxe/src/net/tcp/ftp.c                             |   30 +-
 gpxe/src/net/tcp/http.c                            |   64 +-
 gpxe/src/net/tcp/https.c                           |    2 +
 gpxe/src/net/tcp/iscsi.c                           |   79 +-
 gpxe/src/net/tcpip.c                               |   17 +-
 gpxe/src/net/tls.c                                 |    4 +-
 gpxe/src/net/udp.c                                 |    4 +-
 gpxe/src/net/udp/dhcp.c                            |  419 +++-
 gpxe/src/net/udp/dns.c                             |    4 +-
 gpxe/src/net/udp/slam.c                            |    6 +-
 gpxe/src/net/udp/tftp.c                            |  139 +-
 gpxe/src/proto/fsp.c                               |  243 --
 gpxe/src/proto/igmp.c                              |  167 --
 gpxe/src/proto/nfs.c                               |  616 -----
 gpxe/src/proto/nmb.c                               |  110 -
 gpxe/src/tests/uri_test.c                          |    7 +-
 gpxe/src/usr/autoboot.c                            |   64 +-
 gpxe/src/usr/dhcpmgmt.c                            |   23 +-
 gpxe/src/usr/ifmgmt.c                              |   42 +-
 gpxe/src/usr/imgmgmt.c                             |    4 +-
 gpxe/src/usr/iwmgmt.c                              |  244 ++
 gpxe/src/usr/pxemenu.c                             |    4 +-
 gpxe/src/usr/route.c                               |    4 +-
 gpxe/src/util/.gitignore                           |    1 +
 gpxe/src/util/diffsize.pl                          |  101 +
 gpxe/src/util/dskpad.pl                            |   12 -
 gpxe/src/util/elf2efi.c                            |    7 +-
 gpxe/src/util/geniso                               |    5 +-
 gpxe/src/util/genliso                              |   51 +-
 gpxe/src/util/gensdsk                              |   65 +
 gpxe/src/util/iccfix.c                             |  156 ++
 gpxe/src/util/licence.pl                           |  149 +
 gpxe/src/util/makerom.pl                           |   30 +-
 gpxe/src/util/modrom.pl                            |    4 +-
 gpxe/src/util/padimg.pl                            |   44 +
 gpxe/src/util/parserom.pl                          |    3 +-
 gpxe/src/util/zbin.c                               |  144 +-
 686 files changed, 64695 insertions(+), 10305 deletions(-)

diff --git a/gpxe/gpxe.diff b/gpxe/gpxe.diff
new file mode 100644
index 0000000..3df08a2
--- /dev/null
+++ b/gpxe/gpxe.diff
@@ -0,0 +1,15 @@
+diff --git a/gpxe/src/config/general.h b/gpxe/src/config/general.h
+index 0a9e625..de51f9f 100644
+--- a/gpxe/src/config/general.h
++++ b/gpxe/src/config/general.h
+@@ -55,8 +55,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
+ 
+ #define	DOWNLOAD_PROTO_TFTP	/* Trivial File Transfer Protocol */
+ #define	DOWNLOAD_PROTO_HTTP	/* Hypertext Transfer Protocol */
+-#undef	DOWNLOAD_PROTO_HTTPS	/* Secure Hypertext Transfer Protocol */
+-#undef	DOWNLOAD_PROTO_FTP	/* File Transfer Protocol */
++#define	DOWNLOAD_PROTO_HTTPS	/* Secure Hypertext Transfer Protocol */
++#define	DOWNLOAD_PROTO_FTP	/* File Transfer Protocol */
+ #undef	DOWNLOAD_PROTO_TFTM	/* Multicast Trivial File Transfer Protocol */
+ #undef	DOWNLOAD_PROTO_SLAM	/* Scalable Local Area Multicast */
+ 
diff --git a/gpxe/src/Makefile b/gpxe/src/Makefile
index a627d96..cc91d78 100644
--- a/gpxe/src/Makefile
+++ b/gpxe/src/Makefile
@@ -22,7 +22,7 @@ ECHO		:= echo
 PRINTF		:= printf
 PERL		:= /usr/bin/perl
 CC		:= $(CROSS_COMPILE)gcc
-CPP		:= $(CROSS_COMPILE)gcc -E -Wp,-Wall
+CPP		:= $(CC) -E
 AS		:= $(CROSS_COMPILE)as
 LD		:= $(CROSS_COMPILE)ld
 SIZE		:= $(CROSS_COMPILE)size
@@ -35,12 +35,17 @@ PARSEROM	:= $(PERL) ./util/parserom.pl
 MAKEROM		:= $(PERL) ./util/makerom.pl
 SYMCHECK	:= $(PERL) ./util/symcheck.pl
 SORTOBJDUMP	:= $(PERL) ./util/sortobjdump.pl
+PADIMG		:= $(PERL) ./util/padimg.pl
+LICENCE		:= $(PERL) ./util/licence.pl
 NRV2B		:= ./util/nrv2b
 ZBIN		:= ./util/zbin
 ELF2EFI32	:= ./util/elf2efi32
 ELF2EFI64	:= ./util/elf2efi64
 EFIROM		:= ./util/efirom
+ICCFIX		:= ./util/iccfix
 DOXYGEN		:= doxygen
+BINUTILS_DIR	:= /usr
+BFD_DIR		:= $(BINUTILS_DIR)
 
 ###############################################################################
 #
@@ -49,13 +54,14 @@ DOXYGEN		:= doxygen
 SRCDIRS		:=
 SRCDIRS		+= libgcc
 SRCDIRS		+= core
-SRCDIRS		+= proto
-SRCDIRS		+= net net/tcp net/udp
+SRCDIRS		+= net net/tcp net/udp net/infiniband net/80211
 SRCDIRS		+= image
 SRCDIRS		+= drivers/bus
 SRCDIRS		+= drivers/net
 SRCDIRS		+= drivers/net/e1000
 SRCDIRS		+= drivers/net/phantom
+SRCDIRS		+= drivers/net/rtl818x
+SRCDIRS		+= drivers/net/ath5k
 SRCDIRS		+= drivers/block
 SRCDIRS		+= drivers/nvs
 SRCDIRS		+= drivers/bitbash
@@ -66,6 +72,7 @@ SRCDIRS		+= crypto crypto/axtls crypto/matrixssl
 SRCDIRS		+= hci hci/commands hci/tui
 SRCDIRS		+= hci/mucurses hci/mucurses/widgets
 SRCDIRS		+= usr
+SRCDIRS		+= config
 
 # NON_AUTO_SRCS lists files that are excluded from the normal
 # automatic build system.
@@ -73,6 +80,11 @@ SRCDIRS		+= usr
 NON_AUTO_SRCS	:=
 NON_AUTO_SRCS	+= drivers/net/prism2.c
 
+# INCDIRS lists the include path
+#
+INCDIRS		:=
+INCDIRS		+= include .
+
 ###############################################################################
 #
 # Default build target: build the most common targets and print out a
@@ -115,9 +127,9 @@ install :
 #
 # Version number calculations
 #
-VERSION_MAJOR	= 0
-VERSION_MINOR	= 9
-VERSION_PATCH	= 7
+VERSION_MAJOR	= 1
+VERSION_MINOR	= 0
+VERSION_PATCH	= 0
 EXTRAVERSION	=
 MM_VERSION	= $(VERSION_MAJOR).$(VERSION_MINOR)
 VERSION		= $(MM_VERSION).$(VERSION_PATCH)$(EXTRAVERSION)
diff --git a/gpxe/src/Makefile.housekeeping b/gpxe/src/Makefile.housekeeping
index 2ab842e..1f5e115 100644
--- a/gpxe/src/Makefile.housekeeping
+++ b/gpxe/src/Makefile.housekeeping
@@ -54,6 +54,14 @@ echo :
 
 ###############################################################################
 #
+# Generate a usable "seq" substitute
+#
+define seq
+	$(shell awk 'BEGIN { for ( i = $(1) ; i <= $(2) ; i++ ) print i }')
+endef
+
+###############################################################################
+#
 # Determine host OS
 #
 HOST_OS		:= $(shell uname -s)
@@ -62,6 +70,22 @@ hostos :
 
 ###############################################################################
 #
+# Determine compiler
+
+CCDEFS		:= $(shell $(CC) -E -x c -c /dev/null -dM | cut -d" " -f2)
+ccdefs:
+	@$(ECHO) $(CCDEFS)
+
+ifeq ($(filter __ICC,$(CCDEFS)),__ICC)
+CCTYPE		:= icc
+else
+CCTYPE		:= gcc
+endif
+cctype:
+	@$(ECHO) $(CCTYPE)
+
+###############################################################################
+#
 # Check for tools that can cause failed builds
 #
 .toolcheck :
@@ -103,10 +127,30 @@ oldgas :
 # default, even when -ffreestanding is specified.  We therefore need
 # to disable -fstack-protector if the compiler supports it.
 #
+ifeq ($(CCTYPE),gcc)
 SP_TEST = $(CC) -fno-stack-protector -x c -c /dev/null \
 		-o /dev/null >/dev/null 2>&1
 SP_FLAGS := $(shell $(SP_TEST) && $(ECHO) '-fno-stack-protector')
 CFLAGS	+= $(SP_FLAGS)
+endif
+
+# gcc 4.4 generates .eh_frame sections by default, which distort the
+# output of "size".  Inhibit this.
+#
+ifeq ($(CCTYPE),gcc)
+CFI_TEST = $(CC) -fno-dwarf2-cfi-asm -x c -c /dev/null \
+		 -o /dev/null >/dev/null 2>&1
+CFI_FLAGS := $(shell $(CFI_TEST) && $(ECHO) '-fno-dwarf2-cfi-asm')
+CFLAGS	+= $(CFI_FLAGS)
+endif
+
+# Some versions of gas choke on division operators, treating them as
+# comment markers.  Specifying --divide will work around this problem,
+# but isn't available on older gas versions.
+#
+DIVIDE_TEST = $(AS) --divide /dev/null -o /dev/null 2>/dev/null
+DIVIDE_FLAGS := $(shell $(DIVIDE_TEST) && $(ECHO) '--divide')
+ASFLAGS	+= $(DIVIDE_FLAGS)
 
 ###############################################################################
 #
@@ -248,6 +292,11 @@ MAKEDEPS	+= arch/$(ARCH)/Makefile
 include arch/$(ARCH)/Makefile
 endif
 
+# Include architecture-specific include path
+ifdef ARCH
+INCDIRS		+= arch/$(ARCH)/include
+endif
+
 ###############################################################################
 #
 # Source file handling
@@ -276,12 +325,45 @@ autosrcs :
 
 ifdef BIN
 
+# INCDIRS lists the include path
+incdirs :
+	@$(ECHO) $(INCDIRS)
+
 # Common flags
 #
-CFLAGS		+= -I include -I arch/$(ARCH)/include -I .
-CFLAGS		+= -Os -ffreestanding
-CFLAGS		+= -Wall -W -Wformat-nonliteral
+CFLAGS		+= $(foreach INC,$(INCDIRS),-I$(INC))
+CFLAGS		+= -Os
 CFLAGS		+= -g
+ifeq ($(CCTYPE),gcc)
+CFLAGS		+= -ffreestanding
+CFLAGS		+= -Wall -W -Wformat-nonliteral
+endif
+ifeq ($(CCTYPE),icc)
+CFLAGS		+= -fno-builtin
+CFLAGS		+= -no-ip
+CFLAGS		+= -no-gcc
+CFLAGS		+= -diag-disable 111 # Unreachable code
+CFLAGS		+= -diag-disable 128 # Unreachable loop
+CFLAGS		+= -diag-disable 170 # Array boundary checks
+CFLAGS		+= -diag-disable 177 # Unused functions
+CFLAGS		+= -diag-disable 181 # printf() format checks
+CFLAGS		+= -diag-disable 188 # enum strictness
+CFLAGS		+= -diag-disable 193 # Undefined preprocessor identifiers
+CFLAGS		+= -diag-disable 280 # switch ( constant )
+CFLAGS		+= -diag-disable 310 # K&R parameter lists
+CFLAGS		+= -diag-disable 424 # Extra semicolon
+CFLAGS		+= -diag-disable 589 # Declarations mid-code
+CFLAGS		+= -diag-disable 593 # Unused variables
+CFLAGS		+= -diag-disable 810 # Casting ints to smaller ints
+CFLAGS		+= -diag-disable 981 # Sequence point violations
+CFLAGS		+= -diag-disable 1292 # Ignored attributes
+CFLAGS		+= -diag-disable 1338 # void pointer arithmetic
+CFLAGS		+= -diag-disable 1361 # Variable-length arrays
+CFLAGS		+= -diag-disable 1418 # Missing prototypes
+CFLAGS		+= -diag-disable 1419 # Missing prototypes
+CFLAGS		+= -diag-disable 1599 # Hidden variables
+CFLAGS		+= -Wall -Wmissing-declarations
+endif
 CFLAGS		+= $(EXTRA_CFLAGS)
 ASFLAGS		+= $(EXTRA_ASFLAGS)
 LDFLAGS		+= $(EXTRA_LDFLAGS)
@@ -314,11 +396,21 @@ OBJ_CFLAGS	= $(CFLAGS_$(OBJECT)) -DOBJECT=$(subst -,_,$(OBJECT))
 $(BIN)/%.flags :
 	@$(ECHO) $(OBJ_CFLAGS)
 
+# ICC requires postprocessing objects to fix up table alignments
+#
+ifeq ($(CCTYPE),icc)
+POST_O		= && $(ICCFIX) $@
+POST_O_DEPS	:= $(ICCFIX)
+else
+POST_O		:=
+POST_O_DEPS	:=
+endif
+
 # Rules for specific object types.
 #
 COMPILE_c	= $(CC) $(CFLAGS) $(CFLAGS_c) $(OBJ_CFLAGS)
-RULE_c		= $(Q)$(COMPILE_c) -c $< -o $@
-RULE_c_to_dbg%.o = $(Q)$(COMPILE_c) -Ddebug_$(OBJECT)=$* -c $< -o $@
+RULE_c		= $(Q)$(COMPILE_c) -c $< -o $@ $(POST_O)
+RULE_c_to_dbg%.o = $(Q)$(COMPILE_c) -Ddebug_$(subst -,_,$(OBJECT))=$* -c $< -o $@ $(POST_O)
 RULE_c_to_c	= $(Q)$(COMPILE_c) -E -c $< > $@
 RULE_c_to_s	= $(Q)$(COMPILE_c) -S -g0 -c $< -o $@
 
@@ -346,39 +438,25 @@ define src_template
 	@$(MKDIR) -p $(dir $(2))
 	@$(RM) $(2)
 	@$(TOUCH) $(2)
-	$(foreach OBJ,$(if $(OBJS_$(4)),$(OBJS_$(4)),$(4)), \
-		$(call obj_template,$(1),$(2),$(3),$(OBJ)))
-	@$(PARSEROM) $(1) >> $(2)
-
-endef
-
-# obj_template : generate Makefile rules for a given resultant object
-# of a particular source file.  (We can have multiple objects per
-# source file via the OBJS_xxx list.)
-#
-# $(1) is the full path to the source file (e.g. "drivers/net/rtl8139.c")
-# $(2) is the full path to the .d file (e.g. "bin/deps/drivers/net/rtl8139.d")
-# $(3) is the source type (e.g. "c")
-# $(4) is the object name (e.g. "rtl8139")
-#
-define obj_template
-
 	@$(CPP) $(CFLAGS) $(CFLAGS_$(3)) $(CFLAGS_$(4)) -DOBJECT=$(4) \
-		-Wno-error -MM $(1) -MT "$(4)_DEPS" -MG -MP | \
-		sed 's/_DEPS\s*:/_DEPS =/' >> $(2)
-	@$(ECHO_E) '\n$$(BIN)/$(4).o : $(1) $$(MAKEDEPS) $$($(4)_DEPS)' \
+		-Wno-error -MM $(1) -MG -MP | \
+		sed 's/\.o\s*:/_DEPS =/' >> $(2)
+	@$(ECHO_E) '\n$$(BIN)/$(4).o :' \
+		 '$(1) $$(MAKEDEPS) $$(POST_O_DEPS) $$($(4)_DEPS)' \
 		 '\n\t$$(QM)$(ECHO) "  [BUILD] $$@"' \
 		 '\n\t$$(RULE_$(3))\n' \
 		 '\nBOBJS += $$(BIN)/$(4).o\n' \
 		 $(foreach TGT,$(DEBUG_TARGETS), \
 		    $(if $(RULE_$(3)_to_$(TGT)), \
-		    '\n$$(BIN)/$(4).$(TGT) : $(1) $$(MAKEDEPS) $$($(4)_DEPS)' \
+		    '\n$$(BIN)/$(4).$(TGT) :' \
+		    '$(1) $$(MAKEDEPS) $$(POST_O_DEPS) $$($(4)_DEPS)' \
 		    '\n\t$$(QM)$(ECHO) "  [BUILD] $$@"' \
 		    '\n\t$$(RULE_$(3)_to_$(TGT))\n' \
 		    '\n$(TGT)_OBJS += $$(BIN)/$(4).$(TGT)\n' ) ) \
 		 '\n$(2) : $$($(4)_DEPS)\n' \
 		 '\nTAGS : $$($(4)_DEPS)\n' \
 		>> $(2)
+	@$(PARSEROM) $(1) >> $(2)
 
 endef
 
@@ -415,7 +493,7 @@ roms :
 #
 EMBEDDED_LIST	:= $(BIN)/.embedded.list
 ifeq ($(wildcard $(EMBEDDED_LIST)),)
-EMBEDDED_LIST_IMAGE :=
+EMBEDDED_LIST_IMAGE := <invalid>
 else
 EMBEDDED_LIST_IMAGE := $(shell cat $(EMBEDDED_LIST))
 endif
@@ -428,7 +506,7 @@ $(EMBEDDED_LIST) :
 VERYCLEANUP	+= $(EMBEDDED_LIST)
 
 EMBEDDED_FILES	:= $(subst $(COMMA), ,$(EMBEDDED_IMAGE))
-EMBED_ALL	:= $(foreach i,$(shell seq 1 $(words $(EMBEDDED_FILES))),\
+EMBED_ALL	:= $(foreach i,$(call seq,1,$(words $(EMBEDDED_FILES))),\
 		     EMBED ( $(i), \"$(word $(i), $(EMBEDDED_FILES))\",\
 			     \"$(notdir $(word $(i),$(EMBEDDED_FILES)))\" ))
 
@@ -550,7 +628,7 @@ $(BIN)/%.info :
 #
 BLIB_LIST	:= $(BIN)/.blib.list
 ifeq ($(wildcard $(BLIB_LIST)),)
-BLIB_LIST_OBJS	:=
+BLIB_LIST_OBJS	:= <invalid>
 else
 BLIB_LIST_OBJS	:= $(shell cat $(BLIB_LIST))
 endif
@@ -589,6 +667,55 @@ $(BIN)/%.tmp : $(BLIB) $(MAKEDEPS) $(LDSCRIPT)
 $(BIN)/%.map : $(BIN)/%.tmp
 	@less $(BIN)/$*.tmp.map
 
+# Get objects list for the specified target
+#
+define objs_list
+	$(sort $(foreach OBJ_SYMBOL,\
+		 $(filter obj_%,$(shell $(NM) $(1) | cut -d" " -f3)),\
+		 $(patsubst obj_%,%,$(OBJ_SYMBOL))))
+endef
+$(BIN)/%.objs : $(BIN)/%.tmp
+	$(Q)$(ECHO) $(call objs_list,$<)
+$(BIN)/%.sizes : $(BIN)/%.tmp
+	$(Q)$(SIZE) -t $(foreach OBJ,$(call objs_list,$<),$(wildcard $(BIN)/$(subst _,?,$(OBJ)).o)) | \
+		sort -g
+
+# Get dependency list for the specified target
+#
+define deps_list
+	$(sort $(foreach OBJ,$(call objs_list,$(1)),$($(OBJ)_DEPS)))
+endef
+$(BIN)/%.deps : $(BIN)/%.tmp
+	$(Q)$(ECHO) $(call deps_list,$<)
+
+# Get unneeded source files for the specified target
+#
+define nodeps_list
+	$(sort $(filter-out $(call deps_list,$(1)),\
+		 $(foreach BOBJ,$(BOBJS),\
+		   $($(basename $(notdir $(BOBJ)))_DEPS))))
+endef
+$(BIN)/%.nodeps : $(BIN)/%.tmp
+	$(Q)$(ECHO) $(call nodeps_list,$<)
+
+# Get licensing verdict for the specified target
+#
+define unlicensed_deps_list
+	$(shell grep -L FILE_LICENCE $(call deps_list,$(1)))
+endef
+define licence_list
+	$(patsubst __licence_%,%,\
+	  $(filter __licence_%,$(shell $(NM) $(1) | cut -d" " -f3)))
+endef
+$(BIN)/%.licence : $(BIN)/%.tmp
+	$(QM)$(ECHO) "  [LICENCE] $@"
+	$(Q)$(if $(strip $(call unlicensed_deps_list,$<)),\
+		echo -n "Unable to determine licence because the following " ;\
+		echo "files are missing a licence declaration:" ;\
+		echo $(call unlicensed_deps_list,$<);\
+		exit 1,\
+		$(LICENCE) $(call licence_list,$<))
+
 # Extract compression information from intermediate object file
 #
 $(BIN)/%.zinfo : $(BIN)/%.tmp
@@ -649,6 +776,7 @@ define media_template
 	@$(ECHO_E) '$$(BIN)/%.$(1) : $$(BIN)/%.$(1).zbin' \
 		  '\n\t$$(QM)$(ECHO) "  [FINISH] $$@"' \
 		  '\n\t$$(Q)$$(CP) $$< $$@' \
+		  '\n\t$$(Q)$$(PAD_$(1))' \
 		  '\n\t$$(Q)$$(FINALISE_$(1))' \
 		> $(2)
 
@@ -702,6 +830,9 @@ endif # defined(BIN)
 #
 FINALISE_rom	= $(MAKEROM) $(MAKEROM_FLAGS) $(TGT_MAKEROM_FLAGS) \
 		  -i$(IDENT) -s 0 $@
+FINALISE_hrom	= $(FINALISE_rom)
+FINALISE_xrom	= $(MAKEROM) $(MAKEROM_FLAGS) $(TGT_MAKEROM_FLAGS) \
+		  -i$(IDENT) -n -s 0 $@
 
 # Some ROMs require specific flags to be passed to makerom.pl
 #
@@ -726,16 +857,18 @@ CLEANUP += $(ZBIN)
 #
 # The EFI image converter
 #
+ELF2EFI_CFLAGS	:= -I$(BINUTILS_DIR)/include -I$(BFD_DIR)/include \
+		   -idirafter include -L$(BINUTILS_DIR)/lib -L$(BFD_DIR)/lib \
+		   -lbfd -liberty -lz
+
 $(ELF2EFI32) : util/elf2efi.c $(MAKEDEPS)
 	$(QM)$(ECHO) "  [HOSTCC] $@"
-	$(Q)$(HOST_CC) -DMDE_CPU_IA32 -idirafter include -O2 \
-		-o $@ $< -lbfd -liberty
+	$(Q)$(HOST_CC) $(ELF2EFI_CFLAGS) -DMDE_CPU_IA32 -O2 -o $@ $<
 CLEANUP += $(ELF2EFI32)
 
 $(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS)
 	$(QM)$(ECHO) "  [HOSTCC] $@"
-	$(Q)$(HOST_CC) -DMDE_CPU_X64 -idirafter include -O2 \
-		-o $@ $< -lbfd -liberty
+	$(Q)$(HOST_CC) $(ELF2EFI_CFLAGS) -DMDE_CPU_X64 -O2 -o $@ $<
 CLEANUP += $(ELF2EFI64)
 
 $(EFIROM) : util/efirom.c $(MAKEDEPS)
@@ -745,6 +878,15 @@ CLEANUP += $(EFIROM)
 
 ###############################################################################
 #
+# The ICC fixup utility
+#
+$(ICCFIX) : util/iccfix.c $(MAKEDEPS)
+	$(QM)$(ECHO) "  [HOSTCC] $@"
+	$(Q)$(HOST_CC) -idirafter include -O2 -o $@ $<
+CLEANUP += $(ICCFIX)
+
+###############################################################################
+#
 # Auto-incrementing build serial number.  Append "bs" to your list of
 # build targets to get a serial number printed at the end of the
 # build.  Enable -DBUILD_SERIAL in order to see it when the code runs.
@@ -823,20 +965,24 @@ endif # defined(BIN)
 ifdef BIN
 
 $(BIN)/doxygen.cfg : doxygen.cfg $(MAKEDEPS)
-	$(PERL) -pe 's{\@SRCDIRS\@}{$(SRCDIRS)}; ' \
+	$(Q)$(PERL) -pe 's{\@SRCDIRS\@}{$(SRCDIRS)}; ' \
+		-e  's{\@INCDIRS\@}{$(filter-out .,$(INCDIRS))}; ' \
 		-e  's{\@BIN\@}{$(BIN)}; ' \
 		-e  's{\@ARCH\@}{$(ARCH)}; ' \
 		$< > $@
 
 $(BIN)/doc : $(BIN)/doxygen.cfg
-	$(DOXYGEN) $<
+	$(Q)$(DOXYGEN) $<
 
 .PHONY : $(BIN)/doc
 
-VERYCLEANUP	+= $(BIN)/doc
-
 doc : $(BIN)/doc
 
+doc-clean :
+	$(Q)$(RM) -r $(BIN)/doc
+
+VERYCLEANUP	+= $(BIN)/doc
+
 docview :
 	@[ -f $(BIN)/doc/html/index.html ] || $(MAKE) $(BIN)/doc
 	@if [ -n "$$BROWSER" ] ; then \
diff --git a/gpxe/src/README.pixify b/gpxe/src/README.pixify
deleted file mode 100644
index 9aef25d..0000000
--- a/gpxe/src/README.pixify
+++ /dev/null
@@ -1,90 +0,0 @@
-This file documents the driver changes needed to support use as part
-of a PXE stack.
-
-PROPER WAY
-==========
-
-1. The probe() routine.
-
-There are three additional fields that need to be filled in the nic
-structure: ioaddr, irqno and irq.
-
-  ioaddr is the base I/O address and seems to be for information only;
-  no use will be made of this value other than displaying it on the
-  screen.
-
-  irqno must be the IRQ number for the NIC.  For PCI NICs this can
-  simply be copied from pci->irq.
-
-  irq is a function pointer, like poll and transmit.  It must point to
-  the driver's irq() function.
-
-2. The poll() routine.
-
-This must take an additional parameter: "int retrieve".  Calling
-poll() with retrieve!=0 should function exactly as before.  Calling
-poll() with retrieve==0 indicates that poll() should check for the
-presence of a packet to read, but must *not* read the packet.  The
-packet will be read by a subsequent call to poll() with retrieve!=0.
-
-The easiest way to implement this is to insert the line
-  if ( ! retrieve ) return 1;
-between the "is there a packet ready" and the "fetch packet" parts of
-the existing poll() routine.
-
-Care must be taken that a call to poll() with retrieve==0 does not
-clear the NIC's "packet ready" status indicator, otherwise the
-subsequent call to poll() with retrieve!=0 will fail because it will
-think that there is no packet to read.
-
-poll() should also acknowledge and clear the NIC's "packet received"
-interrupt.  It does not need to worry about enabling/disabling
-interrupts; this is taken care of by calls to the driver's irq()
-routine.
-
-Etherboot will forcibly regenerate an interrupt if a packet remains
-pending after all interrupts have been acknowledged.  You can
-therefore get away with having poll() just acknolwedge and clear all
-NIC interrupts, without particularly worrying about exactly when this
-should be done.
-
-3. The irq() routine.
-
-This is a new routine, with prototype
-  void DRIVER_irq ( struct nic *nic, irq_action_t action );
-"action" takes one of three possible values: ENABLE, DISABLE or FORCE.
-ENABLE and DISABLE mean to enable/disable the NIC's "packet received"
-interrupt.  FORCE means that the NIC should be forced to generate a
-fake "packet received" interrupt.
-
-If you are unable to implement FORCE, your NIC will not work when
-being driven via the UNDI interface under heavy network traffic
-conditions.  Since Etherboot's UNDI driver (make bin/undi.zpxe) is the
-only program known to use this interface, it probably doesn't really
-matter.
-
-
-QUICK AND DIRTY WAY
-===================
-
-It is possible to use the system timer interrupt (IRQ 0) rather than a
-genuine NIC interrupt.  Since there is a constant stream of timer
-interrupts, the net upshot is a whole load of spurious "NIC"
-interrupts that have no effect other than to cause unnecessary PXE API
-calls.  It's inefficient but it works.
-
-To achieve this, simply set nic->irqno=0 in probe() and point nic->irq
-to a dummy routine that does nothing.  Add the line
-  if ( ! retrieve ) return 1;
-at the beginning of poll(), to prevent the packet being read (and
-discarded) when poll() is called with retrieve==0;
-
-
-UNCONVERTED DRIVERS
-===================
-
-Drivers that have not yet been converted should continue to function
-when not used as part of a PXE stack, although there will be a
-harmless compile-time warning about assignment from an incompatible
-pointer type in the probe() function, since the prototype for the
-poll() function is missing the "int retrieve" parameter.
diff --git a/gpxe/src/arch/i386/Makefile b/gpxe/src/arch/i386/Makefile
index 1392bba..dd8da80 100644
--- a/gpxe/src/arch/i386/Makefile
+++ b/gpxe/src/arch/i386/Makefile
@@ -4,22 +4,33 @@ CFLAGS		+= -march=i386
 
 # Code size reduction.
 #
-CFLAGS		+= -fstrength-reduce -fomit-frame-pointer
+CFLAGS		+= -fomit-frame-pointer
+
+# Code size reduction.
+#
+ifeq ($(CCTYPE),gcc)
+CFLAGS		+= -fstrength-reduce
+endif
 
 # Code size reduction.  gcc3 needs a different syntax to gcc2 if you
 # want to avoid spurious warnings.
 #
+ifeq ($(CCTYPE),gcc)
 GCC_VERSION	:= $(subst ., ,$(shell $(CC) -dumpversion))
 GCC_MAJOR	:= $(firstword $(GCC_VERSION))
 ifeq ($(GCC_MAJOR),2)
 CFLAGS		+= -malign-jumps=1 -malign-loops=1 -malign-functions=1
 else
 CFLAGS		+= -falign-jumps=1 -falign-loops=1 -falign-functions=1
-endif
+endif # gcc2
+endif # gcc
 
-# Code size reduction.  This is almost always a win.  The kernel uses it, too.
+# Code size reduction.  This is almost always a win.  The kernel uses
+# it, too.
 #
+ifeq ($(CCTYPE),gcc)
 CFLAGS		+= -mpreferred-stack-boundary=2
+endif
 
 # Code size reduction.  Use regparm for all functions - C functions
 # called from assembly (or vice versa) need __asmcall now
@@ -27,7 +38,9 @@ CFLAGS		+= -mpreferred-stack-boundary=2
 CFLAGS		+= -mregparm=3
 
 # Code size reduction.  Use -mrtd (same __asmcall requirements as above)
+ifeq ($(CCTYPE),gcc)
 CFLAGS		+= -mrtd
+endif
 
 # Code size reduction.  This is the logical complement to -mregparm=3.
 # It doesn't currently buy us anything, but if anything ever tries to
@@ -67,7 +80,9 @@ SRCDIRS		+= arch/i386/drivers
 SRCDIRS		+= arch/i386/drivers/net
 SRCDIRS		+= arch/i386/interface/pcbios
 SRCDIRS		+= arch/i386/interface/pxe
+SRCDIRS		+= arch/i386/interface/pxeparent
 SRCDIRS 	+= arch/i386/interface/syslinux
+SRCDIRS		+= arch/i386/hci/commands
 
 # The various xxx_loader.c files are #included into core/loader.c and
 # should not be compiled directly.
@@ -76,11 +91,6 @@ NON_AUTO_SRCS	+= arch/i386/core/aout_loader.c
 NON_AUTO_SRCS	+= arch/i386/core/freebsd_loader.c
 NON_AUTO_SRCS	+= arch/i386/core/wince_loader.c
 
-# unnrv2b.S is used to generate a 16-bit as well as a 32-bit object.
-#
-OBJS_unnrv2b	= unnrv2b unnrv2b16
-CFLAGS_unnrv2b16 = -DCODE16
-
 # Include common x86 Makefile
 #
 MAKEDEPS	+= arch/x86/Makefile
@@ -101,13 +111,6 @@ NON_AUTO_MEDIA	+= fd0
 	$(Q)dd if=$< bs=512 conv=sync of=/dev/fd0
 	$(Q)sync
 
-# rule to create padded disk images
-NON_AUTO_MEDIA	+= pdsk
-%pdsk : %dsk
-	$(QM)$(ECHO) "  [DSKPAD] $@"
-	$(Q)cp $< $@
-	$(Q)$(PERL) ./util/dskpad.pl $@
-
 # Add NON_AUTO_MEDIA to the media list, so that they show up in the
 # output of "make"
 #
diff --git a/gpxe/src/arch/i386/Makefile.pcbios b/gpxe/src/arch/i386/Makefile.pcbios
index 64b3dac..e38fbca 100644
--- a/gpxe/src/arch/i386/Makefile.pcbios
+++ b/gpxe/src/arch/i386/Makefile.pcbios
@@ -11,21 +11,24 @@ LDFLAGS		+= -N --no-check-sections
 # Media types.
 #
 MEDIA		+= rom
+MEDIA		+= hrom
+MEDIA		+= xrom
 MEDIA		+= pxe
 MEDIA		+= kpxe
 MEDIA		+= kkpxe
-MEDIA		+= elf
-MEDIA		+= elfd
-MEDIA		+= lmelf
-MEDIA		+= lmelfd
 MEDIA		+= lkrn
-MEDIA		+= bImage
 MEDIA		+= dsk
 MEDIA		+= nbi
 MEDIA		+= hd
 MEDIA		+= raw
-MEDIA		+= com
-MEDIA		+= exe
+
+# Padding rules
+#
+PAD_rom		= $(PADIMG) --blksize=512 --byte=0xff $@
+PAD_hrom	= $(PAD_rom)
+PAD_xrom	= $(PAD_rom)
+PAD_dsk		= $(PADIMG) --blksize=512 $@
+PAD_hd		= $(PADIMG) --blksize=32768 $@
 
 # rule to make a non-emulation ISO boot image
 NON_AUTO_MEDIA	+= iso
@@ -39,6 +42,12 @@ NON_AUTO_MEDIA	+= liso
 	$(QM)$(ECHO) "  [GENLISO] $@"
 	$(Q)bash util/genliso $@ $<
 
+# rule to make a syslinux floppy image (mountable, bootable)
+NON_AUTO_MEDIA	+= sdsk
+%sdsk:	%lkrn util/gensdsk
+	$(QM)$(ECHO) "  [GENSDSK] $@"
+	$(Q)bash util/gensdsk $@ $<
+
 # Special target for building Master Boot Record binary
 $(BIN)/mbr.bin : $(BIN)/mbr.o
 	$(QM)$(ECHO) "  [OBJCOPY] $@"
@@ -53,3 +62,9 @@ NON_AUTO_MEDIA	+= usb
 %usb: $(BIN)/usbdisk.bin %hd
 	$(QM)$(ECHO) "  [FINISH] $@"
 	$(Q)cat $^ > $@
+
+# Padded floppy image (e.g. for iLO)
+NON_AUTO_MEDIA += pdsk
+%pdsk : %dsk
+	$(Q)cp $< $@
+	$(Q)$(PADIMG) --blksize=1474560 $@
diff --git a/gpxe/src/arch/i386/core/basemem_packet.c b/gpxe/src/arch/i386/core/basemem_packet.c
index 64e0bcc..d487cce 100644
--- a/gpxe/src/arch/i386/core/basemem_packet.c
+++ b/gpxe/src/arch/i386/core/basemem_packet.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/arch/i386/core/etherboot.prefix.lds b/gpxe/src/arch/i386/core/etherboot.prefix.lds
deleted file mode 100644
index 3550a2a..0000000
--- a/gpxe/src/arch/i386/core/etherboot.prefix.lds
+++ /dev/null
@@ -1,100 +0,0 @@
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-
-ENTRY(_prefix_start)
-SECTIONS {
-	/* Prefix */
-	.prefix : {
-		_verbatim_start = . ;
-		_prefix_start = . ;
-		*(.prefix)
-		. = ALIGN(16);
-		_prefix_end = . ;
-	} = 0x9090
-	_prefix_size = _prefix_end - _prefix_start;
-
-	.text.nocompress : {
-		*(.prefix.udata)
-	} = 0x9090
-
-	decompress_to = . ;
-	.prefix.zdata : {
-		_compressed = . ;
-		*(.prefix.zdata)
-		_compressed_end = . ;
-	}
-	_compressed_size = _compressed_end - _compressed;
-
-	. = ALIGN(16);
-	_verbatim_end = . ;
-
-
-	/* Size of the core of etherboot in memory */
-	_base_size = _end - _text;
-
-	/* _prefix_size is the length of the non-core etherboot prefix */
-	_prefix_size = _prefix_end - _prefix_start;
-
-	/* _verbatim_size is the actual amount that has to be copied to base memory */
-	_verbatim_size = _verbatim_end - _verbatim_start;
-
-	/* _image_size is the amount of base memory needed to run */
-	_image_size = _base_size +  _prefix_size;
-
-	/* Standard sizes rounded up to paragraphs */
-	_prefix_size_pgh   = (_prefix_size + 15) / 16;
-	_verbatim_size_pgh = (_verbatim_size + 15) / 16;
-	_image_size_pgh    = (_image_size + 15) / 16 ;
-	
-	/* Standard sizes in sectors */
-	_prefix_size_sct   = (_prefix_size + 511) / 512;
-	_verbatim_size_sct = (_verbatim_size + 511) / 512;
-	_image_size_sct    = (_image_size + 511) / 512;
-
-	/* Symbol offsets and sizes for the exe prefix */
-	_exe_hdr_size   = 32;
-	_exe_size       = _verbatim_size; /* Should this be - 32 to exclude the header? */
-	_exe_size_tail  = (_exe_size) % 512;
-	_exe_size_pages = ((_exe_size) + 511) / 512;
-	_exe_bss_size   = ((_image_size - _verbatim_size) + 15) / 16;
-	_exe_ss_offset  = (_stack_offset + _prefix_size - _exe_hdr_size + 15) / 16 ;
-
-	/* This is where we copy the compressed image before decompression.
-	 * Prepare to decompress in place.  The end mark is about 8.25 bytes long,
-	 * and the worst case symbol is about 16.5 bytes long.  Therefore
-	 * We need to reserve at least 25 bytes of slack here.  
-	 * Currently I reserve 2048 bytes of just slack to be safe :)
-	 * 2048 bytes easily falls within the BSS (the defualt stack is 4096 bytes)
-	 * so we really are decompressing in place.
-	 * 
-	 * Hmm. I missed a trick.  In the very worst case (no compression)
-	 * the encoded data is 9/8 the size as it started out so to be completely
-	 * safe I need to be 1/8 of the uncompressed code size past the end.
-	 * This will still fit compfortably into our bss in any conceivable scenario.
-	 */
-	_compressed_copy = _edata + _prefix_size  - _compressed_size +
-		/* The amount to overflow _edata */
-		MAX( ((_edata - _text + 7) / 8) , 2016 ) + 32; 
-	_assert = ASSERT( ( _compressed_copy - _prefix_size ) < _ebss , "Cannot decompress in place" ) ; 
-
-	decompress = DEFINED(decompress) ? decompress : 0;
-	/DISCARD/ : {
-		*(.comment)
-		*(.note)
-	}
-
-	/* Symbols used by the prefixes whose addresses are inconvinient 
-	 * to compute, at runtime in the code.
-	 */
-	image_basemem_size = DEFINED(image_basemem_size)? image_basemem_size : 65536;
-	image_basemem      = DEFINED(image_basemem)?     image_basemem : 65536;
-	_prefix_real_to_prot          = _real_to_prot         + _prefix_size ;
-	_prefix_prot_to_real          = _prot_to_real         + _prefix_size ;
-	_prefix_image_basemem_size    = image_basemem_size    + _prefix_size ;
-	_prefix_image_basemem         = image_basemem         + _prefix_size ;
-	_prefix_rm_in_call            = _rm_in_call           + _prefix_size ;
-	_prefix_in_call               = _in_call              + _prefix_size ;
-	_prefix_rom                   = rom                   + _prefix_size ;
-	_prefix_rm_etherboot_location = rm_etherboot_location + _prefix_size ;
-	_prefix_stack_end             = _stack_end            + _prefix_size ;
-}
diff --git a/gpxe/src/arch/i386/core/pic8259.c b/gpxe/src/arch/i386/core/pic8259.c
index 8a0433d..1e2d23c 100644
--- a/gpxe/src/arch/i386/core/pic8259.c
+++ b/gpxe/src/arch/i386/core/pic8259.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/io.h>
 #include <pic8259.h>
 
diff --git a/gpxe/src/arch/i386/core/prefixudata.lds b/gpxe/src/arch/i386/core/prefixudata.lds
deleted file mode 100644
index 1c76128..0000000
--- a/gpxe/src/arch/i386/core/prefixudata.lds
+++ /dev/null
@@ -1,8 +0,0 @@
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-
-SECTIONS {
-	 .prefix.udata : {
-		      *(*)
-	 }
-}
diff --git a/gpxe/src/arch/i386/core/prefixzdata.lds b/gpxe/src/arch/i386/core/prefixzdata.lds
deleted file mode 100644
index bf6ea97..0000000
--- a/gpxe/src/arch/i386/core/prefixzdata.lds
+++ /dev/null
@@ -1,8 +0,0 @@
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-
-SECTIONS {
-	 .prefix.zdata : {
-		      *(*)
-	 }
-}
diff --git a/gpxe/src/arch/i386/core/rdtsc_timer.c b/gpxe/src/arch/i386/core/rdtsc_timer.c
index 443c8ad..7667917 100644
--- a/gpxe/src/arch/i386/core/rdtsc_timer.c
+++ b/gpxe/src/arch/i386/core/rdtsc_timer.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** @file
  *
  * RDTSC timer
diff --git a/gpxe/src/arch/i386/core/realmode.c b/gpxe/src/arch/i386/core/realmode.c
deleted file mode 100644
index 9a77bd8..0000000
--- a/gpxe/src/arch/i386/core/realmode.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Real-mode interface: C portions.
- *
- * Initial version by Michael Brown <mbrown at fensystems.co.uk>, January 2004.
- */
-
-#include "realmode.h"
-
-/*
- * Copy data to/from base memory.
- *
- */
-
-#ifdef KEEP_IT_REAL
-
-void memcpy_to_real ( segoff_t dest, void *src, size_t n ) {
-
-}
-
-void memcpy_from_real ( void *dest, segoff_t src, size_t n ) {
-
-}
-
-#endif /* KEEP_IT_REAL */
diff --git a/gpxe/src/arch/i386/core/relocate.c b/gpxe/src/arch/i386/core/relocate.c
index bdc8498..44e764f 100644
--- a/gpxe/src/arch/i386/core/relocate.c
+++ b/gpxe/src/arch/i386/core/relocate.c
@@ -9,6 +9,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /*
  * The linker passes in the symbol _max_align, which is the alignment
  * that we must preserve, in bytes.
diff --git a/gpxe/src/arch/i386/core/setjmp.S b/gpxe/src/arch/i386/core/setjmp.S
index 59a1b7c..0372714 100644
--- a/gpxe/src/arch/i386/core/setjmp.S
+++ b/gpxe/src/arch/i386/core/setjmp.S
@@ -1,5 +1,7 @@
 /* setjmp and longjmp. Use of these functions is deprecated. */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 	.text
 	.arch i386
 	.code32
diff --git a/gpxe/src/arch/i386/core/stack.S b/gpxe/src/arch/i386/core/stack.S
index da66d23..737ec0e 100644
--- a/gpxe/src/arch/i386/core/stack.S
+++ b/gpxe/src/arch/i386/core/stack.S
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL2_OR_LATER )
+
 	.arch i386
 
 /****************************************************************************
diff --git a/gpxe/src/arch/i386/core/stack16.S b/gpxe/src/arch/i386/core/stack16.S
index d1251f0..523f028 100644
--- a/gpxe/src/arch/i386/core/stack16.S
+++ b/gpxe/src/arch/i386/core/stack16.S
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL2_OR_LATER )
+
 	.arch i386
 
 /****************************************************************************
diff --git a/gpxe/src/arch/i386/core/start16.lds b/gpxe/src/arch/i386/core/start16.lds
deleted file mode 100644
index 544fc78..0000000
--- a/gpxe/src/arch/i386/core/start16.lds
+++ /dev/null
@@ -1,8 +0,0 @@
-/* When linking with an uncompressed image, these symbols are not
- * defined so we provide them here.
- */
-
-__decompressor_uncompressed = 0 ;
-__decompressor__start = 0 ;
-
-INCLUDE arch/i386/core/start16z.lds
diff --git a/gpxe/src/arch/i386/core/start16z.lds b/gpxe/src/arch/i386/core/start16z.lds
deleted file mode 100644
index 711bcf7..0000000
--- a/gpxe/src/arch/i386/core/start16z.lds
+++ /dev/null
@@ -1,65 +0,0 @@
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-
-/* Linker-generated symbols are prefixed with a double underscore.
- * Decompressor symbols are prefixed with __decompressor_.  All other
- * symbols are the same as in the original object file, i.e. the
- * runtime addresses.
- */
-
-ENTRY(_start16)
-
-SECTIONS {
-	.text : {
-		*(.text)
-	}
-	.payload : {
-		__payload_start = .;
-		*(.data)
-		__payload_end = .;
-	}
-
-	/* _payload_size is the size of the binary image appended to
-	 * start16, in bytes.
-	 */
-	__payload_size = __payload_end - __payload_start ;
-
-	/* _size is the size of the runtime image
-	 * (start32 + the C code), in bytes.
-	 */
-	__size = _end - _start ;
-
-	/* _decompressor_size is the size of the decompressor, in
-	 * bytes.  For a non-compressed image, start16.lds sets
-	 * _decompressor_uncompressed = _decompressor__start = 0.
-	 */
-	__decompressor_size = __decompressor_uncompressed - __decompressor__start ;
-
-	/* image__size is the total size of the image, after
-	 * decompression and including the decompressor if applicable.
-	 * It is therefore the amount of memory that start16's payload
-	 * needs in order to execute, in bytes.
-	 */
-	__image_size = __size + __decompressor_size ;
-
-	/* Amount to add to runtime symbols to obtain the offset of
-	 * that symbol within the image.
-	 */
-	__offset_adjust = __decompressor_size - _start ;
-
-	/* Calculations for the stack
-	 */
-	__stack_size = _estack - _stack ;
-	__offset_stack = _stack + __offset_adjust ;
-
-	/* Some symbols will be larger than 16 bits but guaranteed to
-	 * be multiples of 16.  We calculate them in paragraphs and
-	 * export these symbols which can be used in 16-bit code
-	 * without risk of overflow.
-	 */
-	__image_size_pgh = ( __image_size / 16 );
-	__start_pgh = ( _start / 16 );
-	__decompressor_size_pgh = ( __decompressor_size / 16 );
-	__offset_stack_pgh = ( __offset_stack / 16 );
-}
-
diff --git a/gpxe/src/arch/i386/core/timer2.c b/gpxe/src/arch/i386/core/timer2.c
index bb589ec..6e76b2e 100644
--- a/gpxe/src/arch/i386/core/timer2.c
+++ b/gpxe/src/arch/i386/core/timer2.c
@@ -11,6 +11,8 @@
  * your option) any later version.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <gpxe/timer2.h>
 #include <gpxe/io.h>
diff --git a/gpxe/src/arch/i386/core/virtaddr.S b/gpxe/src/arch/i386/core/virtaddr.S
index cf6da4f..aae1e1e 100644
--- a/gpxe/src/arch/i386/core/virtaddr.S
+++ b/gpxe/src/arch/i386/core/virtaddr.S
@@ -4,6 +4,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 #include "librm.h"
 		
 	.arch i386
diff --git a/gpxe/src/arch/i386/core/x86_io.c b/gpxe/src/arch/i386/core/x86_io.c
index 424a96c..d2c363b 100644
--- a/gpxe/src/arch/i386/core/x86_io.c
+++ b/gpxe/src/arch/i386/core/x86_io.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/io.h>
 #include <gpxe/x86_io.h>
 
diff --git a/gpxe/src/arch/i386/drivers/net/undi.c b/gpxe/src/arch/i386/drivers/net/undi.c
index 1090cc9..c6e253c 100644
--- a/gpxe/src/arch/i386/drivers/net/undi.c
+++ b/gpxe/src/arch/i386/drivers/net/undi.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -135,7 +137,7 @@ static void undipci_remove ( struct pci_device *pci ) {
 }
 
 static struct pci_device_id undipci_nics[] = {
-PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)" ),
+PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ),
 };
 
 struct pci_driver undipci_driver __pci_driver = {
diff --git a/gpxe/src/arch/i386/drivers/net/undiisr.S b/gpxe/src/arch/i386/drivers/net/undiisr.S
index 2b31b41..b27effe 100644
--- a/gpxe/src/arch/i386/drivers/net/undiisr.S
+++ b/gpxe/src/arch/i386/drivers/net/undiisr.S
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL2_OR_LATER )
+
 #define PXENV_UNDI_ISR 0x0014
 #define PXENV_UNDI_ISR_IN_START 1
 #define PXENV_UNDI_ISR_OUT_OURS 0
@@ -29,7 +31,7 @@ undiisr:
 	movw	%ax, %ds
 
 	/* Check that we have an UNDI entry point */
-	cmpw	$0, undinet_entry_point
+	cmpw	$0, pxeparent_entry_point
 	je	chain
 	
 	/* Issue UNDI API call */
@@ -40,7 +42,7 @@ undiisr:
 	pushw	%es
 	pushw	%di
 	pushw	%bx
-	lcall	*undinet_entry_point
+	lcall	*pxeparent_entry_point
 	cli	/* Just in case */
 	addw	$6, %sp
 	cmpw	$PXENV_UNDI_ISR_OUT_OURS, funcflag
diff --git a/gpxe/src/arch/i386/drivers/net/undiload.c b/gpxe/src/arch/i386/drivers/net/undiload.c
index dbd9e7c..1d4e88d 100644
--- a/gpxe/src/arch/i386/drivers/net/undiload.c
+++ b/gpxe/src/arch/i386/drivers/net/undiload.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -90,11 +92,10 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) {
 	undi_loader_entry = undirom->loader_entry;
 	__asm__ __volatile__ ( REAL_CODE ( "pushw %%ds\n\t"
 					   "pushw %%ax\n\t"
-					   "lcall *%c2\n\t"
+					   "lcall *undi_loader_entry\n\t"
 					   "addw $4, %%sp\n\t" )
 			       : "=a" ( exit )
-			       : "a" ( __from_data16 ( &undi_loader ) ),
-			         "p" ( __from_data16 ( &undi_loader_entry ) )
+			       : "a" ( __from_data16 ( &undi_loader ) )
 			       : "ebx", "ecx", "edx", "esi", "edi", "ebp" );
 
 	/* UNDI API calls may rudely change the status of A20 and not
diff --git a/gpxe/src/arch/i386/drivers/net/undinet.c b/gpxe/src/arch/i386/drivers/net/undinet.c
index d6db6f7..83b79e7 100644
--- a/gpxe/src/arch/i386/drivers/net/undinet.c
+++ b/gpxe/src/arch/i386/drivers/net/undinet.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <pxe.h>
 #include <realmode.h>
@@ -30,6 +32,7 @@
 #include <gpxe/ethernet.h>
 #include <undi.h>
 #include <undinet.h>
+#include <pxeparent.h>
 
 
 /** @file
@@ -60,179 +63,8 @@ struct undi_nic {
 
 static void undinet_close ( struct net_device *netdev );
 
-/*****************************************************************************
- *
- * UNDI API call
- *
- *****************************************************************************
- */
-
-/**
- * Name UNDI API call
- *
- * @v function		API call number
- * @ret name		API call name
- */
-static inline __attribute__ (( always_inline )) const char *
-undinet_function_name ( unsigned int function ) {
-	switch ( function ) {
-	case PXENV_START_UNDI:
-		return "PXENV_START_UNDI";
-	case PXENV_STOP_UNDI:
-		return "PXENV_STOP_UNDI";
-	case PXENV_UNDI_STARTUP:
-		return "PXENV_UNDI_STARTUP";
-	case PXENV_UNDI_CLEANUP:
-		return "PXENV_UNDI_CLEANUP";
-	case PXENV_UNDI_INITIALIZE:
-		return "PXENV_UNDI_INITIALIZE";
-	case PXENV_UNDI_RESET_ADAPTER:
-		return "PXENV_UNDI_RESET_ADAPTER";
-	case PXENV_UNDI_SHUTDOWN:
-		return "PXENV_UNDI_SHUTDOWN";
-	case PXENV_UNDI_OPEN:
-		return "PXENV_UNDI_OPEN";
-	case PXENV_UNDI_CLOSE:
-		return "PXENV_UNDI_CLOSE";
-	case PXENV_UNDI_TRANSMIT:
-		return "PXENV_UNDI_TRANSMIT";
-	case PXENV_UNDI_SET_MCAST_ADDRESS:
-		return "PXENV_UNDI_SET_MCAST_ADDRESS";
-	case PXENV_UNDI_SET_STATION_ADDRESS:
-		return "PXENV_UNDI_SET_STATION_ADDRESS";
-	case PXENV_UNDI_SET_PACKET_FILTER:
-		return "PXENV_UNDI_SET_PACKET_FILTER";
-	case PXENV_UNDI_GET_INFORMATION:
-		return "PXENV_UNDI_GET_INFORMATION";
-	case PXENV_UNDI_GET_STATISTICS:
-		return "PXENV_UNDI_GET_STATISTICS";
-	case PXENV_UNDI_CLEAR_STATISTICS:
-		return "PXENV_UNDI_CLEAR_STATISTICS";
-	case PXENV_UNDI_INITIATE_DIAGS:
-		return "PXENV_UNDI_INITIATE_DIAGS";
-	case PXENV_UNDI_FORCE_INTERRUPT:
-		return "PXENV_UNDI_FORCE_INTERRUPT";
-	case PXENV_UNDI_GET_MCAST_ADDRESS:
-		return "PXENV_UNDI_GET_MCAST_ADDRESS";
-	case PXENV_UNDI_GET_NIC_TYPE:
-		return "PXENV_UNDI_GET_NIC_TYPE";
-	case PXENV_UNDI_GET_IFACE_INFO:
-		return "PXENV_UNDI_GET_IFACE_INFO";
-	/*
-	 * Duplicate case value; this is a bug in the PXE specification.
-	 *
-	 *	case PXENV_UNDI_GET_STATE:
-	 *		return "PXENV_UNDI_GET_STATE";
-	 */
-	case PXENV_UNDI_ISR:
-		return "PXENV_UNDI_ISR";
-	default:
-		return "UNKNOWN API CALL";
-	}
-}
-
-/**
- * UNDI parameter block
- *
- * Used as the paramter block for all UNDI API calls.  Resides in base
- * memory.
- */
-static union u_PXENV_ANY __bss16 ( undinet_params );
-#define undinet_params __use_data16 ( undinet_params )
-
-/** UNDI entry point
- *
- * Used as the indirection vector for all UNDI API calls.  Resides in
- * base memory.
- */
-SEGOFF16_t __bss16 ( undinet_entry_point );
-#define undinet_entry_point __use_data16 ( undinet_entry_point )
-
-/**
- * Issue UNDI API call
- *
- * @v undinic		UNDI NIC
- * @v function		API call number
- * @v params		UNDI parameter block
- * @v params_len	Length of UNDI parameter block
- * @ret rc		Return status code
- */
-static int undinet_call ( struct undi_nic *undinic, unsigned int function,
-			  void *params, size_t params_len ) {
-	PXENV_EXIT_t exit;
-	int discard_b, discard_D;
-	int rc;
-
-	/* Copy parameter block and entry point */
-	assert ( params_len <= sizeof ( undinet_params ) );
-	memcpy ( &undinet_params, params, params_len );
-
-	/* Call real-mode entry point.  This calling convention will
-	 * work with both the !PXE and the PXENV+ entry points.
-	 */
-	__asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
-					   "pushw %%di\n\t"
-					   "pushw %%bx\n\t"
-					   "lcall *%c3\n\t"
-					   "addw $6, %%sp\n\t" )
-			       : "=a" ( exit ), "=b" ( discard_b ),
-			         "=D" ( discard_D )
-			       : "p" ( __from_data16 ( &undinet_entry_point )),
-			         "b" ( function ),
-			         "D" ( __from_data16 ( &undinet_params ) )
-			       : "ecx", "edx", "esi", "ebp" );
-
-	/* UNDI API calls may rudely change the status of A20 and not
-	 * bother to restore it afterwards.  Intel is known to be
-	 * guilty of this.
-	 *
-	 * Note that we will return to this point even if A20 gets
-	 * screwed up by the UNDI driver, because Etherboot always
-	 * resides in an even megabyte of RAM.
-	 */	
-	gateA20_set();
-
-	/* Determine return status code based on PXENV_EXIT and
-	 * PXENV_STATUS
-	 */
-	if ( exit == PXENV_EXIT_SUCCESS ) {
-		rc = 0;
-	} else {
-		rc = -undinet_params.Status;
-		/* Paranoia; don't return success for the combination
-		 * of PXENV_EXIT_FAILURE but PXENV_STATUS_SUCCESS
-		 */
-		if ( rc == 0 )
-			rc = -EIO;
-	}
-
-	/* If anything goes wrong, print as much debug information as
-	 * it's possible to give.
-	 */
-	if ( rc != 0 ) {
-		SEGOFF16_t rm_params = {
-			.segment = rm_ds,
-			.offset = __from_data16 ( &undinet_params ),
-		};
-
-		DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
-		       undinet_function_name ( function ), strerror ( rc ) );
-		DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
-		       "%#02zx, entry point at %04x:%04x\n", undinic,
-		       rm_params.segment, rm_params.offset, params_len,
-		       undinet_entry_point.segment,
-		       undinet_entry_point.offset );
-		DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
-		DBGC_HDA ( undinic, rm_params, params, params_len );
-		DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
-		DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
-	}
-
-	/* Copy parameter block back */
-	memcpy ( params, &undinet_params, params_len );
-
-	return rc;
-}
+/** Address of UNDI entry point */
+static SEGOFF16_t undinet_entry;
 
 /*****************************************************************************
  *
@@ -335,7 +167,6 @@ static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
  */
 static int undinet_transmit ( struct net_device *netdev,
 			      struct io_buffer *iobuf ) {
-	struct undi_nic *undinic = netdev->priv;
 	struct s_PXENV_UNDI_TRANSMIT undi_transmit;
 	size_t len = iob_len ( iobuf );
 	int rc;
@@ -368,9 +199,9 @@ static int undinet_transmit ( struct net_device *netdev,
 	undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
 
 	/* Issue PXE API call */
-	if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT,
-				   &undi_transmit,
-				   sizeof ( undi_transmit ) ) ) != 0 )
+	if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_TRANSMIT,
+				     &undi_transmit,
+				     sizeof ( undi_transmit ) ) ) != 0 )
 		goto done;
 
 	/* Free I/O buffer */
@@ -440,8 +271,9 @@ static void undinet_poll ( struct net_device *netdev ) {
 
 	/* Run through the ISR loop */
 	while ( 1 ) {
-		if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
-					   sizeof ( undi_isr ) ) ) != 0 )
+		if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
+					     &undi_isr,
+					     sizeof ( undi_isr ) ) ) != 0 )
 			break;
 		switch ( undi_isr.FuncFlag ) {
 		case PXENV_UNDI_ISR_OUT_TRANSMIT:
@@ -537,8 +369,8 @@ static int undinet_open ( struct net_device *netdev ) {
 	 */
 	memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
 		 sizeof ( undi_set_address.StationAddress ) );
-	undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
-		       &undi_set_address, sizeof ( undi_set_address ) );
+	pxeparent_call ( undinet_entry, PXENV_UNDI_SET_STATION_ADDRESS,
+			 &undi_set_address, sizeof ( undi_set_address ) );
 
 	/* Open NIC.  We ask for promiscuous operation, since it's the
 	 * only way to ask for all multicast addresses.  On any
@@ -547,8 +379,8 @@ static int undinet_open ( struct net_device *netdev ) {
 	 */
 	memset ( &undi_open, 0, sizeof ( undi_open ) );
 	undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
-	if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
-				   sizeof ( undi_open ) ) ) != 0 )
+	if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_OPEN,
+				     &undi_open, sizeof ( undi_open ) ) ) != 0 )
 		goto err;
 
 	DBGC ( undinic, "UNDINIC %p opened\n", undinic );
@@ -573,8 +405,9 @@ static void undinet_close ( struct net_device *netdev ) {
 	/* Ensure ISR has exited cleanly */
 	while ( undinic->isr_processing ) {
 		undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
-		if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
-					   sizeof ( undi_isr ) ) ) != 0 )
+		if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
+					     &undi_isr,
+					     sizeof ( undi_isr ) ) ) != 0 )
 			break;
 		switch ( undi_isr.FuncFlag ) {
 		case PXENV_UNDI_ISR_OUT_TRANSMIT:
@@ -589,8 +422,8 @@ static void undinet_close ( struct net_device *netdev ) {
 	}
 
 	/* Close NIC */
-	undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
-		       sizeof ( undi_close ) );
+	pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
+			 &undi_close, sizeof ( undi_close ) );
 
 	/* Disable interrupt and unhook ISR */
 	disable_irq ( undinic->irq );
@@ -650,7 +483,7 @@ int undinet_probe ( struct undi_device *undi ) {
 	undi_set_drvdata ( undi, netdev );
 	netdev->dev = &undi->dev;
 	memset ( undinic, 0, sizeof ( *undinic ) );
-	undinet_entry_point = undi->entry;
+	undinet_entry = undi->entry;
 	DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
 
 	/* Hook in UNDI stack */
@@ -661,9 +494,9 @@ int undinet_probe ( struct undi_device *undi ) {
 		start_undi.DX = undi->isapnp_read_port;
 		start_undi.ES = BIOS_SEG;
 		start_undi.DI = find_pnp_bios();
-		if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
-					   &start_undi,
-					   sizeof ( start_undi ) ) ) != 0 )
+		if ( ( rc = pxeparent_call ( undinet_entry, PXENV_START_UNDI,
+					     &start_undi,
+					     sizeof ( start_undi ) ) ) != 0 )
 			goto err_start_undi;
 	}
 	undi->flags |= UNDI_FL_STARTED;
@@ -671,24 +504,25 @@ int undinet_probe ( struct undi_device *undi ) {
 	/* Bring up UNDI stack */
 	if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
 		memset ( &undi_startup, 0, sizeof ( undi_startup ) );
-		if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
-					   &undi_startup,
-					   sizeof ( undi_startup ) ) ) != 0 )
+		if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_STARTUP,
+					     &undi_startup,
+					     sizeof ( undi_startup ) ) ) != 0 )
 			goto err_undi_startup;
 		memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
-		if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE,
-					   &undi_initialize,
-					   sizeof ( undi_initialize ))) != 0 )
+		if ( ( rc = pxeparent_call ( undinet_entry,
+					     PXENV_UNDI_INITIALIZE,
+					     &undi_initialize,
+					     sizeof ( undi_initialize ))) != 0 )
 			goto err_undi_initialize;
 	}
 	undi->flags |= UNDI_FL_INITIALIZED;
 
 	/* Get device information */
 	memset ( &undi_info, 0, sizeof ( undi_info ) );
-	if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
-				   &undi_info, sizeof ( undi_info ) ) ) != 0 )
+	if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_INFORMATION,
+				     &undi_info, sizeof ( undi_info ) ) ) != 0 )
 		goto err_undi_get_information;
-	memcpy ( netdev->ll_addr, undi_info.PermNodeAddress, ETH_ALEN );
+	memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
 	undinic->irq = undi_info.IntNumber;
 	if ( undinic->irq > IRQ_MAX ) {
 		DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n",
@@ -696,16 +530,17 @@ int undinet_probe ( struct undi_device *undi ) {
 		goto err_bad_irq;
 	}
 	DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
-	       undinic, eth_ntoa ( netdev->ll_addr ), undinic->irq );
+	       undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
 
 	/* Get interface information */
 	memset ( &undi_iface, 0, sizeof ( undi_iface ) );
-	if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
-				   &undi_iface,
-				   sizeof ( undi_iface ) ) ) != 0 )
+	if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_IFACE_INFO,
+				     &undi_iface,
+				     sizeof ( undi_iface ) ) ) != 0 )
 		goto err_undi_get_iface_info;
-	DBGC ( undinic, "UNDINIC %p has type %s and link speed %d\n",
-	       undinic, undi_iface.IfaceType, undi_iface.LinkSpeed );
+	DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
+	       undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
+	       undi_iface.ServiceFlags );
 	if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
 		       sizeof ( undi_iface.IfaceType ) ) == 0 ) {
 		DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
@@ -730,17 +565,17 @@ int undinet_probe ( struct undi_device *undi ) {
  err_undi_initialize:
 	/* Shut down UNDI stack */
 	memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
-	undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
-		       sizeof ( undi_shutdown ) );
+	pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
+			 sizeof ( undi_shutdown ) );
 	memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
-	undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
-		       sizeof ( undi_cleanup ) );
+	pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, &undi_cleanup,
+			 sizeof ( undi_cleanup ) );
 	undi->flags &= ~UNDI_FL_INITIALIZED;
  err_undi_startup:
 	/* Unhook UNDI stack */
 	memset ( &stop_undi, 0, sizeof ( stop_undi ) );
-	undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
-		       sizeof ( stop_undi ) );
+	pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
+			 sizeof ( stop_undi ) );
 	undi->flags &= ~UNDI_FL_STARTED;
  err_start_undi:
 	netdev_nullify ( netdev );
@@ -771,22 +606,22 @@ void undinet_remove ( struct undi_device *undi ) {
 
 		/* Shut down UNDI stack */
 		memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
-		undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
-			       sizeof ( undi_shutdown ) );
+		pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN,
+				 &undi_shutdown, sizeof ( undi_shutdown ) );
 		memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
-		undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
-			       sizeof ( undi_cleanup ) );
+		pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP,
+				 &undi_cleanup, sizeof ( undi_cleanup ) );
 		undi->flags &= ~UNDI_FL_INITIALIZED;
 
 		/* Unhook UNDI stack */
 		memset ( &stop_undi, 0, sizeof ( stop_undi ) );
-		undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
-			       sizeof ( stop_undi ) );
+		pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
+				 sizeof ( stop_undi ) );
 		undi->flags &= ~UNDI_FL_STARTED;
 	}
 
 	/* Clear entry point */
-	memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );
+	memset ( &undinet_entry, 0, sizeof ( undinet_entry ) );
 
 	/* Free network device */
 	netdev_nullify ( netdev );
diff --git a/gpxe/src/arch/i386/drivers/net/undionly.c b/gpxe/src/arch/i386/drivers/net/undionly.c
index 4cdce67..7dfb5d1 100644
--- a/gpxe/src/arch/i386/drivers/net/undionly.c
+++ b/gpxe/src/arch/i386/drivers/net/undionly.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/gpxe/src/arch/i386/drivers/net/undipreload.c b/gpxe/src/arch/i386/drivers/net/undipreload.c
index e29d150..a4b2f4a 100644
--- a/gpxe/src/arch/i386/drivers/net/undipreload.c
+++ b/gpxe/src/arch/i386/drivers/net/undipreload.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <realmode.h>
 #include <undipreload.h>
 
diff --git a/gpxe/src/arch/i386/drivers/net/undirom.c b/gpxe/src/arch/i386/drivers/net/undirom.c
index e578278..2463d96 100644
--- a/gpxe/src/arch/i386/drivers/net/undirom.c
+++ b/gpxe/src/arch/i386/drivers/net/undirom.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/gpxe/src/arch/i386/firmware/pcbios/basemem.c b/gpxe/src/arch/i386/firmware/pcbios/basemem.c
index b126d2a..1ba7d1f 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/basemem.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/basemem.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <realmode.h>
 #include <bios.h>
diff --git a/gpxe/src/arch/i386/firmware/pcbios/bios_console.c b/gpxe/src/arch/i386/firmware/pcbios/bios_console.c
index 9136377..1d18e54 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/bios_console.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/bios_console.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <assert.h>
 #include <realmode.h>
 #include <console.h>
diff --git a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
index decb083..99ca519 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
+++ b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
@@ -15,7 +15,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-	
+
+FILE_LICENCE ( GPL2_OR_LATER )
+
 	.text
 	.arch i386
 	.code16
@@ -235,6 +237,7 @@ get_underlying_e820:
 	popw	%di
 	incw	%bx
 	movl	%edx, %eax
+	clc
 	ret
 2:	
 	/* If the requested region is earlier than the cached region,
diff --git a/gpxe/src/arch/i386/firmware/pcbios/fakee820.c b/gpxe/src/arch/i386/firmware/pcbios/fakee820.c
index 552bf41..ea116fe 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/fakee820.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/fakee820.c
@@ -15,6 +15,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <realmode.h>
 #include <biosint.h>
 
diff --git a/gpxe/src/arch/i386/firmware/pcbios/gateA20.c b/gpxe/src/arch/i386/firmware/pcbios/gateA20.c
index 34e3ac5..1a71472 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/gateA20.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/gateA20.c
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <realmode.h>
 #include <bios.h>
diff --git a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c
index 620b62e..17082c3 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c
@@ -15,6 +15,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <assert.h>
 #include <realmode.h>
 #include <biosint.h>
diff --git a/gpxe/src/arch/i386/firmware/pcbios/memmap.c b/gpxe/src/arch/i386/firmware/pcbios/memmap.c
index ff387d9..8a30dba 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/memmap.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/memmap.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <errno.h>
 #include <realmode.h>
diff --git a/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c b/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c
index 420d2ae..c572914 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
diff --git a/gpxe/src/arch/i386/hci/commands/pxe_cmd.c b/gpxe/src/arch/i386/hci/commands/pxe_cmd.c
new file mode 100644
index 0000000..b5df2d1
--- /dev/null
+++ b/gpxe/src/arch/i386/hci/commands/pxe_cmd.c
@@ -0,0 +1,33 @@
+#include <gpxe/netdevice.h>
+#include <gpxe/command.h>
+#include <hci/ifmgmt_cmd.h>
+#include <pxe_call.h>
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+static int startpxe_payload ( struct net_device *netdev ) {
+	if ( netdev->state & NETDEV_OPEN )
+		pxe_activate ( netdev );
+	return 0;
+}
+
+static int startpxe_exec ( int argc, char **argv ) {
+	return ifcommon_exec ( argc, argv, startpxe_payload,
+			       "Activate PXE on" );
+}
+
+static int stoppxe_exec ( int argc __unused, char **argv __unused ) {
+	pxe_deactivate();
+	return 0;
+}
+
+struct command pxe_commands[] __command = {
+	{
+		.name = "startpxe",
+		.exec = startpxe_exec,
+	},
+	{
+		.name = "stoppxe",
+		.exec = stoppxe_exec,
+	},
+};
diff --git a/gpxe/src/arch/i386/image/bootsector.c b/gpxe/src/arch/i386/image/bootsector.c
index 0f297a2..f96cf20 100644
--- a/gpxe/src/arch/i386/image/bootsector.c
+++ b/gpxe/src/arch/i386/image/bootsector.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/arch/i386/image/bzimage.c b/gpxe/src/arch/i386/image/bzimage.c
index 47d46ca..1945099 100644
--- a/gpxe/src/arch/i386/image/bzimage.c
+++ b/gpxe/src/arch/i386/image/bzimage.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -42,9 +44,11 @@ FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 );
 struct image_type bzimage_image_type __image_type ( PROBE_NORMAL );
 
 /**
- * bzImage load context
+ * bzImage context
  */
-struct bzimage_load_context {
+struct bzimage_context {
+	/** Boot protocol version */
+	unsigned int version;
 	/** Real-mode kernel portion load segment address */
 	unsigned int rm_kernel_seg;
 	/** Real-mode kernel portion load address */
@@ -55,28 +59,14 @@ struct bzimage_load_context {
 	size_t rm_heap;
 	/** Command line (offset from rm_kernel) */
 	size_t rm_cmdline;
+	/** Command line maximum length */
+	size_t cmdline_size;
 	/** Real-mode kernel portion total memory size */
 	size_t rm_memsz;
 	/** Non-real-mode kernel portion load address */
 	userptr_t pm_kernel;
 	/** Non-real-mode kernel portion file and memory size */
 	size_t pm_sz;
-};
-
-/**
- * bzImage execution context
- */
-struct bzimage_exec_context {
-	/** Real-mode kernel portion load segment address */
-	unsigned int rm_kernel_seg;
-	/** Real-mode kernel portion load address */
-	userptr_t rm_kernel;
-	/** Real-mode heap top (offset from rm_kernel) */
-	size_t rm_heap;
-	/** Command line (offset from rm_kernel) */
-	size_t rm_cmdline;
-	/** Command line maximum length */
-	size_t cmdline_size;
 	/** Video mode */
 	unsigned int vid_mode;
 	/** Memory limit */
@@ -85,18 +75,178 @@ struct bzimage_exec_context {
 	physaddr_t ramdisk_image;
 	/** Initrd size */
 	physaddr_t ramdisk_size;
+
+	/** Command line magic block */
+	struct bzimage_cmdline cmdline_magic;
+	/** bzImage header */
+	struct bzimage_header bzhdr;
 };
 
 /**
+ * Parse bzImage header
+ *
+ * @v image		bzImage file
+ * @v bzimg		bzImage context
+ * @v src		bzImage to parse
+ * @ret rc		Return status code
+ */
+static int bzimage_parse_header ( struct image *image,
+				  struct bzimage_context *bzimg,
+				  userptr_t src ) {
+	unsigned int syssize;
+	int is_bzimage;
+
+	/* Sanity check */
+	if ( image->len < ( BZI_HDR_OFFSET + sizeof ( bzimg->bzhdr ) ) ) {
+		DBGC ( image, "bzImage %p too short for kernel header\n",
+		       image );
+		return -ENOEXEC;
+	}
+
+	/* Read in header structures */
+	memset ( bzimg, 0, sizeof ( *bzimg ) );
+	copy_from_user ( &bzimg->cmdline_magic, src, BZI_CMDLINE_OFFSET,
+			 sizeof ( bzimg->cmdline_magic ) );
+	copy_from_user ( &bzimg->bzhdr, src, BZI_HDR_OFFSET,
+			 sizeof ( bzimg->bzhdr ) );
+
+	/* Calculate size of real-mode portion */
+	bzimg->rm_filesz =
+		( ( bzimg->bzhdr.setup_sects ? bzimg->bzhdr.setup_sects : 4 ) + 1 ) << 9;
+	if ( bzimg->rm_filesz > image->len ) {
+		DBGC ( image, "bzImage %p too short for %zd byte of setup\n",
+		       image, bzimg->rm_filesz );
+		return -ENOEXEC;
+	}
+	bzimg->rm_memsz = BZI_ASSUMED_RM_SIZE;
+
+	/* Calculate size of protected-mode portion */
+	bzimg->pm_sz = ( image->len - bzimg->rm_filesz );
+	syssize = ( ( bzimg->pm_sz + 15 ) / 16 );
+
+	/* Check for signatures and determine version */
+	if ( bzimg->bzhdr.boot_flag != BZI_BOOT_FLAG ) {
+		DBGC ( image, "bzImage %p missing 55AA signature\n", image );
+		return -ENOEXEC;
+	}
+	if ( bzimg->bzhdr.header == BZI_SIGNATURE ) {
+		/* 2.00+ */
+		bzimg->version = bzimg->bzhdr.version;
+	} else {
+		/* Pre-2.00.  Check that the syssize field is correct,
+		 * as a guard against accepting arbitrary binary data,
+		 * since the 55AA check is pretty lax.  Note that the
+		 * syssize field is unreliable for protocols between
+		 * 2.00 and 2.03 inclusive, so we should not always
+		 * check this field.
+		 */
+		bzimg->version = 0x0100;
+		if ( bzimg->bzhdr.syssize != syssize ) {
+			DBGC ( image, "bzImage %p bad syssize %x (expected "
+			       "%x)\n", image, bzimg->bzhdr.syssize, syssize );
+			return -ENOEXEC;
+		}
+	}
+
+	/* Determine image type */
+	is_bzimage = ( ( bzimg->version >= 0x0200 ) ?
+		       ( bzimg->bzhdr.loadflags & BZI_LOAD_HIGH ) : 0 );
+
+	/* Calculate load address of real-mode portion */
+	bzimg->rm_kernel_seg = ( is_bzimage ? 0x1000 : 0x9000 );
+	bzimg->rm_kernel = real_to_user ( bzimg->rm_kernel_seg, 0 );
+
+	/* Allow space for the stack and heap */
+	bzimg->rm_memsz += BZI_STACK_SIZE;
+	bzimg->rm_heap = bzimg->rm_memsz;
+
+	/* Allow space for the command line */
+	bzimg->rm_cmdline = bzimg->rm_memsz;
+	bzimg->rm_memsz += BZI_CMDLINE_SIZE;
+
+	/* Calculate load address of protected-mode portion */
+	bzimg->pm_kernel = phys_to_user ( is_bzimage ? BZI_LOAD_HIGH_ADDR
+					: BZI_LOAD_LOW_ADDR );
+
+	/* Extract video mode */
+	bzimg->vid_mode = bzimg->bzhdr.vid_mode;
+
+	/* Extract memory limit */
+	bzimg->mem_limit = ( ( bzimg->version >= 0x0203 ) ?
+			     bzimg->bzhdr.initrd_addr_max : BZI_INITRD_MAX );
+
+	/* Extract command line size */
+	bzimg->cmdline_size = ( ( bzimg->version >= 0x0206 ) ?
+				bzimg->bzhdr.cmdline_size : BZI_CMDLINE_SIZE );
+
+	DBGC ( image, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx "
+	       "cmdlen %zd\n", image, bzimg->version,
+	       user_to_phys ( bzimg->rm_kernel, 0 ), bzimg->rm_filesz,
+	       user_to_phys ( bzimg->pm_kernel, 0 ), bzimg->pm_sz,
+	       bzimg->cmdline_size );
+
+	return 0;
+}
+
+/**
+ * Update bzImage header in loaded kernel
+ *
+ * @v image		bzImage file
+ * @v bzimg		bzImage context
+ * @v dst		bzImage to update
+ */
+static void bzimage_update_header ( struct image *image,
+				    struct bzimage_context *bzimg,
+				    userptr_t dst ) {
+
+	/* Set loader type */
+	if ( bzimg->version >= 0x0200 )
+		bzimg->bzhdr.type_of_loader = BZI_LOADER_TYPE_GPXE;
+
+	/* Set heap end pointer */
+	if ( bzimg->version >= 0x0201 ) {
+		bzimg->bzhdr.heap_end_ptr = ( bzimg->rm_heap - 0x200 );
+		bzimg->bzhdr.loadflags |= BZI_CAN_USE_HEAP;
+	}
+
+	/* Set command line */
+	if ( bzimg->version >= 0x0202 ) {
+		bzimg->bzhdr.cmd_line_ptr = user_to_phys ( bzimg->rm_kernel,
+							   bzimg->rm_cmdline );
+	} else {
+		bzimg->cmdline_magic.magic = BZI_CMDLINE_MAGIC;
+		bzimg->cmdline_magic.offset = bzimg->rm_cmdline;
+		bzimg->bzhdr.setup_move_size = bzimg->rm_memsz;
+	}
+
+	/* Set video mode */
+	bzimg->bzhdr.vid_mode = bzimg->vid_mode;
+
+	/* Set initrd address */
+	if ( bzimg->version >= 0x0200 ) {
+		bzimg->bzhdr.ramdisk_image = bzimg->ramdisk_image;
+		bzimg->bzhdr.ramdisk_size = bzimg->ramdisk_size;
+	}
+
+	/* Write out header structures */
+	copy_to_user ( dst, BZI_CMDLINE_OFFSET, &bzimg->cmdline_magic,
+		       sizeof ( bzimg->cmdline_magic ) );
+	copy_to_user ( dst, BZI_HDR_OFFSET, &bzimg->bzhdr,
+		       sizeof ( bzimg->bzhdr ) );
+
+	DBGC ( image, "bzImage %p vidmode %d\n", image, bzimg->vid_mode );
+}
+
+/**
  * Parse kernel command line for bootloader parameters
  *
  * @v image		bzImage file
- * @v exec_ctx		Execution context
+ * @v bzimg		bzImage context
  * @v cmdline		Kernel command line
  * @ret rc		Return status code
  */
 static int bzimage_parse_cmdline ( struct image *image,
-				   struct bzimage_exec_context *exec_ctx,
+				   struct bzimage_context *bzimg,
 				   const char *cmdline ) {
 	char *vga;
 	char *mem;
@@ -105,13 +255,13 @@ static int bzimage_parse_cmdline ( struct image *image,
 	if ( ( vga = strstr ( cmdline, "vga=" ) ) ) {
 		vga += 4;
 		if ( strcmp ( vga, "normal" ) == 0 ) {
-			exec_ctx->vid_mode = BZI_VID_MODE_NORMAL;
+			bzimg->vid_mode = BZI_VID_MODE_NORMAL;
 		} else if ( strcmp ( vga, "ext" ) == 0 ) {
-			exec_ctx->vid_mode = BZI_VID_MODE_EXT;
+			bzimg->vid_mode = BZI_VID_MODE_EXT;
 		} else if ( strcmp ( vga, "ask" ) == 0 ) {
-			exec_ctx->vid_mode = BZI_VID_MODE_ASK;
+			bzimg->vid_mode = BZI_VID_MODE_ASK;
 		} else {
-			exec_ctx->vid_mode = strtoul ( vga, &vga, 0 );
+			bzimg->vid_mode = strtoul ( vga, &vga, 0 );
 			if ( *vga && ( *vga != ' ' ) ) {
 				DBGC ( image, "bzImage %p strange \"vga=\""
 				       "terminator '%c'\n", image, *vga );
@@ -122,17 +272,17 @@ static int bzimage_parse_cmdline ( struct image *image,
 	/* Look for "mem=" */
 	if ( ( mem = strstr ( cmdline, "mem=" ) ) ) {
 		mem += 4;
-		exec_ctx->mem_limit = strtoul ( mem, &mem, 0 );
+		bzimg->mem_limit = strtoul ( mem, &mem, 0 );
 		switch ( *mem ) {
 		case 'G':
 		case 'g':
-			exec_ctx->mem_limit <<= 10;
+			bzimg->mem_limit <<= 10;
 		case 'M':
 		case 'm':
-			exec_ctx->mem_limit <<= 10;
+			bzimg->mem_limit <<= 10;
 		case 'K':
 		case 'k':
-			exec_ctx->mem_limit <<= 10;
+			bzimg->mem_limit <<= 10;
 			break;
 		case '\0':
 		case ' ':
@@ -142,7 +292,7 @@ static int bzimage_parse_cmdline ( struct image *image,
 			       "terminator '%c'\n", image, *mem );
 			break;
 		}
-		exec_ctx->mem_limit -= 1;
+		bzimg->mem_limit -= 1;
 	}
 
 	return 0;
@@ -152,20 +302,20 @@ static int bzimage_parse_cmdline ( struct image *image,
  * Set command line
  *
  * @v image		bzImage image
- * @v exec_ctx		Execution context
+ * @v bzimg		bzImage context
  * @v cmdline		Kernel command line
  * @ret rc		Return status code
  */
 static int bzimage_set_cmdline ( struct image *image,
-				 struct bzimage_exec_context *exec_ctx,
+				 struct bzimage_context *bzimg,
 				 const char *cmdline ) {
 	size_t cmdline_len;
 
 	/* Copy command line down to real-mode portion */
 	cmdline_len = ( strlen ( cmdline ) + 1 );
-	if ( cmdline_len > exec_ctx->cmdline_size )
-		cmdline_len = exec_ctx->cmdline_size;
-	copy_to_user ( exec_ctx->rm_kernel, exec_ctx->rm_cmdline,
+	if ( cmdline_len > bzimg->cmdline_size )
+		cmdline_len = bzimg->cmdline_size;
+	copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
 		       cmdline, cmdline_len );
 	DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
 
@@ -217,14 +367,16 @@ static size_t bzimage_load_initrd ( struct image *image,
 	}
 
 	/* Copy in initrd image body */
+	if ( address )
+		memcpy_user ( address, offset, initrd->data, 0, initrd->len );
+	offset += initrd->len;
 	if ( address ) {
 		DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n",
-		       image, initrd, address, ( address + offset ) );
-		memcpy_user ( address, offset, initrd->data, 0,
-			      initrd->len );
+		       image, initrd, user_to_phys ( address, 0 ),
+		       user_to_phys ( address, offset ) );
 	}
-	offset += initrd->len;
 
+	/* Round up to 4-byte boundary */
 	offset = ( ( offset + 0x03 ) & ~0x03 );
 	return offset;
 }
@@ -233,20 +385,19 @@ static size_t bzimage_load_initrd ( struct image *image,
  * Load initrds, if any
  *
  * @v image		bzImage image
- * @v exec_ctx		Execution context
+ * @v bzimg		bzImage context
  * @ret rc		Return status code
  */
 static int bzimage_load_initrds ( struct image *image,
-				  struct bzimage_exec_context *exec_ctx ) {
+				  struct bzimage_context *bzimg ) {
 	struct image *initrd;
 	size_t total_len = 0;
 	physaddr_t address;
 	int rc;
 
 	/* Add up length of all initrd images */
-	for_each_image ( initrd ) {
+	for_each_image ( initrd )
 		total_len += bzimage_load_initrd ( image, initrd, UNULL );
-	}
 
 	/* Give up if no initrd images found */
 	if ( ! total_len )
@@ -268,7 +419,7 @@ static int bzimage_load_initrds ( struct image *image,
 			return -ENOBUFS;
 		}
 		/* Check that we are within the kernel's range */
-		if ( ( address + total_len - 1 ) > exec_ctx->mem_limit )
+		if ( ( address + total_len - 1 ) > bzimg->mem_limit )
 			continue;
 		/* Prepare and verify segment */
 		if ( ( rc = prep_segment ( phys_to_user ( address ), 0,
@@ -279,8 +430,8 @@ static int bzimage_load_initrds ( struct image *image,
 	}
 
 	/* Record initrd location */
-	exec_ctx->ramdisk_image = address;
-	exec_ctx->ramdisk_size = total_len;
+	bzimg->ramdisk_image = address;
+	bzimg->ramdisk_size = total_len;
 
 	/* Construct initrd */
 	DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n",
@@ -300,60 +451,37 @@ static int bzimage_load_initrds ( struct image *image,
  * @ret rc		Return status code
  */
 static int bzimage_exec ( struct image *image ) {
-	struct bzimage_exec_context exec_ctx;
-	struct bzimage_header bzhdr;
+	struct bzimage_context bzimg;
 	const char *cmdline = ( image->cmdline ? image->cmdline : "" );
 	int rc;
 
-	/* Initialise context */
-	memset ( &exec_ctx, 0, sizeof ( exec_ctx ) );
-
-	/* Retrieve kernel header */
-	exec_ctx.rm_kernel_seg = image->priv.ul;
-	exec_ctx.rm_kernel = real_to_user ( exec_ctx.rm_kernel_seg, 0 );
-	copy_from_user ( &bzhdr, exec_ctx.rm_kernel, BZI_HDR_OFFSET,
-			 sizeof ( bzhdr ) );
-	exec_ctx.rm_cmdline = exec_ctx.rm_heap = 
-		( bzhdr.heap_end_ptr + 0x200 );
-	exec_ctx.vid_mode = bzhdr.vid_mode;
-	if ( bzhdr.version >= 0x0203 ) {
-		exec_ctx.mem_limit = bzhdr.initrd_addr_max;
-	} else {
-		exec_ctx.mem_limit = BZI_INITRD_MAX;
-	}
-	if ( bzhdr.version >= 0x0206 ) {
-		exec_ctx.cmdline_size = bzhdr.cmdline_size;
-	} else {
-		exec_ctx.cmdline_size = BZI_CMDLINE_SIZE;
-	}
-	DBG ( "cmdline_size = %zd\n", exec_ctx.cmdline_size );
+	/* Read and parse header from loaded kernel */
+	if ( ( rc = bzimage_parse_header ( image, &bzimg,
+					   image->priv.user ) ) != 0 )
+		return rc;
+	assert ( bzimg.rm_kernel == image->priv.user );
 
 	/* Parse command line for bootloader parameters */
-	if ( ( rc = bzimage_parse_cmdline ( image, &exec_ctx, cmdline ) ) != 0)
+	if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
 		return rc;
 
 	/* Store command line */
-	if ( ( rc = bzimage_set_cmdline ( image, &exec_ctx, cmdline ) ) != 0 )
+	if ( ( rc = bzimage_set_cmdline ( image, &bzimg, cmdline ) ) != 0 )
 		return rc;
 
 	/* Load any initrds */
-	if ( ( rc = bzimage_load_initrds ( image, &exec_ctx ) ) != 0 )
+	if ( ( rc = bzimage_load_initrds ( image, &bzimg ) ) != 0 )
 		return rc;
 
-	/* Update and store kernel header */
-	bzhdr.vid_mode = exec_ctx.vid_mode;
-	bzhdr.ramdisk_image = exec_ctx.ramdisk_image;
-	bzhdr.ramdisk_size = exec_ctx.ramdisk_size;
-	copy_to_user ( exec_ctx.rm_kernel, BZI_HDR_OFFSET, &bzhdr,
-		       sizeof ( bzhdr ) );
+	/* Update kernel header */
+	bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
 
 	/* Prepare for exiting */
 	shutdown ( SHUTDOWN_BOOT );
 
 	DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
-	       "(stack %04x:%04zx)\n", image,
-	       ( exec_ctx.rm_kernel_seg + 0x20 ),
-	       exec_ctx.rm_kernel_seg, exec_ctx.rm_heap );
+	       "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
+	       bzimg.rm_kernel_seg, bzimg.rm_heap );
 
 	/* Jump to the kernel */
 	__asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t"
@@ -365,9 +493,9 @@ static int bzimage_exec ( struct image *image ) {
 					   "pushw %w2\n\t"
 					   "pushw $0\n\t"
 					   "lret\n\t" )
-			       : : "r" ( exec_ctx.rm_kernel_seg ),
-			           "r" ( exec_ctx.rm_heap ),
-			           "r" ( exec_ctx.rm_kernel_seg + 0x20 ) );
+			       : : "r" ( bzimg.rm_kernel_seg ),
+			           "r" ( bzimg.rm_heap ),
+			           "r" ( bzimg.rm_kernel_seg + 0x20 ) );
 
 	/* There is no way for the image to return, since we provide
 	 * no return address.
@@ -378,192 +506,49 @@ static int bzimage_exec ( struct image *image ) {
 }
 
 /**
- * Load and parse bzImage header
- *
- * @v image		bzImage file
- * @v load_ctx		Load context
- * @v bzhdr		Buffer for bzImage header
- * @ret rc		Return status code
- */
-static int bzimage_load_header ( struct image *image,
-				 struct bzimage_load_context *load_ctx,
-				 struct bzimage_header *bzhdr ) {
-
-	/* Sanity check */
-	if ( image->len < ( BZI_HDR_OFFSET + sizeof ( *bzhdr ) ) ) {
-		DBGC ( image, "bzImage %p too short for kernel header\n",
-		       image );
-		return -ENOEXEC;
-	}
-
-	/* Read and verify header */
-	copy_from_user ( bzhdr, image->data, BZI_HDR_OFFSET,
-			 sizeof ( *bzhdr ) );
-	if ( bzhdr->header != BZI_SIGNATURE ) {
-		DBGC ( image, "bzImage %p bad signature %08x\n",
-		       image, bzhdr->header );
-		return -ENOEXEC;
-	}
-
-	/* We don't support ancient kernels */
-	if ( bzhdr->version < 0x0200 ) {
-		DBGC ( image, "bzImage %p version %04x not supported\n",
-		       image, bzhdr->version );
-		return -ENOTSUP;
-	}
-
-	/* Calculate load address and size of real-mode portion */
-	load_ctx->rm_kernel_seg = ( ( bzhdr->loadflags & BZI_LOAD_HIGH ) ?
-				    0x1000 :  /* 1000:0000 (bzImage) */
-				    0x9000 ); /* 9000:0000 (zImage) */
-	load_ctx->rm_kernel = real_to_user ( load_ctx->rm_kernel_seg, 0 );
-	load_ctx->rm_filesz =
-		( ( bzhdr->setup_sects ? bzhdr->setup_sects : 4 ) + 1 ) << 9;
-	load_ctx->rm_memsz = BZI_ASSUMED_RM_SIZE;
-	if ( load_ctx->rm_filesz > image->len ) {
-		DBGC ( image, "bzImage %p too short for %zd byte of setup\n",
-		       image, load_ctx->rm_filesz );
-		return -ENOEXEC;
-	}
-
-	/* Calculate load address and size of non-real-mode portion */
-	load_ctx->pm_kernel = ( ( bzhdr->loadflags & BZI_LOAD_HIGH ) ?
-				phys_to_user ( BZI_LOAD_HIGH_ADDR ) :
-				phys_to_user ( BZI_LOAD_LOW_ADDR ) );
-	load_ctx->pm_sz = ( image->len - load_ctx->rm_filesz );
-
-	DBGC ( image, "bzImage %p version %04x RM %#zx bytes PM %#zx bytes\n",
-	       image, bzhdr->version, load_ctx->rm_filesz, load_ctx->pm_sz );
-	return 0;
-}
-
-/**
- * Load real-mode portion of bzImage
+ * Load bzImage image into memory
  *
  * @v image		bzImage file
- * @v load_ctx		Load context
  * @ret rc		Return status code
  */
-static int bzimage_load_real ( struct image *image,
-			       struct bzimage_load_context *load_ctx ) {
+int bzimage_load ( struct image *image ) {
+	struct bzimage_context bzimg;
 	int rc;
 
-	/* Allow space for the stack and heap */
-	load_ctx->rm_memsz += BZI_STACK_SIZE;
-	load_ctx->rm_heap = load_ctx->rm_memsz;
+	/* Read and parse header from image */
+	if ( ( rc = bzimage_parse_header ( image, &bzimg,
+					   image->data ) ) != 0 )
+		return rc;
 
-	/* Allow space for the command line */
-	load_ctx->rm_cmdline = load_ctx->rm_memsz;
-	load_ctx->rm_memsz += BZI_CMDLINE_SIZE;
+	/* This is a bzImage image, valid or otherwise */
+	if ( ! image->type )
+		image->type = &bzimage_image_type;
 
-	/* Prepare, verify, and load the real-mode segment */
-	if ( ( rc = prep_segment ( load_ctx->rm_kernel, load_ctx->rm_filesz,
-				   load_ctx->rm_memsz ) ) != 0 ) {
+	/* Prepare segments */
+	if ( ( rc = prep_segment ( bzimg.rm_kernel, bzimg.rm_filesz,
+				   bzimg.rm_memsz ) ) != 0 ) {
 		DBGC ( image, "bzImage %p could not prepare RM segment: %s\n",
 		       image, strerror ( rc ) );
 		return rc;
 	}
-	memcpy_user ( load_ctx->rm_kernel, 0, image->data, 0,
-		      load_ctx->rm_filesz );
-
-	return 0;
-}
-
-/**
- * Load non-real-mode portion of bzImage
- *
- * @v image		bzImage file
- * @v load_ctx		Load context
- * @ret rc		Return status code
- */
-static int bzimage_load_non_real ( struct image *image,
-				   struct bzimage_load_context *load_ctx ) {
-	int rc;
-
-	/* Prepare, verify and load the non-real-mode segment */
-	if ( ( rc = prep_segment ( load_ctx->pm_kernel, load_ctx->pm_sz,
-				   load_ctx->pm_sz ) ) != 0 ) {
+	if ( ( rc = prep_segment ( bzimg.pm_kernel, bzimg.pm_sz,
+				   bzimg.pm_sz ) ) != 0 ) {
 		DBGC ( image, "bzImage %p could not prepare PM segment: %s\n",
 		       image, strerror ( rc ) );
 		return rc;
 	}
-	memcpy_user ( load_ctx->pm_kernel, 0, image->data, load_ctx->rm_filesz,
-		      load_ctx->pm_sz );
-
-	return 0;
-}
-
-/**
- * Update and store bzImage header
- *
- * @v image		bzImage file
- * @v load_ctx		Load context
- * @v bzhdr		Original bzImage header
- * @ret rc		Return status code
- */
-static int bzimage_write_header ( struct image *image __unused,
-				  struct bzimage_load_context *load_ctx,
-				  struct bzimage_header *bzhdr ) {
-	struct bzimage_cmdline cmdline;
-
-	/* Update the header and copy it into the loaded kernel */
-	bzhdr->type_of_loader = BZI_LOADER_TYPE_GPXE;
-	if ( bzhdr->version >= 0x0201 ) {
-		bzhdr->heap_end_ptr = ( load_ctx->rm_heap - 0x200 );
-		bzhdr->loadflags |= BZI_CAN_USE_HEAP;
-	}
-	if ( bzhdr->version >= 0x0202 ) {
-		bzhdr->cmd_line_ptr = user_to_phys ( load_ctx->rm_kernel,
-						     load_ctx->rm_cmdline );
-	} else {
-		cmdline.magic = BZI_CMDLINE_MAGIC;
-		cmdline.offset = load_ctx->rm_cmdline;
-		copy_to_user ( load_ctx->rm_kernel, BZI_CMDLINE_OFFSET,
-			       &cmdline, sizeof ( cmdline ) );
-		bzhdr->setup_move_size = load_ctx->rm_memsz;
-	}
-	copy_to_user ( load_ctx->rm_kernel, BZI_HDR_OFFSET,
-		       bzhdr, sizeof ( *bzhdr ) );
-
-	return 0;
-}
-
-/**
- * Load bzImage image into memory
- *
- * @v image		bzImage file
- * @ret rc		Return status code
- */
-int bzimage_load ( struct image *image ) {
-	struct bzimage_load_context load_ctx;
-	struct bzimage_header bzhdr;
-	int rc;
-
-	/* Initialise context */
-	memset ( &load_ctx, 0, sizeof ( load_ctx ) );
 
-	/* Load and verify header */
-	if ( ( rc = bzimage_load_header ( image, &load_ctx, &bzhdr ) ) != 0 )
-		return rc;
-
-	/* This is a bzImage image, valid or otherwise */
-	if ( ! image->type )
-		image->type = &bzimage_image_type;
-
-	/* Load real-mode portion */
-	if ( ( rc = bzimage_load_real ( image, &load_ctx ) ) != 0 )
-		return rc;
-
-	/* Load non-real-mode portion */
-	if ( ( rc = bzimage_load_non_real ( image, &load_ctx ) ) != 0 )
-		return rc;
+	/* Load segments */
+	memcpy_user ( bzimg.rm_kernel, 0, image->data,
+		      0, bzimg.rm_filesz );
+	memcpy_user ( bzimg.pm_kernel, 0, image->data,
+		      bzimg.rm_filesz, bzimg.pm_sz );
 
 	/* Update and write out header */
-	if ( ( rc = bzimage_write_header ( image, &load_ctx, &bzhdr ) ) != 0 )
-		return rc;
+	bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
 
 	/* Record real-mode segment in image private data field */
-	image->priv.ul = load_ctx.rm_kernel_seg;
+	image->priv.user = bzimg.rm_kernel;
 
 	return 0;
 }
diff --git a/gpxe/src/arch/i386/image/com32.c b/gpxe/src/arch/i386/image/com32.c
index d1b9a59..6ab347c 100644
--- a/gpxe/src/arch/i386/image/com32.c
+++ b/gpxe/src/arch/i386/image/com32.c
@@ -23,6 +23,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/gpxe/src/arch/i386/image/comboot.c b/gpxe/src/arch/i386/image/comboot.c
index 40e3218..a00b2b9 100644
--- a/gpxe/src/arch/i386/image/comboot.c
+++ b/gpxe/src/arch/i386/image/comboot.c
@@ -23,6 +23,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/gpxe/src/arch/i386/image/elfboot.c b/gpxe/src/arch/i386/image/elfboot.c
index c8daf72..a41040e 100644
--- a/gpxe/src/arch/i386/image/elfboot.c
+++ b/gpxe/src/arch/i386/image/elfboot.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <errno.h>
 #include <elf.h>
 #include <gpxe/image.h>
diff --git a/gpxe/src/arch/i386/image/eltorito.c b/gpxe/src/arch/i386/image/eltorito.c
index 9d57310..53eb2c0 100644
--- a/gpxe/src/arch/i386/image/eltorito.c
+++ b/gpxe/src/arch/i386/image/eltorito.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/arch/i386/image/multiboot.c b/gpxe/src/arch/i386/image/multiboot.c
index 52bb10f..5b62095 100644
--- a/gpxe/src/arch/i386/image/multiboot.c
+++ b/gpxe/src/arch/i386/image/multiboot.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -139,10 +141,11 @@ static void multiboot_build_memmap ( struct image *image,
 /**
  * Add command line in base memory
  *
+ * @v imgname		Image name
  * @v cmdline		Command line
  * @ret physaddr	Physical address of command line
  */
-physaddr_t multiboot_add_cmdline ( const char *cmdline ) {
+physaddr_t multiboot_add_cmdline ( const char *imgname, const char *cmdline ) {
 	char *mb_cmdline;
 
 	if ( ! cmdline )
@@ -153,7 +156,7 @@ physaddr_t multiboot_add_cmdline ( const char *cmdline ) {
 	mb_cmdline_offset +=
 		( snprintf ( mb_cmdline,
 			     ( sizeof ( mb_cmdlines ) - mb_cmdline_offset ),
-			     "%s", cmdline ) + 1 );
+			     "%s %s", imgname, cmdline ) + 1 );
 
 	/* Truncate to terminating NUL in buffer if necessary */
 	if ( mb_cmdline_offset > sizeof ( mb_cmdlines ) )
@@ -208,8 +211,8 @@ multiboot_build_module_list ( struct image *image,
 			  ( ( count - insert ) * sizeof ( *module ) ) );
 		module->mod_start = start;
 		module->mod_end = end;
-		module->string =
-			multiboot_add_cmdline ( module_image->cmdline );
+		module->string = multiboot_add_cmdline ( module_image->name,
+						       module_image->cmdline );
 		module->reserved = 0;
 		
 		/* We promise to page-align modules */
@@ -264,10 +267,8 @@ static int multiboot_exec ( struct image *image ) {
 	memset ( &mbinfo, 0, sizeof ( mbinfo ) );
 	mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP |
 			 MBI_FLAG_CMDLINE | MBI_FLAG_MODS );
-	multiboot_build_memmap ( image, &mbinfo, mbmemmap,
-				 ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) );
 	mb_cmdline_offset = 0;
-	mbinfo.cmdline = multiboot_add_cmdline ( image->cmdline );
+	mbinfo.cmdline = multiboot_add_cmdline ( image->name, image->cmdline );
 	mbinfo.mods_count = multiboot_build_module_list ( image, mbmodules,
 				( sizeof(mbmodules) / sizeof(mbmodules[0]) ) );
 	mbinfo.mods_addr = virt_to_phys ( mbmodules );
@@ -279,6 +280,12 @@ static int multiboot_exec ( struct image *image ) {
 	 */
 	shutdown ( SHUTDOWN_BOOT );
 
+	/* Build memory map after unhiding bootloader memory regions as part of
+	 * shutting everything down.
+	 */
+	multiboot_build_memmap ( image, &mbinfo, mbmemmap,
+				 ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) );
+
 	/* Jump to OS with flat physical addressing */
 	DBGC ( image, "MULTIBOOT %p starting execution at %lx\n",
 	       image, entry );
@@ -360,6 +367,13 @@ static int multiboot_load_raw ( struct image *image,
 	userptr_t buffer;
 	int rc;
 
+	/* Sanity check */
+	if ( ! ( hdr->mb.flags & MB_FLAG_RAW ) ) {
+		DBGC ( image, "MULTIBOOT %p is not flagged as a raw image\n",
+		       image );
+		return -EINVAL;
+	}
+
 	/* Verify and prepare segment */
 	offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
 	filesz = ( hdr->mb.load_end_addr ?
@@ -432,14 +446,14 @@ static int multiboot_load ( struct image *image ) {
 		return -ENOTSUP;
 	}
 
-	/* Load the actual image */
-	if ( hdr.mb.flags & MB_FLAG_RAW ) {
-		if ( ( rc = multiboot_load_raw ( image, &hdr ) ) != 0 )
-			return rc;
-	} else {
-		if ( ( rc = multiboot_load_elf ( image ) ) != 0 )
-			return rc;
-	}
+	/* There is technically a bit MB_FLAG_RAW to indicate whether
+	 * this is an ELF or a raw image.  In practice, grub will use
+	 * the ELF header if present, and Solaris relies on this
+	 * behaviour.
+	 */
+	if ( ( ( rc = multiboot_load_elf ( image ) ) != 0 ) &&
+	     ( ( rc = multiboot_load_raw ( image, &hdr ) ) != 0 ) )
+		return rc;
 
 	return 0;
 }
diff --git a/gpxe/src/arch/i386/image/pxe_image.c b/gpxe/src/arch/i386/image/pxe_image.c
index 90550d8..63429f8 100644
--- a/gpxe/src/arch/i386/image/pxe_image.c
+++ b/gpxe/src/arch/i386/image/pxe_image.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -42,28 +44,24 @@ struct image_type pxe_image_type __image_type ( PROBE_PXE );
  * @ret rc		Return status code
  */
 static int pxe_exec ( struct image *image ) {
+	struct net_device *netdev;
 	int rc;
 
-	/* Ensure that PXE stack is ready to use */
-	pxe_init_structures();
-	pxe_hook_int1a();
-
 	/* Arbitrarily pick the most recently opened network device */
-	pxe_set_netdev ( last_opened_netdev() );
-
-	/* Many things will break if pxe_netdev is NULL */
-	if ( ! pxe_netdev ) {
+	if ( ( netdev = last_opened_netdev() ) == NULL ) {
 		DBGC ( image, "IMAGE %p could not locate PXE net device\n",
 		       image );
 		return -ENODEV;
 	}
 
+	/* Activate PXE */
+	pxe_activate ( netdev );
+
 	/* Start PXE NBP */
 	rc = pxe_start_nbp();
 
 	/* Deactivate PXE */
-	pxe_set_netdev ( NULL );
-	pxe_unhook_int1a();
+	pxe_deactivate();
 
 	return rc;
 }
diff --git a/gpxe/src/arch/i386/include/basemem.h b/gpxe/src/arch/i386/include/basemem.h
index cd5668e..c477c7f 100644
--- a/gpxe/src/arch/i386/include/basemem.h
+++ b/gpxe/src/arch/i386/include/basemem.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <realmode.h>
 #include <bios.h>
diff --git a/gpxe/src/arch/i386/include/basemem_packet.h b/gpxe/src/arch/i386/include/basemem_packet.h
index e4d4f49..3cb4776 100644
--- a/gpxe/src/arch/i386/include/basemem_packet.h
+++ b/gpxe/src/arch/i386/include/basemem_packet.h
@@ -1,6 +1,8 @@
 #ifndef BASEMEM_PACKET_H
 #define BASEMEM_PACKET_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <realmode.h>
 
 /** Maximum length of base memory packet buffer */
diff --git a/gpxe/src/arch/i386/include/bios.h b/gpxe/src/arch/i386/include/bios.h
index 979a092..70bb73d 100644
--- a/gpxe/src/arch/i386/include/bios.h
+++ b/gpxe/src/arch/i386/include/bios.h
@@ -1,6 +1,8 @@
 #ifndef BIOS_H
 #define BIOS_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define BDA_SEG 0x0040
 #define BDA_FBMS 0x0013
 #define BDA_NUM_DRIVES 0x0075
diff --git a/gpxe/src/arch/i386/include/biosint.h b/gpxe/src/arch/i386/include/biosint.h
index d365cf0..ab466af 100644
--- a/gpxe/src/arch/i386/include/biosint.h
+++ b/gpxe/src/arch/i386/include/biosint.h
@@ -6,6 +6,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <realmode.h>
 
 struct segoff;
diff --git a/gpxe/src/arch/i386/include/bits/byteswap.h b/gpxe/src/arch/i386/include/bits/byteswap.h
index 98418c2..ddbd40e 100644
--- a/gpxe/src/arch/i386/include/bits/byteswap.h
+++ b/gpxe/src/arch/i386/include/bits/byteswap.h
@@ -1,6 +1,8 @@
 #ifndef ETHERBOOT_BITS_BYTESWAP_H
 #define ETHERBOOT_BITS_BYTESWAP_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static inline __attribute__ ((always_inline, const)) uint16_t
 __bswap_variable_16(uint16_t x)
 {
diff --git a/gpxe/src/arch/i386/include/bits/compiler.h b/gpxe/src/arch/i386/include/bits/compiler.h
index 119a9a2..000db0c 100644
--- a/gpxe/src/arch/i386/include/bits/compiler.h
+++ b/gpxe/src/arch/i386/include/bits/compiler.h
@@ -1,6 +1,8 @@
 #ifndef _BITS_COMPILER_H
 #define _BITS_COMPILER_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef ASSEMBLY
 
 /** Declare a function with standard calling conventions */
diff --git a/gpxe/src/arch/i386/include/bits/endian.h b/gpxe/src/arch/i386/include/bits/endian.h
index 413e702..8418854 100644
--- a/gpxe/src/arch/i386/include/bits/endian.h
+++ b/gpxe/src/arch/i386/include/bits/endian.h
@@ -1,6 +1,8 @@
 #ifndef ETHERBOOT_BITS_ENDIAN_H
 #define ETHERBOOT_BITS_ENDIAN_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define __BYTE_ORDER __LITTLE_ENDIAN
 
 #endif /* ETHERBOOT_BITS_ENDIAN_H */
diff --git a/gpxe/src/arch/i386/include/bits/errfile.h b/gpxe/src/arch/i386/include/bits/errfile.h
index 5ea8a31..32b8a08 100644
--- a/gpxe/src/arch/i386/include/bits/errfile.h
+++ b/gpxe/src/arch/i386/include/bits/errfile.h
@@ -1,6 +1,8 @@
 #ifndef _BITS_ERRFILE_H
 #define _BITS_ERRFILE_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @addtogroup errfile Error file identifiers
  * @{
@@ -12,6 +14,7 @@
 #define ERRFILE_bios_smbios	( ERRFILE_ARCH | ERRFILE_CORE | 0x00030000 )
 #define ERRFILE_biosint		( ERRFILE_ARCH | ERRFILE_CORE | 0x00040000 )
 #define ERRFILE_int13		( ERRFILE_ARCH | ERRFILE_CORE | 0x00050000 )
+#define ERRFILE_pxeparent	( ERRFILE_ARCH | ERRFILE_CORE | 0x00060000 )
 
 #define ERRFILE_bootsector     ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
 #define ERRFILE_bzimage	       ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )
diff --git a/gpxe/src/arch/i386/include/bits/io.h b/gpxe/src/arch/i386/include/bits/io.h
index dd0ee44..eded977 100644
--- a/gpxe/src/arch/i386/include/bits/io.h
+++ b/gpxe/src/arch/i386/include/bits/io.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/x86_io.h>
 
 #endif /* _BITS_IO_H */
diff --git a/gpxe/src/arch/i386/include/bits/nap.h b/gpxe/src/arch/i386/include/bits/nap.h
index f8ba7a7..1354f6b 100644
--- a/gpxe/src/arch/i386/include/bits/nap.h
+++ b/gpxe/src/arch/i386/include/bits/nap.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/bios_nap.h>
 #include <gpxe/efi/efix86_nap.h>
 
diff --git a/gpxe/src/arch/i386/include/bits/smbios.h b/gpxe/src/arch/i386/include/bits/smbios.h
index 647ea19..a68413a 100644
--- a/gpxe/src/arch/i386/include/bits/smbios.h
+++ b/gpxe/src/arch/i386/include/bits/smbios.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/bios_smbios.h>
 
 #endif /* _BITS_SMBIOS_H */
diff --git a/gpxe/src/arch/i386/include/bits/stdint.h b/gpxe/src/arch/i386/include/bits/stdint.h
index 6ccf097..8edf131 100644
--- a/gpxe/src/arch/i386/include/bits/stdint.h
+++ b/gpxe/src/arch/i386/include/bits/stdint.h
@@ -1,8 +1,10 @@
 #ifndef _BITS_STDINT_H
 #define _BITS_STDINT_H
 
-typedef unsigned int		size_t;
-typedef signed int		ssize_t;
+FILE_LICENCE ( GPL2_OR_LATER );
+
+typedef __SIZE_TYPE__		size_t;
+typedef signed long		ssize_t;
 typedef signed long		off_t;
 
 typedef unsigned char		uint8_t;
diff --git a/gpxe/src/arch/i386/include/bits/timer.h b/gpxe/src/arch/i386/include/bits/timer.h
index 99666d8..32e6bd4 100644
--- a/gpxe/src/arch/i386/include/bits/timer.h
+++ b/gpxe/src/arch/i386/include/bits/timer.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/bios_timer.h>
 #include <gpxe/rdtsc_timer.h>
 
diff --git a/gpxe/src/arch/i386/include/bits/uaccess.h b/gpxe/src/arch/i386/include/bits/uaccess.h
index 0ecc502..2bb52e0 100644
--- a/gpxe/src/arch/i386/include/bits/uaccess.h
+++ b/gpxe/src/arch/i386/include/bits/uaccess.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <librm.h>
 
 #endif /* _BITS_UACCESS_H */
diff --git a/gpxe/src/arch/i386/include/bits/umalloc.h b/gpxe/src/arch/i386/include/bits/umalloc.h
index dcbd0a6..17ba2cb 100644
--- a/gpxe/src/arch/i386/include/bits/umalloc.h
+++ b/gpxe/src/arch/i386/include/bits/umalloc.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/memtop_umalloc.h>
 
 #endif /* _BITS_UMALLOC_H */
diff --git a/gpxe/src/arch/i386/include/bootsector.h b/gpxe/src/arch/i386/include/bootsector.h
index e907105..8730fbf 100644
--- a/gpxe/src/arch/i386/include/bootsector.h
+++ b/gpxe/src/arch/i386/include/bootsector.h
@@ -6,6 +6,8 @@
  * x86 bootsector image format
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern int call_bootsector ( unsigned int segment, unsigned int offset,
 			     unsigned int drive );
 
diff --git a/gpxe/src/arch/i386/include/bzimage.h b/gpxe/src/arch/i386/include/bzimage.h
index aee058a..42b31fe 100644
--- a/gpxe/src/arch/i386/include/bzimage.h
+++ b/gpxe/src/arch/i386/include/bzimage.h
@@ -1,6 +1,8 @@
 #ifndef _BZIMAGE_H
 #define _BZIMAGE_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /**
@@ -75,6 +77,9 @@ struct bzimage_header {
 /** Offset of bzImage header within kernel image */
 #define BZI_HDR_OFFSET 0x1f1
 
+/** bzImage boot flag value */
+#define BZI_BOOT_FLAG 0xaa55
+
 /** bzImage magic signature value */
 #define BZI_SIGNATURE 0x53726448
 
diff --git a/gpxe/src/arch/i386/include/callbacks_arch.h b/gpxe/src/arch/i386/include/callbacks_arch.h
deleted file mode 100644
index f9cba48..0000000
--- a/gpxe/src/arch/i386/include/callbacks_arch.h
+++ /dev/null
@@ -1,243 +0,0 @@
-/* Callout/callback interface for Etherboot
- *
- * This file provides the mechanisms for making calls from Etherboot
- * to external programs and vice-versa.
- *
- * Initial version by Michael Brown <mbrown at fensystems.co.uk>, January 2004.
- *
- * $Id$
- */
-
-#ifndef CALLBACKS_ARCH_H
-#define CALLBACKS_ARCH_H
-
-/* Skip the definitions that won't make sense to the assembler */
-#ifndef ASSEMBLY
-
-/* Struct to hold general-purpose register values.  PUSHAL and POPAL
- * can work directly with this structure; do not change the order of
- * registers.
- */
-typedef struct {
-	union {
-		uint16_t di;
-		uint32_t edi;
-	};
-	union {
-		uint16_t si;
-		uint32_t esi;
-	};
-	union {
-		uint16_t bp;
-		uint32_t ebp;
-	};
-	union {
-		uint16_t sp;
-		uint32_t esp;
-	};
-	union {
-		struct {
-			uint8_t bl;
-			uint8_t bh;
-		} PACKED;
-		uint16_t bx;
-		uint32_t ebx;
-	};
-	union {
-		struct {
-			uint8_t dl;
-			uint8_t dh;
-		} PACKED;
-		uint16_t dx;
-		uint32_t edx;
-	};
-	union {
-		struct {
-			uint8_t cl;
-			uint8_t ch;
-		} PACKED;
-		uint16_t cx;
-		uint32_t ecx;
-	};
-	union {
-		struct {
-			uint8_t al;
-			uint8_t ah;
-		} PACKED;
-		uint16_t ax;
-		uint32_t eax;
-	};
-} regs_t;
-
-/* Struct to hold segment register values.  Don't change the order;
- * many bits of assembly code rely on it.
- */
-typedef struct {
-	uint16_t cs;
-	uint16_t ss;
-	uint16_t ds;
-	uint16_t es;
-	uint16_t fs;
-	uint16_t gs;
-} PACKED seg_regs_t;
-
-/* Struct for a GDT descriptor */
-typedef struct {
-		uint16_t limit;
-		uint32_t address;
-		uint16_t padding;
-} PACKED gdt_descriptor_t;
-
-/* Struct for a GDT entry.  Use GDT_SEGMENT() to fill it in.
- */
-typedef struct {
-	uint16_t limit_0_15;
-	uint16_t base_0_15;
-	uint8_t base_16_23;
-	uint8_t accessed__type__sflag__dpl__present;
-	uint8_t limit_16_19__avl__size__granularity;
-	uint8_t base_24_31;
-} PACKED gdt_segment_t;
-
-#define GDT_SEGMENT(base,limit,type,sflag,dpl,avl,size,granularity) \
-	( (gdt_segment_t) { \
-		( (limit) & 0xffff ), \
-		( (base) & 0xffff ), \
-		( ( (base) >> 16 ) & 0xff ), \
-		( ( 1 << 0 ) | ( (type) << 1 ) | \
-		  ( (sflag) << 4 ) | ( (dpl) << 5 ) | ( 1 << 7 ) ), \
-		( ( (limit) >> 16 ) | \
-		  ( (avl) << 4 ) | ( (size) << 5 ) | ( (granularity) << 7 ) ),\
-		( (base) >> 24 ) \
-	} )
-#define GDT_SEGMENT_BASE(gdt_segment) \
-	( (gdt_segment)->base_0_15 | \
-	  (gdt_segment)->base_16_23 << 16 | \
-	  (gdt_segment)->base_24_31 << 24 )
-#define GDT_SEGMENT_LIMIT(gdt_segment) \
-	( (gdt_segment)->limit_0_15 | \
-	  ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
-	      & 0xf ) << 16 ) )
-#define GDT_SEGMENT_GRANULARITY(gdt_segment) \
-	( ( (gdt_segment)->limit_16_19__avl__size__granularity \
-	    & 0x80 ) >> 7 )
-#define GDT_SEGMENT_TYPE(gdt_segment) \
-	( ( (gdt_segment)->accessed__type__sflag__dpl__present & 0x0e ) >> 1 )
-#define GDT_SEGMENT_SIZE(gdt_segment) \
-	( ( (gdt_segment)->limit_16_19__avl__size__granularity \
-	    & 0x60 ) >> 5 )
-
-#define GDT_TYPE_DATA			(0x0)
-#define GDT_TYPE_STACK			(0x2)
-#define GDT_TYPE_WRITEABLE		(0x1)
-#define GDT_TYPE_CODE			(0x6)
-#define GDT_TYPE_EXEC_ONLY_CODE		(0x4)
-#define GDT_TYPE_CONFORMING		(0x1)
-#define GDT_SFLAG_SYSTEM		(0)
-#define GDT_SFLAG_NORMAL		(1)
-#define GDT_AVL_NORMAL			(0)
-#define GDT_SIZE_16BIT			(0x0)
-#define GDT_SIZE_32BIT			(0x2)
-#define GDT_SIZE_64BIT			(0x1)
-#define GDT_SIZE_UNKNOWN		(0x3)
-#define GDT_GRANULARITY_SMALL		(0)
-#define GDT_GRANULARITY_LARGE		(1)
-#define GDT_SEGMENT_NORMAL(base,limit,type,size,granularity) \
-	GDT_SEGMENT ( base, limit, type, \
-		      GDT_SFLAG_NORMAL, 0, GDT_AVL_NORMAL, \
-		      size, granularity )
-
-/* Protected mode code segment */
-#define GDT_SEGMENT_PMCS(base) GDT_SEGMENT_NORMAL ( \
-	base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \
-	GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
-#define GDT_SEGMENT_PMCS_PHYS GDT_SEGMENT_PMCS(0)
-/* Protected mode data segment */
-#define GDT_SEGMENT_PMDS(base) GDT_SEGMENT_NORMAL ( \
-	base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
-	GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
-#define GDT_SEGMENT_PMDS_PHYS GDT_SEGMENT_PMDS(0)
-/* Real mode code segment */
-/* Not sure if there's any reason to use GDT_TYPE_EXEC_ONLY_CODE
- * instead of just GDT_TYPE_CODE, but that's what our old GDT did and
- * it worked, so I'm not changing it.
- */
-#define GDT_SEGMENT_RMCS(base) GDT_SEGMENT_NORMAL ( \
-	base, 0xffff, GDT_TYPE_EXEC_ONLY_CODE | GDT_TYPE_CONFORMING, \
-	GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL )
-/* Real mode data segment */
-#define GDT_SEGMENT_RMDS(base) GDT_SEGMENT_NORMAL ( \
-	base, 0xffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
-	GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL )
-/* Long mode code segment */
-#define GDT_SEGMENT_LMCS(base) GDT_SEGMENT_NORMAL ( \
-	base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \
-	GDT_SIZE_64BIT, GDT_GRANULARITY_LARGE )
-#define GDT_SEGMENT_LMCS_PHYS GDT_SEGMENT_LMCS(0)
-/* Long mode data segment */
-/* AFIACT, GDT_SIZE_64BIT applies only to code segments */
-#define GDT_SEGMENT_LMDS(base) GDT_SEGMENT_NORMAL ( \
-	base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
-	GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
-#define GDT_SEGMENT_LMDS_PHYS GDT_SEGMENT_LMDS(0)
-
-/* Template for creating GDT structures (including segment register
- * lists), suitable for passing as parameters to external_call().
- */
-#define GDT_STRUCT_t(num_segments) \
-	struct { \
-		gdt_descriptor_t descriptor; \
-		gdt_segment_t segments[num_segments]; \
-	} PACKED
-/* And utility function for filling it in */
-#define GDT_ADJUST(structure) { \
-	(structure)->descriptor.address = \
-		virt_to_phys(&((structure)->descriptor.limit)); \
-	(structure)->descriptor.limit = \
-		sizeof((structure)->segments) + 8 - 1; \
-	(structure)->descriptor.padding = 0; \
-}
-
-/* Data passed in to in_call() by assembly wrapper.
- */
-typedef struct {
-	regs_t		regs;
-	seg_regs_t	seg_regs;
-	gdt_descriptor_t gdt_desc;
-	uint32_t	flags;
-	struct {
-		uint32_t offset;
-		uint32_t segment;
-	} ret_addr;
-} PACKED i386_pm_in_call_data_t;
-
-typedef struct {
-	seg_regs_t	seg_regs;
-	union {
-		uint16_t	pad;
-		uint16_t	prefix_sp;
-	};
-	uint16_t	flags;
-	struct {
-		uint16_t offset;
-		uint16_t segment;
-	} ret_addr;
-	uint32_t orig_opcode;
-} PACKED i386_rm_in_call_data_t;
-
-typedef struct {
-	i386_pm_in_call_data_t *pm;
-	i386_rm_in_call_data_t *rm;
-} i386_in_call_data_t;
-#define in_call_data_t i386_in_call_data_t
-
-/* Function prototypes
- */
-extern int install_rm_callback_interface ( void *address, size_t available );
-
-#endif /* ASSEMBLY */
-
-#define RM_IN_CALL	(0)
-#define RM_IN_CALL_FAR	(2)
-
-#endif /* CALLBACKS_ARCH_H */
diff --git a/gpxe/src/arch/i386/include/comboot.h b/gpxe/src/arch/i386/include/comboot.h
index 56661a8..1232f0a 100644
--- a/gpxe/src/arch/i386/include/comboot.h
+++ b/gpxe/src/arch/i386/include/comboot.h
@@ -7,6 +7,8 @@
  * SYSLINUX COMBOOT
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <setjmp.h>
 #include <gpxe/in.h>
diff --git a/gpxe/src/arch/i386/include/fakee820.h b/gpxe/src/arch/i386/include/fakee820.h
index f1fe8af..9d00fb6 100644
--- a/gpxe/src/arch/i386/include/fakee820.h
+++ b/gpxe/src/arch/i386/include/fakee820.h
@@ -1,6 +1,8 @@
 #ifndef _FAKEE820_H
 #define _FAKEE820_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern void fake_e820 ( void );
 extern void unfake_e820 ( void );
 
diff --git a/gpxe/src/arch/i386/include/gpxe/abft.h b/gpxe/src/arch/i386/include/gpxe/abft.h
index 1c651ef..9065e61 100644
--- a/gpxe/src/arch/i386/include/gpxe/abft.h
+++ b/gpxe/src/arch/i386/include/gpxe/abft.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/acpi.h>
 #include <gpxe/if_ether.h>
diff --git a/gpxe/src/arch/i386/include/gpxe/bios_nap.h b/gpxe/src/arch/i386/include/gpxe/bios_nap.h
index f1c721e..c32429b 100644
--- a/gpxe/src/arch/i386/include/gpxe/bios_nap.h
+++ b/gpxe/src/arch/i386/include/gpxe/bios_nap.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef NAP_PCBIOS
 #define NAP_PREFIX_pcbios
 #else
diff --git a/gpxe/src/arch/i386/include/gpxe/bios_smbios.h b/gpxe/src/arch/i386/include/gpxe/bios_smbios.h
index 0a6f277..83726b1 100644
--- a/gpxe/src/arch/i386/include/gpxe/bios_smbios.h
+++ b/gpxe/src/arch/i386/include/gpxe/bios_smbios.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef SMBIOS_PCBIOS
 #define SMBIOS_PREFIX_pcbios
 #else
diff --git a/gpxe/src/arch/i386/include/gpxe/bios_timer.h b/gpxe/src/arch/i386/include/gpxe/bios_timer.h
index 7e3caa3..ed9df52 100644
--- a/gpxe/src/arch/i386/include/gpxe/bios_timer.h
+++ b/gpxe/src/arch/i386/include/gpxe/bios_timer.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef TIMER_PCBIOS
 #define TIMER_PREFIX_pcbios
 #else
diff --git a/gpxe/src/arch/i386/include/gpxe/ibft.h b/gpxe/src/arch/i386/include/gpxe/ibft.h
index 5eef547..c41e2e4 100644
--- a/gpxe/src/arch/i386/include/gpxe/ibft.h
+++ b/gpxe/src/arch/i386/include/gpxe/ibft.h
@@ -28,6 +28,8 @@
  *
  */
 
+FILE_LICENCE ( BSD2 );
+
 /** @file
  *
  * iSCSI boot firmware table
diff --git a/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h b/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h
index a3cd2c0..eaf7025 100644
--- a/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h
+++ b/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef UMALLOC_MEMTOP
 #define UMALLOC_PREFIX_memtop
 #else
diff --git a/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h b/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h
index 0e03d70..67ba22f 100644
--- a/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h
+++ b/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef TIMER_RDTSC
 #define TIMER_PREFIX_rdtsc
 #else
diff --git a/gpxe/src/arch/i386/include/gpxe/sbft.h b/gpxe/src/arch/i386/include/gpxe/sbft.h
new file mode 100644
index 0000000..3003843
--- /dev/null
+++ b/gpxe/src/arch/i386/include/gpxe/sbft.h
@@ -0,0 +1,125 @@
+#ifndef _GPXE_SBFT_H
+#define _GPXE_SBFT_H
+
+/*
+ * Copyright (C) 2009 Fen Systems Ltd <mbrown at fensystems.co.uk>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+FILE_LICENCE ( BSD2 );
+
+/** @file
+ *
+ * SRP boot firmware table
+ *
+ * The working draft specification for the SRP boot firmware table can
+ * be found at
+ *
+ *   http://etherboot.org/wiki/srp/sbft
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/acpi.h>
+#include <gpxe/scsi.h>
+#include <gpxe/srp.h>
+#include <gpxe/ib_srp.h>
+
+/** SRP Boot Firmware Table signature */
+#define SBFT_SIG "sBFT"
+
+/** An offset from the start of the sBFT */
+typedef uint16_t sbft_off_t;
+
+/**
+ * SRP Boot Firmware Table
+ */
+struct sbft_table {
+	/** ACPI header */
+	struct acpi_description_header acpi;
+	/** Offset to SCSI subtable */
+	sbft_off_t scsi_offset;
+	/** Offset to SRP subtable */
+	sbft_off_t srp_offset;
+	/** Offset to IB subtable, if present */
+	sbft_off_t ib_offset;
+	/** Reserved */
+	uint8_t reserved[6];
+} __attribute__ (( packed ));
+
+/**
+ * sBFT SCSI subtable
+ */
+struct sbft_scsi_subtable {
+	/** LUN */
+	struct scsi_lun lun;
+} __attribute__ (( packed ));
+
+/**
+ * sBFT SRP subtable
+ */
+struct sbft_srp_subtable {
+	/** Initiator and target ports */
+	struct srp_port_ids port_ids;
+} __attribute__ (( packed ));
+
+/**
+ * sBFT IB subtable
+ */
+struct sbft_ib_subtable {
+	/** Source GID */
+	struct ib_gid sgid;
+	/** Destination GID */
+	struct ib_gid dgid;
+	/** Service ID */
+	struct ib_gid_half service_id;
+	/** Partition key */
+	uint16_t pkey;
+	/** Reserved */
+	uint8_t reserved[6];
+} __attribute__ (( packed ));
+
+/**
+ * An sBFT created by gPXE
+ */
+struct gpxe_sbft {
+	/** The table header */
+	struct sbft_table table;
+	/** The SCSI subtable */
+	struct sbft_scsi_subtable scsi;
+	/** The SRP subtable */
+	struct sbft_srp_subtable srp;
+	/** The IB subtable */
+	struct sbft_ib_subtable ib;
+} __attribute__ (( packed, aligned ( 16 ) ));
+
+struct srp_device;
+
+extern int sbft_fill_data ( struct srp_device *srp );
+
+#endif /* _GPXE_SBFT_H */
diff --git a/gpxe/src/arch/i386/include/gpxe/timer2.h b/gpxe/src/arch/i386/include/gpxe/timer2.h
index 59705fa..8f11951 100644
--- a/gpxe/src/arch/i386/include/gpxe/timer2.h
+++ b/gpxe/src/arch/i386/include/gpxe/timer2.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern void timer2_udelay ( unsigned long usecs );
 
 #endif /* _GPXE_TIMER2_H */
diff --git a/gpxe/src/arch/i386/include/gpxe/x86_io.h b/gpxe/src/arch/i386/include/gpxe/x86_io.h
index b1ae3ba..beb5b22 100644
--- a/gpxe/src/arch/i386/include/gpxe/x86_io.h
+++ b/gpxe/src/arch/i386/include/gpxe/x86_io.h
@@ -15,6 +15,8 @@
  * into a machine with such an old CPU anyway.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef IOAPI_X86
 #define IOAPI_PREFIX_x86
 #else
diff --git a/gpxe/src/arch/i386/include/int13.h b/gpxe/src/arch/i386/include/int13.h
index bf6d031..e1884d9 100644
--- a/gpxe/src/arch/i386/include/int13.h
+++ b/gpxe/src/arch/i386/include/int13.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/list.h>
 #include <realmode.h>
diff --git a/gpxe/src/arch/i386/include/librm.h b/gpxe/src/arch/i386/include/librm.h
old mode 100755
new mode 100644
index 9eb2767..f193f5e
--- a/gpxe/src/arch/i386/include/librm.h
+++ b/gpxe/src/arch/i386/include/librm.h
@@ -1,6 +1,8 @@
 #ifndef LIBRM_H
 #define LIBRM_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* Segment selectors as used in our protected-mode GDTs.
  *
  * Don't change these unless you really know what you're doing.
diff --git a/gpxe/src/arch/i386/include/limits.h b/gpxe/src/arch/i386/include/limits.h
index f13db26..031b6c5 100644
--- a/gpxe/src/arch/i386/include/limits.h
+++ b/gpxe/src/arch/i386/include/limits.h
@@ -1,6 +1,8 @@
 #ifndef LIMITS_H
 #define LIMITS_H	1
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* Number of bits in a `char' */
 #define CHAR_BIT	8
 
diff --git a/gpxe/src/arch/i386/include/memsizes.h b/gpxe/src/arch/i386/include/memsizes.h
index 6222fd6..7b21749 100644
--- a/gpxe/src/arch/i386/include/memsizes.h
+++ b/gpxe/src/arch/i386/include/memsizes.h
@@ -1,6 +1,8 @@
 #ifndef _MEMSIZES_H
 #define _MEMSIZES_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <basemem.h>
 
 /**
diff --git a/gpxe/src/arch/i386/include/multiboot.h b/gpxe/src/arch/i386/include/multiboot.h
index 4ca7089..44614c7 100644
--- a/gpxe/src/arch/i386/include/multiboot.h
+++ b/gpxe/src/arch/i386/include/multiboot.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /** The magic number for the Multiboot header */
diff --git a/gpxe/src/arch/i386/include/pic8259.h b/gpxe/src/arch/i386/include/pic8259.h
index 0c501a9..f8e20c4 100644
--- a/gpxe/src/arch/i386/include/pic8259.h
+++ b/gpxe/src/arch/i386/include/pic8259.h
@@ -4,6 +4,8 @@
  * Initially written by Michael Brown (mcb30).
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef PIC8259_H
 #define PIC8259_H
 
diff --git a/gpxe/src/arch/i386/include/pnpbios.h b/gpxe/src/arch/i386/include/pnpbios.h
index ab31c69..4c20e73 100644
--- a/gpxe/src/arch/i386/include/pnpbios.h
+++ b/gpxe/src/arch/i386/include/pnpbios.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* BIOS segment address */
 #define BIOS_SEG 0xf000
 
diff --git a/gpxe/src/arch/i386/include/pxe.h b/gpxe/src/arch/i386/include/pxe.h
index 6d332ac..041ee7b 100644
--- a/gpxe/src/arch/i386/include/pxe.h
+++ b/gpxe/src/arch/i386/include/pxe.h
@@ -1,6 +1,8 @@
 #ifndef PXE_H
 #define PXE_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "pxe_types.h"
 #include "pxe_api.h"
 #include <gpxe/device.h>
@@ -65,6 +67,7 @@ union u_PXENV_ANY {
 	struct s_PXENV_GET_FILE_SIZE		get_file_size;
 	struct s_PXENV_FILE_EXEC		file_exec;
 	struct s_PXENV_FILE_API_CHECK		file_api_check;
+	struct s_PXENV_FILE_EXIT_HOOK		file_exit_hook;
 };
 
 typedef union u_PXENV_ANY PXENV_ANY_t;
diff --git a/gpxe/src/arch/i386/include/pxe_api.h b/gpxe/src/arch/i386/include/pxe_api.h
index b3d4bca..92f046f 100644
--- a/gpxe/src/arch/i386/include/pxe_api.h
+++ b/gpxe/src/arch/i386/include/pxe_api.h
@@ -15,6 +15,31 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * As an alternative, at your option, you may use this file under the
+ * following terms, known as the "MIT license":
+ *
+ * Copyright (c) 2005-2009 Michael Brown <mbrown at fensystems.co.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
  */
 
 /** @file
@@ -23,6 +48,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "pxe_types.h"
 
 /** @addtogroup pxe Preboot eXecution Environment (PXE) API
@@ -1415,6 +1442,25 @@ extern PXENV_EXIT_t pxenv_undi_get_nic_type (
 /** PXE API function code for pxenv_undi_get_iface_info() */
 #define	PXENV_UNDI_GET_IFACE_INFO	0x0013
 
+/** Broadcast supported */
+#define SUPPORTED_BROADCAST		0x0001
+/** Multicast supported */
+#define SUPPORTED_MULTICAST		0x0002
+/** Functional/group addressing supported */
+#define SUPPORTED_GROUP			0x0004
+/** Promiscuous mode supported */
+#define SUPPORTED_PROMISCUOUS		0x0008
+/** Software settable station address */
+#define SUPPORTED_SET_STATION_ADDRESS	0x0010
+/** InitiateDiagnostics supported */
+#define SUPPORTED_DIAGNOSTICS		0x0040
+/** Reset MAC supported */
+#define SUPPORTED_RESET			0x0400
+/** Open / Close Adapter supported */
+#define SUPPORTED_OPEN_CLOSE		0x0800
+/** Interrupt Request supported */
+#define SUPPORTED_IRQ			0x1000
+
 /** Parameter block for pxenv_undi_get_iface_info() */
 struct s_PXENV_UNDI_GET_IFACE_INFO {
 	PXENV_STATUS_t	Status;		/**< PXE status code */
@@ -1732,6 +1778,28 @@ extern PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_a
 
 /** @} */ /* pxenv_file_api_check */
 
+/** @defgroup pxenv_file_exit_hook PXENV_FILE_EXIT_HOOK
+ *
+ * FILE EXIT HOOK
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_exit_hook() */
+#define PXENV_FILE_EXIT_HOOK			0x00e7
+
+/** Parameter block for pxenv_file_exit_hook() */
+struct s_PXENV_FILE_EXIT_HOOK {
+	PXENV_STATUS_t Status;		/**< PXE status code */
+	SEGOFF16_t Hook;		/**< SEG16:OFF16 to jump to */
+} PACKED;
+
+typedef struct s_PXENV_FILE_EXIT_HOOK PXENV_FILE_EXIT_HOOK_t;
+
+extern PXENV_EXIT_t pxenv_file_exit_hook ( struct s_PXENV_FILE_EXIT_HOOK *file_exit_hook );
+
+/** @} */ /* pxenv_file_exit_hook */
+
 /** @} */ /* pxe_file_api */
 
 /** @defgroup pxe_loader_api PXE Loader API
diff --git a/gpxe/src/arch/i386/include/pxe_call.h b/gpxe/src/arch/i386/include/pxe_call.h
index 2f3ea15..4d24561 100644
--- a/gpxe/src/arch/i386/include/pxe_call.h
+++ b/gpxe/src/arch/i386/include/pxe_call.h
@@ -6,9 +6,13 @@
  * PXE API entry point
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <pxe_api.h>
 #include <realmode.h>
 
+struct net_device;
+
 /** PXE load address segment */
 #define PXE_LOAD_SEGMENT 0
 
@@ -26,10 +30,28 @@ extern struct s_PXE __text16 ( ppxe );
 extern struct s_PXENV __text16 ( pxenv );
 #define pxenv __use_text16 ( pxenv )
 
-extern void pxe_hook_int1a ( void );
-extern int pxe_unhook_int1a ( void );
-extern void pxe_init_structures ( void );
+extern void pxe_activate ( struct net_device *netdev );
+extern int pxe_deactivate ( void );
 extern int pxe_start_nbp ( void );
 extern __asmcall void pxe_api_call ( struct i386_all_regs *ix86 );
+extern int _pxe_api_call_weak ( struct i386_all_regs *ix86 )
+	__attribute__ (( weak ));
+
+/**
+ * Dispatch PXE API call weakly
+ *
+ * @v ix86		Registers for PXE call
+ * @ret present		Zero if the PXE stack is present, nonzero if not
+ *
+ * A successful return only indicates that the PXE stack was available
+ * for dispatching the call; it says nothing about the success of
+ * whatever the call asked for.
+ */
+static inline int pxe_api_call_weak ( struct i386_all_regs *ix86 )
+{
+	if ( _pxe_api_call_weak != NULL )
+		return _pxe_api_call_weak ( ix86 );
+	return -1;
+}
 
 #endif /* _PXE_CALL_H */
diff --git a/gpxe/src/arch/i386/include/pxe_types.h b/gpxe/src/arch/i386/include/pxe_types.h
index dd9092e..a6516d2 100644
--- a/gpxe/src/arch/i386/include/pxe_types.h
+++ b/gpxe/src/arch/i386/include/pxe_types.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <errno.h> /* PXE status codes */
 
diff --git a/gpxe/src/arch/i386/include/pxeparent.h b/gpxe/src/arch/i386/include/pxeparent.h
new file mode 100644
index 0000000..b31e24a
--- /dev/null
+++ b/gpxe/src/arch/i386/include/pxeparent.h
@@ -0,0 +1,11 @@
+#ifndef PXEPARENT_H
+#define PXEPARENT_H
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <pxe_types.h>
+
+extern int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
+			    void *params, size_t params_len );
+
+#endif
diff --git a/gpxe/src/arch/i386/include/realmode.h b/gpxe/src/arch/i386/include/realmode.h
index 26e6dd7..a0a830b 100644
--- a/gpxe/src/arch/i386/include/realmode.h
+++ b/gpxe/src/arch/i386/include/realmode.h
@@ -10,6 +10,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /*
  * Declaration of variables in .data16
  *
diff --git a/gpxe/src/arch/i386/include/registers.h b/gpxe/src/arch/i386/include/registers.h
index e68fa85..2839e2b 100644
--- a/gpxe/src/arch/i386/include/registers.h
+++ b/gpxe/src/arch/i386/include/registers.h
@@ -10,6 +10,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /**
diff --git a/gpxe/src/arch/i386/include/setjmp.h b/gpxe/src/arch/i386/include/setjmp.h
index c18d03e..5d3c11b 100644
--- a/gpxe/src/arch/i386/include/setjmp.h
+++ b/gpxe/src/arch/i386/include/setjmp.h
@@ -1,6 +1,8 @@
 #ifndef ETHERBOOT_SETJMP_H
 #define ETHERBOOT_SETJMP_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <realmode.h>
 
diff --git a/gpxe/src/arch/i386/include/undi.h b/gpxe/src/arch/i386/include/undi.h
index c6253d0..de6925b 100644
--- a/gpxe/src/arch/i386/include/undi.h
+++ b/gpxe/src/arch/i386/include/undi.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef ASSEMBLY
 
 #include <gpxe/device.h>
diff --git a/gpxe/src/arch/i386/include/undiload.h b/gpxe/src/arch/i386/include/undiload.h
index bfc1187..426830e 100644
--- a/gpxe/src/arch/i386/include/undiload.h
+++ b/gpxe/src/arch/i386/include/undiload.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct undi_device;
 struct undi_rom;
 
diff --git a/gpxe/src/arch/i386/include/undinet.h b/gpxe/src/arch/i386/include/undinet.h
index 1a4a385..c3c17c1 100644
--- a/gpxe/src/arch/i386/include/undinet.h
+++ b/gpxe/src/arch/i386/include/undinet.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct undi_device;
 
 extern int undinet_probe ( struct undi_device *undi );
diff --git a/gpxe/src/arch/i386/include/undipreload.h b/gpxe/src/arch/i386/include/undipreload.h
index d9bc8cb..de9b8fb 100644
--- a/gpxe/src/arch/i386/include/undipreload.h
+++ b/gpxe/src/arch/i386/include/undipreload.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <realmode.h>
 #include <undi.h>
 
diff --git a/gpxe/src/arch/i386/include/undirom.h b/gpxe/src/arch/i386/include/undirom.h
index a263600..86d7077 100644
--- a/gpxe/src/arch/i386/include/undirom.h
+++ b/gpxe/src/arch/i386/include/undirom.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <pxe_types.h>
 
 /** An UNDI PCI device ID */
diff --git a/gpxe/src/arch/i386/interface/pcbios/abft.c b/gpxe/src/arch/i386/interface/pcbios/abft.c
index af28bbc..8694172 100644
--- a/gpxe/src/arch/i386/interface/pcbios/abft.c
+++ b/gpxe/src/arch/i386/interface/pcbios/abft.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <realmode.h>
 #include <gpxe/aoe.h>
 #include <gpxe/netdevice.h>
diff --git a/gpxe/src/arch/i386/interface/pcbios/aoeboot.c b/gpxe/src/arch/i386/interface/pcbios/aoeboot.c
index 6e1e51c..2670b15 100644
--- a/gpxe/src/arch/i386/interface/pcbios/aoeboot.c
+++ b/gpxe/src/arch/i386/interface/pcbios/aoeboot.c
@@ -1,58 +1,74 @@
 #include <stdint.h>
 #include <string.h>
+#include <stdlib.h>
 #include <stdio.h>
-#include <byteswap.h>
+#include <errno.h>
 #include <gpxe/aoe.h>
 #include <gpxe/ata.h>
 #include <gpxe/netdevice.h>
-#include <gpxe/settings.h>
 #include <gpxe/sanboot.h>
 #include <gpxe/abft.h>
 #include <int13.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static int aoeboot ( const char *root_path ) {
-	struct ata_device ata;
-	struct int13_drive drive;
+	struct ata_device *ata;
+	struct int13_drive *drive;
 	int rc;
 
-	memset ( &ata, 0, sizeof ( ata ) );
-	memset ( &drive, 0, sizeof ( drive ) );
-
-	printf ( "AoE booting from %s\n", root_path );
+	ata = zalloc ( sizeof ( *ata ) );
+	if ( ! ata ) {
+		rc = -ENOMEM;
+		goto err_alloc_ata;
+	}
+	drive = zalloc ( sizeof ( *drive ) );
+	if ( ! drive ) {
+		rc = -ENOMEM;
+		goto err_alloc_drive;
+	}
 
 	/* FIXME: ugly, ugly hack */
 	struct net_device *netdev = last_opened_netdev();
 
-	if ( ( rc = aoe_attach ( &ata, netdev, root_path ) ) != 0 ) {
+	if ( ( rc = aoe_attach ( ata, netdev, root_path ) ) != 0 ) {
 		printf ( "Could not attach AoE device: %s\n",
 			 strerror ( rc ) );
-		goto error_attach;
+		goto err_attach;
 	}
-	if ( ( rc = init_atadev ( &ata ) ) != 0 ) {
+	if ( ( rc = init_atadev ( ata ) ) != 0 ) {
 		printf ( "Could not initialise AoE device: %s\n",
 			 strerror ( rc ) );
-		goto error_init;
+		goto err_init;
 	}
 
 	/* FIXME: ugly, ugly hack */
 	struct aoe_session *aoe =
-		container_of ( ata.backend, struct aoe_session, refcnt );
+		container_of ( ata->backend, struct aoe_session, refcnt );
 	abft_fill_data ( aoe );
 
-	drive.blockdev = &ata.blockdev;
+	drive->blockdev = &ata->blockdev;
 
-	register_int13_drive ( &drive );
-	printf ( "Registered as BIOS drive %#02x\n", drive.drive );
-	printf ( "Booting from BIOS drive %#02x\n", drive.drive );
-	rc = int13_boot ( drive.drive );
+	register_int13_drive ( drive );
+	printf ( "Registered as BIOS drive %#02x\n", drive->drive );
+	printf ( "Booting from BIOS drive %#02x\n", drive->drive );
+	rc = int13_boot ( drive->drive );
 	printf ( "Boot failed\n" );
 
-	printf ( "Unregistering BIOS drive %#02x\n", drive.drive );
-	unregister_int13_drive ( &drive );
+	/* Leave drive registered, if instructed to do so */
+	if ( keep_san() )
+		return rc;
+
+	printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
+	unregister_int13_drive ( drive );
 
- error_init:
-	aoe_detach ( &ata );
- error_attach:
+ err_init:
+	aoe_detach ( ata );
+ err_attach:
+	free ( drive );
+ err_alloc_drive:
+	free ( ata );
+ err_alloc_ata:
 	return rc;
 }
 
diff --git a/gpxe/src/arch/i386/interface/pcbios/bios_nap.c b/gpxe/src/arch/i386/interface/pcbios/bios_nap.c
index 2f4a051..e38cac7 100644
--- a/gpxe/src/arch/i386/interface/pcbios/bios_nap.c
+++ b/gpxe/src/arch/i386/interface/pcbios/bios_nap.c
@@ -1,6 +1,8 @@
 #include <gpxe/nap.h>
 #include <realmode.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * Save power by halting the CPU until the next interrupt
  *
diff --git a/gpxe/src/arch/i386/interface/pcbios/bios_smbios.c b/gpxe/src/arch/i386/interface/pcbios/bios_smbios.c
index efaaef0..094214b 100644
--- a/gpxe/src/arch/i386/interface/pcbios/bios_smbios.c
+++ b/gpxe/src/arch/i386/interface/pcbios/bios_smbios.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
diff --git a/gpxe/src/arch/i386/interface/pcbios/bios_timer.c b/gpxe/src/arch/i386/interface/pcbios/bios_timer.c
index 0b475ea..8ecf7c1 100644
--- a/gpxe/src/arch/i386/interface/pcbios/bios_timer.c
+++ b/gpxe/src/arch/i386/interface/pcbios/bios_timer.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** @file
  *
  * BIOS timer
diff --git a/gpxe/src/arch/i386/interface/pcbios/biosint.c b/gpxe/src/arch/i386/interface/pcbios/biosint.c
index 1306f91..a193def 100644
--- a/gpxe/src/arch/i386/interface/pcbios/biosint.c
+++ b/gpxe/src/arch/i386/interface/pcbios/biosint.c
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * Hook INT vector
  *
diff --git a/gpxe/src/arch/i386/interface/pcbios/ib_srpboot.c b/gpxe/src/arch/i386/interface/pcbios/ib_srpboot.c
new file mode 100644
index 0000000..b1cbc33
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pcbios/ib_srpboot.c
@@ -0,0 +1,73 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/sanboot.h>
+#include <int13.h>
+#include <gpxe/srp.h>
+#include <gpxe/sbft.h>
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+static int ib_srpboot ( const char *root_path ) {
+	struct scsi_device *scsi;
+	struct int13_drive *drive;
+	int rc;
+
+	scsi = zalloc ( sizeof ( *scsi ) );
+	if ( ! scsi ) {
+		rc = -ENOMEM;
+		goto err_alloc_scsi;
+	}
+	drive = zalloc ( sizeof ( *drive ) );
+	if ( ! drive ) {
+		rc = -ENOMEM;
+		goto err_alloc_drive;
+	}
+
+	if ( ( rc = srp_attach ( scsi, root_path ) ) != 0 ) {
+		printf ( "Could not attach IB_SRP device: %s\n",
+			 strerror ( rc ) );
+		goto err_attach;
+	}
+	if ( ( rc = init_scsidev ( scsi ) ) != 0 ) {
+		printf ( "Could not initialise IB_SRP device: %s\n",
+			 strerror ( rc ) );
+		goto err_init;
+	}
+
+	drive->blockdev = &scsi->blockdev;
+
+	/* FIXME: ugly, ugly hack */
+	struct srp_device *srp =
+		container_of ( scsi->backend, struct srp_device, refcnt );
+	sbft_fill_data ( srp );
+
+	register_int13_drive ( drive );
+	printf ( "Registered as BIOS drive %#02x\n", drive->drive );
+	printf ( "Booting from BIOS drive %#02x\n", drive->drive );
+	rc = int13_boot ( drive->drive );
+	printf ( "Boot failed\n" );
+
+	/* Leave drive registered, if instructed to do so */
+	if ( keep_san() )
+		return rc;
+
+	printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
+	unregister_int13_drive ( drive );
+
+ err_init:
+	srp_detach ( scsi );
+ err_attach:
+	free ( drive );
+ err_alloc_drive:
+	free ( scsi );
+ err_alloc_scsi:
+	return rc;
+}
+
+struct sanboot_protocol ib_srp_sanboot_protocol __sanboot_protocol = {
+	.prefix = "ib_srp:",
+	.boot = ib_srpboot,
+};
diff --git a/gpxe/src/arch/i386/interface/pcbios/ibft.c b/gpxe/src/arch/i386/interface/pcbios/ibft.c
index 43d1f85..adf8e6b 100644
--- a/gpxe/src/arch/i386/interface/pcbios/ibft.c
+++ b/gpxe/src/arch/i386/interface/pcbios/ibft.c
@@ -25,6 +25,8 @@
  *
  */
 
+FILE_LICENCE ( BSD2 );
+
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -35,6 +37,7 @@
 #include <gpxe/acpi.h>
 #include <gpxe/in.h>
 #include <gpxe/netdevice.h>
+#include <gpxe/ethernet.h>
 #include <gpxe/dhcp.h>
 #include <gpxe/iscsi.h>
 #include <gpxe/ibft.h>
@@ -234,7 +237,8 @@ static int ibft_set_string_option ( struct ibft_string_block *strings,
  */
 static const char * ibft_string ( struct ibft_string_block *strings,
 				  struct ibft_string *string ) {
-	return ( ( ( char * ) strings->table ) + string->offset );
+	return ( string->offset ?
+		 ( ( ( char * ) strings->table ) + string->offset ) : NULL );
 }
 
 /**
@@ -248,6 +252,7 @@ static const char * ibft_string ( struct ibft_string_block *strings,
 static int ibft_fill_nic ( struct ibft_nic *nic,
 			   struct ibft_string_block *strings,
 			   struct net_device *netdev ) {
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
 	struct in_addr netmask_addr = { 0 };
 	unsigned int netmask_count = 0;
 	int rc;
@@ -276,10 +281,12 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
 	DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix );
 
 	/* Extract values from net-device configuration */
-	memcpy ( nic->mac_address, netdev->ll_addr,
-		 sizeof ( nic->mac_address ) );
-	DBG ( "iBFT NIC MAC = %s\n",
-	      netdev->ll_protocol->ntoa ( nic->mac_address ) );
+	if ( ( rc = ll_protocol->eth_addr ( netdev->ll_addr,
+					    nic->mac_address ) ) != 0 ) {
+		DBG ( "Could not determine iBFT MAC: %s\n", strerror ( rc ) );
+		return rc;
+	}
+	DBG ( "iBFT NIC MAC = %s\n", eth_ntoa ( nic->mac_address ) );
 	nic->pci_bus_dev_func = netdev->dev->desc.location;
 	DBG ( "iBFT NIC PCI = %04x\n", nic->pci_bus_dev_func );
 
diff --git a/gpxe/src/arch/i386/interface/pcbios/int13.c b/gpxe/src/arch/i386/interface/pcbios/int13.c
index 2e9de5c..87b613a 100644
--- a/gpxe/src/arch/i386/interface/pcbios/int13.c
+++ b/gpxe/src/arch/i386/interface/pcbios/int13.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <limits.h>
 #include <byteswap.h>
@@ -50,6 +52,48 @@ extern void int13_wrapper ( void );
 static LIST_HEAD ( drives );
 
 /**
+ * Number of BIOS drives
+ *
+ * Note that this is the number of drives in the system as a whole
+ * (i.e. a mirror of the counter at 40:75), rather than a count of the
+ * number of emulated drives.
+ */
+static uint8_t num_drives;
+
+/**
+ * Update BIOS drive count
+ */
+static void int13_set_num_drives ( void ) {
+	struct int13_drive *drive;
+
+	/* Get current drive count */
+	get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+
+	/* Ensure count is large enough to cover all of our emulated drives */
+	list_for_each_entry ( drive, &drives, list ) {
+		if ( num_drives <= ( drive->drive & 0x7f ) )
+			num_drives = ( ( drive->drive & 0x7f ) + 1 );
+	}
+
+	/* Update current drive count */
+	put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+}
+
+/**
+ * Check number of drives
+ */
+static void int13_check_num_drives ( void ) {
+	uint8_t check_num_drives;
+
+	get_real ( check_num_drives, BDA_SEG, BDA_NUM_DRIVES );
+	if ( check_num_drives != num_drives ) {
+		int13_set_num_drives();
+		DBG ( "INT13 fixing up number of drives from %d to %d\n",
+		      check_num_drives, num_drives );
+	}
+}
+
+/**
  * INT 13, 00 - Reset disk system
  *
  * @v drive		Emulated drive
@@ -98,6 +142,7 @@ static int int13_rw_sectors ( struct int13_drive *drive,
 	unsigned long lba;
 	unsigned int count;
 	userptr_t buffer;
+	int rc;
 
 	/* Validate blocksize */
 	if ( blockdev->blksize != INT13_BLKSIZE ) {
@@ -122,8 +167,10 @@ static int int13_rw_sectors ( struct int13_drive *drive,
 	      head, sector, lba, ix86->segs.es, ix86->regs.bx, count );
 
 	/* Read from / write to block device */
-	if ( io ( blockdev, lba, count, buffer ) != 0 )
+	if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) {
+		DBG ( "INT 13 failed: %s\n", strerror ( rc ) );
 		return -INT13_STATUS_READ_ERROR;
+	}
 
 	return 0;
 }
@@ -202,9 +249,13 @@ static int int13_get_parameters ( struct int13_drive *drive,
  */
 static int int13_get_disk_type ( struct int13_drive *drive,
 				 struct i386_all_regs *ix86 ) {
+	uint32_t blocks;
+
 	DBG ( "Get disk type\n" );
-	ix86->regs.cx = ( drive->cylinders >> 16 );
-	ix86->regs.dx = ( drive->cylinders & 0xffff );
+	blocks = ( ( drive->blockdev->blocks <= 0xffffffffUL ) ?
+		   drive->blockdev->blocks : 0xffffffffUL );
+	ix86->regs.cx = ( blocks >> 16 );
+	ix86->regs.dx = ( blocks & 0xffff );
 	return INT13_DISK_TYPE_HDD;
 }
 
@@ -248,6 +299,7 @@ static int int13_extended_rw ( struct int13_drive *drive,
 	uint64_t lba;
 	unsigned long count;
 	userptr_t buffer;
+	int rc;
 
 	/* Read parameters from disk address structure */
 	copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
@@ -259,8 +311,10 @@ static int int13_extended_rw ( struct int13_drive *drive,
 	      addr.buffer.segment, addr.buffer.offset, count );
 	
 	/* Read from / write to block device */
-	if ( io ( blockdev, lba, count, buffer ) != 0 )
+	if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) {
+		DBG ( "INT 13 failed: %s\n", strerror ( rc ) );
 		return -INT13_STATUS_READ_ERROR;
+	}
 
 	return 0;
 }
@@ -328,6 +382,9 @@ static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
 	struct int13_drive *drive;
 	int status;
 
+	/* Check BIOS hasn't killed off our drive */
+	int13_check_num_drives();
+
 	list_for_each_entry ( drive, &drives, list ) {
 
 		if ( bios_drive != drive->drive ) {
@@ -387,7 +444,7 @@ static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
 		/* Negative status indicates an error */
 		if ( status < 0 ) {
 			status = -status;
-			DBG ( "INT13 failed with status %x\n", status );
+			DBG ( "INT 13 returning failure status %x\n", status );
 		} else {
 			ix86->flags &= ~CF;
 		}
@@ -543,7 +600,6 @@ void register_int13_drive ( struct int13_drive *drive ) {
 	/* Assign natural drive number */
 	get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
 	drive->natural_drive = ( num_drives | 0x80 );
-	num_drives++;
 
 	/* Assign drive number */
 	if ( ( drive->drive & 0xff ) == 0xff ) {
@@ -552,13 +608,8 @@ void register_int13_drive ( struct int13_drive *drive ) {
 	} else {
 		/* Use specified drive number (+0x80 if necessary) */
 		drive->drive |= 0x80;
-		if ( num_drives <= ( drive->drive & 0x7f ) )
-			num_drives = ( ( drive->drive & 0x7f ) + 1 );
 	}
 
-	/* Update BIOS drive count */
-	put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
-
 	DBG ( "Registered INT13 drive %02x (naturally %02x) with C/H/S "
 	      "geometry %d/%d/%d\n", drive->drive, drive->natural_drive,
 	      drive->cylinders, drive->heads, drive->sectors_per_track );
@@ -569,6 +620,9 @@ void register_int13_drive ( struct int13_drive *drive ) {
 
 	/* Add to list of emulated drives */
 	list_add ( &drive->list, &drives );
+
+	/* Update BIOS drive count */
+	int13_set_num_drives();
 }
 
 /**
@@ -652,7 +706,8 @@ int int13_boot ( unsigned int drive ) {
 
 	/* Jump to boot sector */
 	if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) {
-		DBG ( "INT 13 drive %02x boot returned\n", drive );
+		DBG ( "INT 13 drive %02x boot returned: %s\n",
+		      drive, strerror ( rc ) );
 		return rc;
 	}
 
diff --git a/gpxe/src/arch/i386/interface/pcbios/iscsiboot.c b/gpxe/src/arch/i386/interface/pcbios/iscsiboot.c
index 02aec4b..00efd8f 100644
--- a/gpxe/src/arch/i386/interface/pcbios/iscsiboot.c
+++ b/gpxe/src/arch/i386/interface/pcbios/iscsiboot.c
@@ -4,26 +4,16 @@
 #include <stdio.h>
 #include <errno.h>
 #include <gpxe/iscsi.h>
-#include <gpxe/settings.h>
-#include <gpxe/dhcp.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/ibft.h>
-#include <gpxe/init.h>
 #include <gpxe/sanboot.h>
 #include <int13.h>
-#include <usr/autoboot.h>
 
-struct setting keep_san_setting __setting = {
-	.name = "keep-san",
-	.description = "Preserve SAN connection",
-	.tag = DHCP_EB_KEEP_SAN,
-	.type = &setting_type_int8,
-};
+FILE_LICENCE ( GPL2_OR_LATER );
 
 static int iscsiboot ( const char *root_path ) {
 	struct scsi_device *scsi;
 	struct int13_drive *drive;
-	int keep_san;
 	int rc;
 
 	scsi = zalloc ( sizeof ( *scsi ) );
@@ -37,8 +27,6 @@ static int iscsiboot ( const char *root_path ) {
 		goto err_alloc_drive;
 	}
 
-	printf ( "iSCSI booting from %s\n", root_path );
-
 	if ( ( rc = iscsi_attach ( scsi, root_path ) ) != 0 ) {
 		printf ( "Could not attach iSCSI device: %s\n",
 			 strerror ( rc ) );
@@ -65,12 +53,8 @@ static int iscsiboot ( const char *root_path ) {
 	printf ( "Boot failed\n" );
 
 	/* Leave drive registered, if instructed to do so */
-	keep_san = fetch_intz_setting ( NULL, &keep_san_setting );
-	if ( keep_san ) {
-		printf ( "Preserving connection to SAN disk\n" );
-		shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES;
+	if ( keep_san() )
 		return rc;
-	}
 
 	printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
 	unregister_int13_drive ( drive );
diff --git a/gpxe/src/arch/i386/interface/pcbios/keepsan.c b/gpxe/src/arch/i386/interface/pcbios/keepsan.c
new file mode 100644
index 0000000..904e017
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pcbios/keepsan.c
@@ -0,0 +1,26 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <gpxe/settings.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/init.h>
+#include <gpxe/sanboot.h>
+#include <usr/autoboot.h>
+
+struct setting keep_san_setting __setting = {
+	.name = "keep-san",
+	.description = "Preserve SAN connection",
+	.tag = DHCP_EB_KEEP_SAN,
+	.type = &setting_type_int8,
+};
+
+int keep_san ( void ) {
+	int keep_san;
+
+	keep_san = fetch_intz_setting ( NULL, &keep_san_setting );
+	if ( ! keep_san )
+		return 0;
+
+	printf ( "Preserving connection to SAN disk\n" );
+	shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES;
+	return 1;
+}
diff --git a/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c b/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c
index 744d8e3..0645fe6 100644
--- a/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c
+++ b/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/arch/i386/interface/pcbios/pcibios.c b/gpxe/src/arch/i386/interface/pcbios/pcibios.c
index 81b4fd3..f2c3880 100644
--- a/gpxe/src/arch/i386/interface/pcbios/pcibios.c
+++ b/gpxe/src/arch/i386/interface/pcbios/pcibios.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/pci.h>
 #include <realmode.h>
diff --git a/gpxe/src/arch/i386/interface/pcbios/sbft.c b/gpxe/src/arch/i386/interface/pcbios/sbft.c
new file mode 100644
index 0000000..12927c7
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pcbios/sbft.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2009 Fen Systems Ltd <mbrown at fensystems.co.uk>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+FILE_LICENCE ( BSD2 );
+
+/** @file
+ *
+ * SRP boot firmware table
+ *
+ */
+
+#include <assert.h>
+#include <realmode.h>
+#include <gpxe/srp.h>
+#include <gpxe/ib_srp.h>
+#include <gpxe/acpi.h>
+#include <gpxe/sbft.h>
+
+#define sbftab __use_data16 ( sbftab )
+/** The sBFT used by gPXE */
+struct gpxe_sbft __data16 ( sbftab ) = {
+	/* Table header */
+	.table = {
+		/* ACPI header */
+		.acpi = {
+			.signature = SBFT_SIG,
+			.length = sizeof ( sbftab ),
+			.revision = 1,
+			.oem_id = "FENSYS",
+			.oem_table_id = "gPXE",
+		},
+		.scsi_offset = offsetof ( typeof ( sbftab ), scsi ),
+		.srp_offset = offsetof ( typeof ( sbftab ), srp ),
+		.ib_offset = offsetof ( typeof ( sbftab ), ib ),
+	},
+};
+
+/**
+ * Fill in all variable portions of sBFT
+ *
+ * @v srp		SRP device
+ * @ret rc		Return status code
+ */
+int sbft_fill_data ( struct srp_device *srp ) {
+	struct sbft_scsi_subtable *sbft_scsi = &sbftab.scsi;
+	struct sbft_srp_subtable *sbft_srp = &sbftab.srp;
+	struct sbft_ib_subtable *sbft_ib = &sbftab.ib;
+	struct ib_srp_parameters *ib_params;
+	struct segoff rm_sbftab = {
+		.segment = rm_ds,
+		.offset = __from_data16 ( &sbftab ),
+	};
+
+	/* Fill in the SCSI subtable */
+	memcpy ( &sbft_scsi->lun, &srp->lun, sizeof ( sbft_scsi->lun ) );
+
+	/* Fill in the SRP subtable */
+	memcpy ( &sbft_srp->port_ids, &srp->port_ids,
+		 sizeof ( sbft_srp->port_ids ) );
+
+	/* Fill in the IB subtable */
+	assert ( srp->transport == &ib_srp_transport );
+	ib_params = ib_srp_params ( srp );
+	memcpy ( &sbft_ib->sgid, &ib_params->sgid, sizeof ( sbft_ib->sgid ) );
+	memcpy ( &sbft_ib->dgid, &ib_params->dgid, sizeof ( sbft_ib->dgid ) );
+	memcpy ( &sbft_ib->service_id, &ib_params->service_id,
+		 sizeof ( sbft_ib->service_id ) );
+	sbft_ib->pkey = ib_params->pkey;
+
+	/* Update checksum */
+	acpi_fix_checksum ( &sbftab.table.acpi );
+
+	DBGC ( &sbftab, "SRP Boot Firmware Table at %04x:%04x:\n",
+	       rm_sbftab.segment, rm_sbftab.offset );
+	DBGC_HDA ( &sbftab, rm_sbftab, &sbftab, sizeof ( sbftab ) );
+
+	return 0;
+}
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_call.c b/gpxe/src/arch/i386/interface/pxe/pxe_call.c
index 06dee25..66a9b1e 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_call.c
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_call.c
@@ -16,7 +16,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/uaccess.h>
+#include <gpxe/init.h>
 #include <registers.h>
 #include <biosint.h>
 #include <pxe.h>
@@ -34,6 +37,9 @@ extern struct segoff __text16 ( pxe_int_1a_vector );
 /** INT 1A handler */
 extern void pxe_int_1a ( void );
 
+/** INT 1A hooked flag */
+static int int_1a_hooked = 0;
+
 /** A function pointer to hold any PXE API call
  *
  * Used by pxe_api_call() to avoid large swathes of duplicated code.
@@ -98,6 +104,7 @@ union pxenv_call {
 	PXENV_EXIT_t ( * get_file_size ) ( struct s_PXENV_GET_FILE_SIZE * );
 	PXENV_EXIT_t ( * file_exec ) ( struct s_PXENV_FILE_EXEC * );
 	PXENV_EXIT_t ( * file_api_check ) ( struct s_PXENV_FILE_API_CHECK * );
+	PXENV_EXIT_t ( * file_exit_hook ) ( struct s_PXENV_FILE_EXIT_HOOK * );
 };
 
 /**
@@ -304,6 +311,10 @@ __asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
 		pxenv_call.file_api_check = pxenv_file_api_check;
 		param_len = sizeof ( pxenv_any.file_api_check );
 		break;
+	case PXENV_FILE_EXIT_HOOK:
+		pxenv_call.file_exit_hook = pxenv_file_exit_hook;
+		param_len = sizeof ( pxenv_any.file_exit_hook );
+		break;
 	default:
 		DBG ( "PXENV_UNKNOWN_%hx", opcode );
 		pxenv_call.unknown = pxenv_unknown;
@@ -334,6 +345,18 @@ __asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
 }
 
 /**
+ * Dispatch weak PXE API call with PXE stack available
+ *
+ * @v ix86		Registers for PXE call
+ * @ret present		Zero (PXE stack present)
+ */
+int _pxe_api_call_weak ( struct i386_all_regs *ix86 )
+{
+	pxe_api_call ( ix86 );
+	return 0;
+}
+
+/**
  * Dispatch PXE loader call
  *
  * @v es:di		Address of PXE parameter block
@@ -362,25 +385,6 @@ __asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) {
 }
 
 /**
- * Hook INT 1A for PXE
- *
- */
-void pxe_hook_int1a ( void ) {
-	hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
-			      &pxe_int_1a_vector );
-}
-
-/**
- * Unhook INT 1A for PXE
- *
- * @ret rc		Return status code
- */
-int pxe_unhook_int1a ( void ) {
-	return unhook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
-				       &pxe_int_1a_vector );
-}
-
-/**
  * Calculate byte checksum as used by PXE
  *
  * @v data		Data
@@ -401,7 +405,7 @@ static uint8_t pxe_checksum ( void *data, size_t size ) {
  * Initialise !PXE and PXENV+ structures
  *
  */
-void pxe_init_structures ( void ) {
+static void pxe_init_structures ( void ) {
 	uint32_t rm_cs_phys = ( rm_cs << 4 );
 	uint32_t rm_ds_phys = ( rm_ds << 4 );
 
@@ -427,6 +431,55 @@ void pxe_init_structures ( void ) {
 	pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) );
 }
 
+/** PXE structure initialiser */
+struct init_fn pxe_init_fn __init_fn ( INIT_NORMAL ) = {
+	.initialise = pxe_init_structures,
+};
+
+/**
+ * Activate PXE stack
+ *
+ * @v netdev		Net device to use as PXE net device
+ */
+void pxe_activate ( struct net_device *netdev ) {
+
+	/* Ensure INT 1A is hooked */
+	if ( ! int_1a_hooked ) {
+		hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
+				      &pxe_int_1a_vector );
+		int_1a_hooked = 1;
+	}
+
+	/* Set PXE network device */
+	pxe_set_netdev ( netdev );
+}
+
+/**
+ * Deactivate PXE stack
+ *
+ * @ret rc		Return status code
+ */
+int pxe_deactivate ( void ) {
+	int rc;
+
+	/* Clear PXE network device */
+	pxe_set_netdev ( NULL );
+
+	/* Ensure INT 1A is unhooked, if possible */
+	if ( int_1a_hooked ) {
+		if ( ( rc = unhook_bios_interrupt ( 0x1a,
+						    (unsigned int) pxe_int_1a,
+						    &pxe_int_1a_vector ))!= 0){
+			DBG ( "Could not unhook INT 1A: %s\n",
+			      strerror ( rc ) );
+			return rc;
+		}
+		int_1a_hooked = 0;
+	}
+
+	return 0;
+}
+
 /**
  * Start PXE NBP at 0000:7c00
  *
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_entry.S b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S
index 0e8c8e2..0d3a57c 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_entry.S
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S
@@ -17,6 +17,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 	.arch i386
 
 /****************************************************************************
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_file.c b/gpxe/src/arch/i386/interface/pxe/pxe_file.c
index 4167458..8d83212 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_file.c
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_file.c
@@ -12,9 +12,12 @@
 #include <gpxe/posix_io.h>
 #include <gpxe/features.h>
 #include <pxe.h>
+#include <realmode.h>
 
 /*
  * Copyright (C) 2007 Michael Brown <mbrown at fensystems.co.uk>.
+ * Portions (C) 2010 Shao Miller <shao.miller at yrdsb.edu.on.ca>.
+ *              [PXE exit hook logic]
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -31,6 +34,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 );
 
 /**
@@ -228,6 +233,9 @@ PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
 	return PXENV_EXIT_SUCCESS;
 }
 
+segoff_t __data16 ( pxe_exit_hook ) = { 0, 0 };
+#define pxe_exit_hook __use_data16 ( pxe_exit_hook )
+
 /**
  * FILE API CHECK
  *
@@ -258,7 +266,41 @@ PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_chec
 		file_api_check->Magic    = 0xe9c17b20;
 		file_api_check->Provider = 0x45585067; /* "gPXE" */
 		file_api_check->APIMask  = 0x0000007f; /* Functions e0-e6 */
+		/* Check to see if we have a PXE exit hook */
+		if ( pxe_exit_hook.segment | pxe_exit_hook.offset )
+			/* Function e7, also */
+			file_api_check->APIMask |= 0x00000080;
 		file_api_check->Flags    = 0;	       /* None defined */
 		return PXENV_EXIT_SUCCESS;
 	}
 }
+
+/**
+ * FILE EXIT HOOK
+ *
+ * @v file_exit_hook			Pointer to a struct
+ *					s_PXENV_FILE_EXIT_HOOK
+ * @v s_PXENV_FILE_EXIT_HOOK::Hook	SEG16:OFF16 to jump to
+ * @ret #PXENV_EXIT_SUCCESS		Successfully set hook
+ * @ret #PXENV_EXIT_FAILURE		We're not an NBP build
+ * @ret s_PXENV_FILE_EXIT_HOOK::Status	PXE status code
+ *
+ */
+PXENV_EXIT_t pxenv_file_exit_hook ( struct s_PXENV_FILE_EXIT_HOOK
+					*file_exit_hook ) {
+	DBG ( "PXENV_FILE_EXIT_HOOK" );
+
+	/* Check to see if we have a PXE exit hook */
+	if ( pxe_exit_hook.segment | pxe_exit_hook.offset ) {
+		/* We'll jump to the specified SEG16:OFF16 during exit */
+		pxe_exit_hook.segment = file_exit_hook->Hook.segment;
+		pxe_exit_hook.offset = file_exit_hook->Hook.offset;
+		file_exit_hook->Status = PXENV_STATUS_SUCCESS;
+		return PXENV_EXIT_SUCCESS;
+	}
+
+	DBG ( " not NBP" );
+	file_exit_hook->Status = PXENV_STATUS_UNSUPPORTED;
+	return PXENV_EXIT_FAILURE;
+}
+
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_loader.c b/gpxe/src/arch/i386/interface/pxe/pxe_loader.c
index d228a36..b35caf7 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_loader.c
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_loader.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/init.h>
 #include "pxe.h"
 #include "pxe_call.h"
@@ -37,9 +39,6 @@ PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
 	DBG ( "[PXENV_UNDI_LOADER to CS %04x DS %04x]",
 	      undi_loader->UNDI_CS, undi_loader->UNDI_DS );
 
-	/* Set up PXE data structures */
-	pxe_init_structures();
-
 	/* Fill in UNDI loader structure */
 	undi_loader->PXEptr.segment = rm_cs;
 	undi_loader->PXEptr.offset = __from_text16 ( &ppxe );
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c b/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c
index 193abc3..3939c7b 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c
@@ -23,6 +23,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
@@ -35,6 +37,7 @@
 #include <gpxe/init.h>
 #include <gpxe/if_ether.h>
 #include <basemem_packet.h>
+#include <biosint.h>
 #include "pxe.h"
 #include "pxe_call.h"
 
@@ -294,11 +297,8 @@ PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
 	}
 	DBG ( " using netdev %s", netdev->name );
 
-	/* Save as PXE net device */
-	pxe_set_netdev ( netdev );
-
-	/* Hook INT 1A */
-	pxe_hook_int1a();
+	/* Activate PXE */
+	pxe_activate ( netdev );
 
 	start_undi->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
@@ -311,15 +311,20 @@ PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
 PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
 	DBG ( "PXENV_STOP_UNDI" );
 
-	/* Unhook INT 1A */
-	pxe_unhook_int1a();
-
-	/* Clear PXE net device */
-	pxe_set_netdev ( NULL );
+	/* Deactivate PXE */
+	pxe_deactivate();
 
 	/* Prepare for unload */
 	shutdown ( SHUTDOWN_BOOT );
 
+	/* Check to see if we still have any hooked interrupts */
+	if ( hooked_bios_interrupts != 0 ) {
+		DBG ( "PXENV_STOP_UNDI failed: %d interrupts still hooked\n",
+		      hooked_bios_interrupts );
+		stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
+		return PXENV_EXIT_FAILURE;
+	}
+
 	stop_undi->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
 }
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c b/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c
index 715a0b6..0e3ca3c 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c
@@ -22,6 +22,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
@@ -138,7 +140,7 @@ static void pxe_tftp_xfer_close ( struct xfer_interface *xfer __unused,
 
 static struct xfer_interface_operations pxe_tftp_xfer_ops = {
 	.close		= pxe_tftp_xfer_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= pxe_tftp_xfer_deliver_iob,
@@ -163,7 +165,8 @@ static struct xfer_interface_operations pxe_tftp_xfer_ops = {
  * @ret rc		Return status code
  */
 static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
-			   const unsigned char *filename, size_t blksize ) {
+			   const unsigned char *filename, size_t blksize,
+			   int sizeonly ) {
 	char uri_string[PXE_TFTP_URI_LEN];
 	struct in_addr address;
 	int rc;
@@ -183,7 +186,8 @@ static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
 	if ( blksize < TFTP_DEFAULT_BLKSIZE )
 		blksize = TFTP_DEFAULT_BLKSIZE;
 	snprintf ( uri_string, sizeof ( uri_string ),
-		   "tftp://%s:%d%s%s?blksize=%zd",
+		   "tftp%s://%s:%d%s%s?blksize=%zd",
+		   sizeonly ? "size" : "",
 		   inet_ntoa ( address ), ntohs ( port ),
 		   ( ( filename[0] == '/' ) ? "" : "/" ), filename, blksize );
 	DBG ( " %s", uri_string );
@@ -252,7 +256,8 @@ PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
 	if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress,
 				    tftp_open->TFTPPort,
 				    tftp_open->FileName,
-				    tftp_open->PacketSize ) ) != 0 ) {
+				    tftp_open->PacketSize,
+				    0) ) != 0 ) {
 		tftp_open->Status = PXENV_STATUS ( rc );
 		return PXENV_EXIT_FAILURE;
 	}
@@ -486,7 +491,7 @@ PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
 
 	/* Open TFTP file */
 	if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0,
-				    tftp_read_file->FileName, 0 ) ) != 0 ) {
+				    tftp_read_file->FileName, 0, 0 ) ) != 0 ) {
 		tftp_read_file->Status = PXENV_STATUS ( rc );
 		return PXENV_EXIT_FAILURE;
 	}
@@ -556,7 +561,7 @@ PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
 
 	/* Open TFTP file */
 	if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0,
-				    tftp_get_fsize->FileName, 0 ) ) != 0 ) {
+				    tftp_get_fsize->FileName, 0, 1 ) ) != 0 ) {
 		tftp_get_fsize->Status = PXENV_STATUS ( rc );
 		return PXENV_EXIT_FAILURE;
 	}
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_udp.c b/gpxe/src/arch/i386/interface/pxe/pxe_udp.c
index 033b1ad..f470220 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_udp.c
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_udp.c
@@ -30,6 +30,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** A PXE UDP connection */
 struct pxe_udp_connection {
 	/** Data transfer interface to UDP stack */
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_undi.c b/gpxe/src/arch/i386/interface/pxe/pxe_undi.c
index 4e4a3da..c9b67c0 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_undi.c
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_undi.c
@@ -22,6 +22,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -88,12 +90,26 @@ static void pxe_netdev_close ( void ) {
 	undi_tx_count = 0;
 }
 
+/**
+ * Dump multicast address list
+ *
+ * @v mcast		PXE multicast address list
+ */
+static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
+	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+	unsigned int i;
+
+	for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
+		DBG ( " %s", ll_protocol->ntoa ( mcast->McastAddr[i] ) );
+	}
+}
+
 /* PXENV_UNDI_STARTUP
  *
  * Status: working
  */
 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
-	DBG ( "PXENV_UNDI_STARTUP" );
+	DBG ( "PXENV_UNDI_STARTUP\n" );
 
 	undi_startup->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
@@ -104,7 +120,7 @@ PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
  * Status: working
  */
 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
-	DBG ( "PXENV_UNDI_CLEANUP" );
+	DBG ( "PXENV_UNDI_CLEANUP\n" );
 
 	pxe_netdev_close();
 
@@ -118,7 +134,8 @@ PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
  */
 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
 				     *undi_initialize ) {
-	DBG ( "PXENV_UNDI_INITIALIZE" );
+	DBG ( "PXENV_UNDI_INITIALIZE protocolini %08x\n",
+	      undi_initialize->ProtocolIni );
 
 	undi_initialize->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
@@ -133,9 +150,13 @@ PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
 	int rc;
 
 	DBG ( "PXENV_UNDI_RESET_ADAPTER" );
+	pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
+	DBG ( "\n" );
 
 	pxe_netdev_close();
 	if ( ( rc = pxe_netdev_open() ) != 0 ) {
+		DBG ( "PXENV_UNDI_RESET_ADAPTER could not reopen %s: %s\n",
+		      pxe_netdev->name, strerror ( rc ) );
 		undi_reset_adapter->Status = PXENV_STATUS ( rc );
 		return PXENV_EXIT_FAILURE;
 	}
@@ -150,7 +171,7 @@ PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
  */
 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
 				   *undi_shutdown ) {
-	DBG ( "PXENV_UNDI_SHUTDOWN" );
+	DBG ( "PXENV_UNDI_SHUTDOWN\n" );
 
 	pxe_netdev_close();
 
@@ -165,9 +186,14 @@ PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
 	int rc;
 
-	DBG ( "PXENV_UNDI_OPEN" );
+	DBG ( "PXENV_UNDI_OPEN flag %04x filter %04x",
+	      undi_open->OpenFlag, undi_open->PktFilter );
+	pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
+	DBG ( "\n" );
 
 	if ( ( rc = pxe_netdev_open() ) != 0 ) {
+		DBG ( "PXENV_UNDI_OPEN could not open %s: %s\n",
+		      pxe_netdev->name, strerror ( rc ) );
 		undi_open->Status = PXENV_STATUS ( rc );
 		return PXENV_EXIT_FAILURE;
 	}
@@ -181,7 +207,7 @@ PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
  * Status: working
  */
 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
-	DBG ( "PXENV_UNDI_CLOSE" );
+	DBG ( "PXENV_UNDI_CLOSE\n" );
 
 	pxe_netdev_close();
 
@@ -207,7 +233,13 @@ PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
 	unsigned int i;
 	int rc;
 
-	DBG ( "PXENV_UNDI_TRANSMIT" );
+	DBG2 ( "PXENV_UNDI_TRANSMIT" );
+
+	/* Forcibly enable interrupts at this point, to work around
+	 * callers that never call PXENV_UNDI_OPEN before attempting
+	 * to use the UNDI API.
+	 */
+	netdev_irq ( pxe_netdev, 1 );
 
 	/* Identify network-layer protocol */
 	switch ( undi_transmit->Protocol ) {
@@ -219,25 +251,29 @@ PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
 		ll_hlen = 0;
 		break;
 	default:
+		DBG2 ( " %02x invalid protocol\n", undi_transmit->Protocol );
 		undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
 		return PXENV_EXIT_FAILURE;
 	}
-	DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
+	DBG2 ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
 
 	/* Calculate total packet length */
 	copy_from_real ( &tbd, undi_transmit->TBD.segment,
 			 undi_transmit->TBD.offset, sizeof ( tbd ) );
 	len = tbd.ImmedLength;
-	DBG ( " %d", tbd.ImmedLength );
+	DBG2 ( " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
+	       tbd.ImmedLength );
 	for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
 		datablk = &tbd.DataBlock[i];
 		len += datablk->TDDataLen;
-		DBG ( "+%d", datablk->TDDataLen );
+		DBG2 ( " %04x:%04x+%x", datablk->TDDataPtr.segment,
+		       datablk->TDDataPtr.offset, datablk->TDDataLen );
 	}
 
 	/* Allocate and fill I/O buffer */
 	iobuf = alloc_iob ( ll_hlen + len );
 	if ( ! iobuf ) {
+		DBG2 ( " could not allocate iobuf\n" );
 		undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
 		return PXENV_EXIT_FAILURE;
 	}
@@ -262,45 +298,57 @@ PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
 					 undi_transmit->DestAddr.offset,
 					 ll_protocol->ll_addr_len );
 			ll_dest = destaddr;
+			DBG2 ( " DEST %s", ll_protocol->ntoa ( ll_dest ) );
 		} else {
-			DBG ( " BCAST" );
-			ll_dest = ll_protocol->ll_broadcast;
+			ll_dest = pxe_netdev->ll_broadcast;
+			DBG2 ( " BCAST" );
 		}
 
 		/* Add link-layer header */
-		if ( ( rc = ll_protocol->push ( iobuf, ll_dest,
+		if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
 						pxe_netdev->ll_addr,
 						net_protocol->net_proto ))!=0){
+			DBG2 ( " could not add link-layer header: %s\n",
+			       strerror ( rc ) );
 			free_iob ( iobuf );
 			undi_transmit->Status = PXENV_STATUS ( rc );
 			return PXENV_EXIT_FAILURE;
 		}
 	}
 
+	/* Flag transmission as in-progress.  Do this before starting
+	 * to transmit the packet, because the ISR may trigger before
+	 * we return from netdev_tx().
+	 */
+	undi_tx_count++;
+
 	/* Transmit packet */
+	DBG2 ( "\n" );
 	if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
+		DBG2 ( "PXENV_UNDI_TRANSMIT could not transmit: %s\n",
+		       strerror ( rc ) );
+		undi_tx_count--;
 		undi_transmit->Status = PXENV_STATUS ( rc );
 		return PXENV_EXIT_FAILURE;
 	}
 
-	/* Flag transmission as in-progress */
-	undi_tx_count++;
-
 	undi_transmit->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
 }
 
 /* PXENV_UNDI_SET_MCAST_ADDRESS
  *
- * Status: stub (no PXE multicast support)
+ * Status: working (for NICs that support receive-all-multicast)
  */
 PXENV_EXIT_t
 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
 			       *undi_set_mcast_address ) {
 	DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
+	pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
+	DBG ( "\n" );
 
-	undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
-	return PXENV_EXIT_FAILURE;
+	undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
+	return PXENV_EXIT_SUCCESS;
 }
 
 /* PXENV_UNDI_SET_STATION_ADDRESS
@@ -310,13 +358,16 @@ pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
 PXENV_EXIT_t 
 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
 				 *undi_set_station_address ) {
+	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
 
-	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
+	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS %s",
+	      ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
 
 	/* If adapter is open, the change will have no effect; return
 	 * an error
 	 */
 	if ( pxe_netdev->state & NETDEV_OPEN ) {
+		DBG ( " failed: netdev is open\n" );
 		undi_set_station_address->Status =
 			PXENV_STATUS_UNDI_INVALID_STATE;
 		return PXENV_EXIT_FAILURE;
@@ -325,8 +376,9 @@ pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
 	/* Update MAC address */
 	memcpy ( pxe_netdev->ll_addr,
 		 &undi_set_station_address->StationAddress,
-		 pxe_netdev->ll_protocol->ll_addr_len );
+		 ll_protocol->ll_addr_len );
 
+	DBG ( "\n" );
 	undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
 }
@@ -339,10 +391,17 @@ pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
 PXENV_EXIT_t
 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
 			       *undi_set_packet_filter ) {
-	DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
 
-	undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
-	return PXENV_EXIT_FAILURE;
+	DBG ( "PXENV_UNDI_SET_PACKET_FILTER %02x\n",
+	      undi_set_packet_filter->filter );
+
+	/* Pretend that we succeeded, otherwise the 3Com DOS UNDI
+	 * driver refuses to load.  (We ignore the filter value in the
+	 * PXENV_UNDI_OPEN call anyway.)
+	 */
+	undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
+
+	return PXENV_EXIT_SUCCESS;
 }
 
 /* PXENV_UNDI_GET_INFORMATION
@@ -353,6 +412,7 @@ PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
 					  *undi_get_information ) {
 	struct device *dev = pxe_netdev->dev;
 	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+	size_t ll_addr_len = ll_protocol->ll_addr_len;
 
 	DBG ( "PXENV_UNDI_GET_INFORMATION" );
 
@@ -361,17 +421,14 @@ PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
 	/* Cheat: assume all cards can cope with this */
 	undi_get_information->MaxTranUnit = ETH_MAX_MTU;
 	undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
-	undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
-	/* Cheat: assume card is always configured with its permanent
-	 * node address.  This is a valid assumption within Etherboot
-	 * at the time of writing.
-	 */
+	undi_get_information->HwAddrLen = ll_addr_len;
+	assert ( ll_addr_len <=
+		 sizeof ( undi_get_information->CurrentNodeAddress ) );
 	memcpy ( &undi_get_information->CurrentNodeAddress,
 		 pxe_netdev->ll_addr,
 		 sizeof ( undi_get_information->CurrentNodeAddress ) );
-	memcpy ( &undi_get_information->PermNodeAddress,
-		 pxe_netdev->ll_addr,
-		 sizeof ( undi_get_information->PermNodeAddress ) );
+	ll_protocol->init_addr ( pxe_netdev->hw_addr,
+				 &undi_get_information->PermNodeAddress );
 	undi_get_information->ROMAddress = 0;
 		/* nic.rom_info->rom_segment; */
 	/* We only provide the ability to receive or transmit a single
@@ -380,6 +437,10 @@ PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
 	undi_get_information->RxBufCt = 1;
 	undi_get_information->TxBufCt = 1;
 
+	DBG ( " io %04x irq %d mtu %d %s %s\n",
+	      undi_get_information->BaseIo, undi_get_information->IntNumber,
+	      undi_get_information->MaxTranUnit, ll_protocol->name,
+	      ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
 	undi_get_information->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
 }
@@ -397,6 +458,11 @@ PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
 	undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
 	undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
 
+	DBG ( " txok %d rxok %d rxcrc %d rxrsrc %d\n",
+	      undi_get_statistics->XmtGoodFrames,
+	      undi_get_statistics->RcvGoodFrames,
+	      undi_get_statistics->RcvCRCErrors,
+	      undi_get_statistics->RcvResourceErrors );
 	undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
 }
@@ -407,7 +473,7 @@ PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
  */
 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
 					   *undi_clear_statistics ) {
-	DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
+	DBG ( "PXENV_UNDI_CLEAR_STATISTICS\n" );
 
 	memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
 	memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
@@ -423,7 +489,7 @@ PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
  */
 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
 					 *undi_initiate_diags ) {
-	DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
+	DBG ( "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" );
 
 	undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
 	return PXENV_EXIT_FAILURE;
@@ -436,7 +502,7 @@ PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
  */
 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
 					  *undi_force_interrupt ) {
-	DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
+	DBG ( "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
 
 	undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
 	return PXENV_EXIT_FAILURE;
@@ -444,15 +510,28 @@ PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
 
 /* PXENV_UNDI_GET_MCAST_ADDRESS
  *
- * Status: stub (no PXE multicast support)
+ * Status: working
  */
 PXENV_EXIT_t
 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
 			       *undi_get_mcast_address ) {
-	DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
+	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+	struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
+	int rc;
 
-	undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
-	return PXENV_EXIT_FAILURE;
+	DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS %s", inet_ntoa ( ip ) );
+
+	if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
+				      undi_get_mcast_address->MediaAddr ))!=0){
+		DBG ( " failed: %s\n", strerror ( rc ) );
+		undi_get_mcast_address->Status = PXENV_STATUS ( rc );
+		return PXENV_EXIT_FAILURE;
+	}
+	DBG ( "=>%s\n",
+	      ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
+
+	undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
+	return PXENV_EXIT_SUCCESS;
 }
 
 /* PXENV_UNDI_GET_NIC_TYPE
@@ -484,6 +563,13 @@ PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
 		 */
 		undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
 		undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
+		DBG ( " PCI %02x:%02x.%x %04x:%04x (%04x:%04x) %02x%02x%02x "
+		      "rev %02x\n", PCI_BUS ( info->BusDevFunc ),
+		      PCI_SLOT ( info->BusDevFunc ),
+		      PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
+		      info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
+		      info->Base_Class, info->Sub_Class, info->Prog_Intf,
+		      info->Rev );
 		break; }
 	case BUS_TYPE_ISAPNP: {
 		struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
@@ -495,8 +581,12 @@ PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
 		/* Cheat: remaining fields are probably unnecessary,
 		 * and would require adding extra code to isapnp.c.
 		 */
+		DBG ( " ISAPnP CSN %04x %08x %02x%02x%02x\n",
+		      info->CardSelNum, info->EISA_Dev_ID,
+		      info->Base_Class, info->Sub_Class, info->Prog_Intf );
 		break; }
 	default:
+		DBG ( " failed: unknown bus type\n" );
 		undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
 		return PXENV_EXIT_FAILURE;
 	}
@@ -517,12 +607,18 @@ PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
 	 * Most PXE stacks seem to take this approach.
 	 */
 	snprintf ( ( char * ) undi_get_iface_info->IfaceType,
-		   sizeof ( undi_get_iface_info->IfaceType ), "gPXE" );
+		   sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
 	undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
-	undi_get_iface_info->ServiceFlags = 0;
+	undi_get_iface_info->ServiceFlags =
+		( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
+		  SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
+		  SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ );
 	memset ( undi_get_iface_info->Reserved, 0,
 		 sizeof(undi_get_iface_info->Reserved) );
 
+	DBG ( " %s %dbps flags %08x\n", undi_get_iface_info->IfaceType,
+	      undi_get_iface_info->LinkSpeed,
+	      undi_get_iface_info->ServiceFlags );
 	undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
 }
@@ -533,7 +629,7 @@ PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
  */
 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
 				    *undi_get_state ) {
-	DBG ( "PXENV_UNDI_GET_STATE" );
+	DBG ( "PXENV_UNDI_GET_STATE failed: unsupported\n" );
 
 	undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
 	return PXENV_EXIT_FAILURE;
@@ -555,7 +651,10 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 	unsigned int prottype;
 	int rc;
 
-	DBG ( "PXENV_UNDI_ISR" );
+	/* Use coloured debug, since UNDI ISR messages are likely to
+	 * be interspersed amongst other UNDI messages.
+	 */
+	DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
 
 	/* Just in case some idiot actually looks at these fields when
 	 * we weren't meant to fill them in...
@@ -568,7 +667,7 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 
 	switch ( undi_isr->FuncFlag ) {
 	case PXENV_UNDI_ISR_IN_START :
-		DBG ( " START" );
+		DBGC2 ( &pxenv_undi_isr, " START" );
 
 		/* Call poll().  This should acknowledge the device
 		 * interrupt and queue up any received packet.
@@ -579,13 +678,14 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 		netdev_irq ( pxe_netdev, 0 );
 
 		/* Always say it was ours for the sake of simplicity */
+		DBGC2 ( &pxenv_undi_isr, " OURS" );
 		undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
 		break;
 	case PXENV_UNDI_ISR_IN_PROCESS :
-		DBG ( " PROCESS" );
-		/* Fall through */
 	case PXENV_UNDI_ISR_IN_GET_NEXT :
-		DBG ( " GET_NEXT" );
+		DBGC2 ( &pxenv_undi_isr, " %s",
+			( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
+			  "PROCESS" : "GET_NEXT" ) );
 
 		/* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
 		 * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
@@ -600,7 +700,7 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 		 * netdev TX queue is empty, report the TX completion.
 		 */
 		if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
-			DBG ( " TXC" );
+			DBGC2 ( &pxenv_undi_isr, " TXC" );
 			undi_tx_count--;
 			undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
 			break;
@@ -609,7 +709,7 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 		/* Remove first packet from netdev RX queue */
 		iobuf = netdev_rx_dequeue ( pxe_netdev );
 		if ( ! iobuf ) {
-			DBG ( " DONE" );
+			DBGC2 ( &pxenv_undi_isr, " DONE" );
 			/* No more packets remaining */
 			undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
 			/* Re-enable interrupts */
@@ -619,17 +719,18 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 
 		/* Copy packet to base memory buffer */
 		len = iob_len ( iobuf );
-		DBG ( " RX %zd", len );
+		DBGC2 ( &pxenv_undi_isr, " RX" );
 		if ( len > sizeof ( basemem_packet ) ) {
 			/* Should never happen */
+			DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
 			len = sizeof ( basemem_packet );
 		}
 		memcpy ( basemem_packet, iobuf->data, len );
 
 		/* Strip link-layer header */
 		ll_protocol = pxe_netdev->ll_protocol;
-		if ( ( rc = ll_protocol->pull ( iobuf, &ll_dest, &ll_source,
-						&net_proto ) ) != 0 ) {
+		if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
+						&ll_source, &net_proto )) !=0){
 			/* Assume unknown net_proto and no ll_source */
 			net_proto = 0;
 			ll_source = NULL;
@@ -655,7 +756,6 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 			prottype = P_UNKNOWN;
 			break;
 		}
-		DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
 
 		/* Fill in UNDI_ISR structure */
 		undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
@@ -666,12 +766,18 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 		undi_isr->Frame.offset = __from_data16 ( basemem_packet );
 		undi_isr->ProtType = prottype;
 		undi_isr->PktType = XMT_DESTADDR;
+		DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
+			undi_isr->Frame.segment, undi_isr->Frame.offset,
+			undi_isr->BufferLength, undi_isr->FrameLength,
+			( net_protocol ? net_protocol->name : "RAW" ),
+			undi_isr->FrameHeaderLength );
 
 		/* Free packet */
 		free_iob ( iobuf );
 		break;
 	default :
-		DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
+		DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
+			undi_isr->FuncFlag );
 
 		/* Should never happen */
 		undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
@@ -679,6 +785,7 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
 		return PXENV_EXIT_FAILURE;
 	}
 
+	DBGC2 ( &pxenv_undi_isr, "\n" );
 	undi_isr->Status = PXENV_STATUS_SUCCESS;
 	return PXENV_EXIT_SUCCESS;
 }
diff --git a/gpxe/src/arch/i386/interface/pxeparent/pxeparent.c b/gpxe/src/arch/i386/interface/pxeparent/pxeparent.c
new file mode 100644
index 0000000..582db5d
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pxeparent/pxeparent.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/dhcp.h>
+#include <pxeparent.h>
+#include <pxe_api.h>
+#include <pxe_types.h>
+#include <pxe.h>
+
+/** @file
+ *
+ * Call interface to parent PXE stack
+ *
+ */
+
+/**
+ * Name PXE API call
+ *
+ * @v function		API call number
+ * @ret name		API call name
+ */
+static inline __attribute__ (( always_inline )) const char *
+pxeparent_function_name ( unsigned int function ) {
+	switch ( function ) {
+	case PXENV_START_UNDI:
+		return "PXENV_START_UNDI";
+	case PXENV_STOP_UNDI:
+		return "PXENV_STOP_UNDI";
+	case PXENV_UNDI_STARTUP:
+		return "PXENV_UNDI_STARTUP";
+	case PXENV_UNDI_CLEANUP:
+		return "PXENV_UNDI_CLEANUP";
+	case PXENV_UNDI_INITIALIZE:
+		return "PXENV_UNDI_INITIALIZE";
+	case PXENV_UNDI_RESET_ADAPTER:
+		return "PXENV_UNDI_RESET_ADAPTER";
+	case PXENV_UNDI_SHUTDOWN:
+		return "PXENV_UNDI_SHUTDOWN";
+	case PXENV_UNDI_OPEN:
+		return "PXENV_UNDI_OPEN";
+	case PXENV_UNDI_CLOSE:
+		return "PXENV_UNDI_CLOSE";
+	case PXENV_UNDI_TRANSMIT:
+		return "PXENV_UNDI_TRANSMIT";
+	case PXENV_UNDI_SET_MCAST_ADDRESS:
+		return "PXENV_UNDI_SET_MCAST_ADDRESS";
+	case PXENV_UNDI_SET_STATION_ADDRESS:
+		return "PXENV_UNDI_SET_STATION_ADDRESS";
+	case PXENV_UNDI_SET_PACKET_FILTER:
+		return "PXENV_UNDI_SET_PACKET_FILTER";
+	case PXENV_UNDI_GET_INFORMATION:
+		return "PXENV_UNDI_GET_INFORMATION";
+	case PXENV_UNDI_GET_STATISTICS:
+		return "PXENV_UNDI_GET_STATISTICS";
+	case PXENV_UNDI_CLEAR_STATISTICS:
+		return "PXENV_UNDI_CLEAR_STATISTICS";
+	case PXENV_UNDI_INITIATE_DIAGS:
+		return "PXENV_UNDI_INITIATE_DIAGS";
+	case PXENV_UNDI_FORCE_INTERRUPT:
+		return "PXENV_UNDI_FORCE_INTERRUPT";
+	case PXENV_UNDI_GET_MCAST_ADDRESS:
+		return "PXENV_UNDI_GET_MCAST_ADDRESS";
+	case PXENV_UNDI_GET_NIC_TYPE:
+		return "PXENV_UNDI_GET_NIC_TYPE";
+	case PXENV_UNDI_GET_IFACE_INFO:
+		return "PXENV_UNDI_GET_IFACE_INFO";
+	/*
+	 * Duplicate case value; this is a bug in the PXE specification.
+	 *
+	 *	case PXENV_UNDI_GET_STATE:
+	 *		return "PXENV_UNDI_GET_STATE";
+	 */
+	case PXENV_UNDI_ISR:
+		return "PXENV_UNDI_ISR";
+	case PXENV_GET_CACHED_INFO:
+		return "PXENV_GET_CACHED_INFO";
+	default:
+		return "UNKNOWN API CALL";
+	}
+}
+
+/**
+ * PXE parent parameter block
+ *
+ * Used as the paramter block for all parent PXE API calls.  Resides in base
+ * memory.
+ */
+static union u_PXENV_ANY __bss16 ( pxeparent_params );
+#define pxeparent_params __use_data16 ( pxeparent_params )
+
+/** PXE parent entry point
+ *
+ * Used as the indirection vector for all parent PXE API calls.  Resides in
+ * base memory.
+ */
+SEGOFF16_t __bss16 ( pxeparent_entry_point );
+#define pxeparent_entry_point __use_data16 ( pxeparent_entry_point )
+
+/**
+ * Issue parent PXE API call
+ *
+ * @v entry		Parent PXE stack entry point
+ * @v function		API call number
+ * @v params		PXE parameter block
+ * @v params_len	Length of PXE parameter block
+ * @ret rc		Return status code
+ */
+int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
+		     void *params, size_t params_len ) {
+	PXENV_EXIT_t exit;
+	int discard_b, discard_D;
+	int rc;
+
+	/* Copy parameter block and entry point */
+	assert ( params_len <= sizeof ( pxeparent_params ) );
+	memcpy ( &pxeparent_params, params, params_len );
+	memcpy ( &pxeparent_entry_point, &entry, sizeof ( entry ) );
+
+	/* Call real-mode entry point.  This calling convention will
+	 * work with both the !PXE and the PXENV+ entry points.
+	 */
+	__asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
+					   "pushw %%di\n\t"
+					   "pushw %%bx\n\t"
+					   "lcall *pxeparent_entry_point\n\t"
+					   "addw $6, %%sp\n\t" )
+			       : "=a" ( exit ), "=b" ( discard_b ),
+			         "=D" ( discard_D )
+			       : "b" ( function ),
+			         "D" ( __from_data16 ( &pxeparent_params ) )
+			       : "ecx", "edx", "esi", "ebp" );
+
+	/* PXE API calls may rudely change the status of A20 and not
+	 * bother to restore it afterwards.  Intel is known to be
+	 * guilty of this.
+	 *
+	 * Note that we will return to this point even if A20 gets
+	 * screwed up by the parent PXE stack, because Etherboot always
+	 * resides in an even megabyte of RAM.
+	 */
+	gateA20_set();
+
+	/* Determine return status code based on PXENV_EXIT and
+	 * PXENV_STATUS
+	 */
+	if ( exit == PXENV_EXIT_SUCCESS ) {
+		rc = 0;
+	} else {
+		rc = -pxeparent_params.Status;
+		/* Paranoia; don't return success for the combination
+		 * of PXENV_EXIT_FAILURE but PXENV_STATUS_SUCCESS
+		 */
+		if ( rc == 0 )
+			rc = -EIO;
+	}
+
+	/* If anything goes wrong, print as much debug information as
+	 * it's possible to give.
+	 */
+	if ( rc != 0 ) {
+		SEGOFF16_t rm_params = {
+			.segment = rm_ds,
+			.offset = __from_data16 ( &pxeparent_params ),
+		};
+
+		DBG ( "PXEPARENT %s failed: %s\n",
+		       pxeparent_function_name ( function ), strerror ( rc ) );
+		DBG ( "PXEPARENT parameters at %04x:%04x length "
+		       "%#02zx, entry point at %04x:%04x\n",
+		       rm_params.segment, rm_params.offset, params_len,
+		       pxeparent_entry_point.segment,
+		       pxeparent_entry_point.offset );
+		DBG ( "PXEPARENT parameters provided:\n" );
+		DBG_HDA ( rm_params, params, params_len );
+		DBG ( "PXEPARENT parameters returned:\n" );
+		DBG_HDA ( rm_params, &pxeparent_params, params_len );
+	}
+
+	/* Copy parameter block back */
+	memcpy ( params, &pxeparent_params, params_len );
+
+	return rc;
+}
+
diff --git a/gpxe/src/arch/i386/interface/pxeparent/pxeparent_dhcp.c b/gpxe/src/arch/i386/interface/pxeparent/pxeparent_dhcp.c
new file mode 100644
index 0000000..6605943
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pxeparent/pxeparent_dhcp.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/netdevice.h>
+#include <undipreload.h>
+#include <pxeparent.h>
+#include <realmode.h>
+#include <pxe_api.h>
+
+/**
+ * Present cached DHCP packet if it exists
+ */
+void __weak_impl ( get_cached_dhcpack ) ( void ) {
+	struct undi_device *undi;
+	struct s_PXENV_GET_CACHED_INFO get_cached_info;
+	int rc;
+
+	/* Use preloaded UNDI device to get at PXE entry point */
+	undi = &preloaded_undi;
+	if ( ! undi->entry.segment ) {
+		DBG ( "PXEDHCP no preloaded UNDI device found\n" );
+		return;
+	}
+
+	/* Check that stack is available to get cached info */
+	if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
+		DBG ( "PXEDHCP stack was unloaded, no cache available\n" );
+		return;
+	}
+
+	/* Obtain cached DHCP packet */
+	memset ( &get_cached_info, 0, sizeof ( get_cached_info ) );
+	get_cached_info.PacketType = PXENV_PACKET_TYPE_DHCP_ACK;
+
+	if ( ( rc = pxeparent_call ( undi->entry, PXENV_GET_CACHED_INFO,
+				     &get_cached_info,
+				     sizeof ( get_cached_info ) ) ) != 0 ) {
+		DBG ( "PXEDHCP GET_CACHED_INFO failed: %s\n", strerror ( rc ) );
+		return;
+	}
+
+	DBG ( "PXEDHCP got cached info at %04x:%04x length %d\n",
+	      get_cached_info.Buffer.segment, get_cached_info.Buffer.offset,
+	      get_cached_info.BufferSize );
+
+	/* Present cached DHCP packet */
+	store_cached_dhcpack ( real_to_user ( get_cached_info.Buffer.segment,
+					      get_cached_info.Buffer.offset ),
+			       get_cached_info.BufferSize );
+}
diff --git a/gpxe/src/arch/i386/interface/syslinux/com32_call.c b/gpxe/src/arch/i386/interface/syslinux/com32_call.c
index 4a782dc..d2c3f91 100644
--- a/gpxe/src/arch/i386/interface/syslinux/com32_call.c
+++ b/gpxe/src/arch/i386/interface/syslinux/com32_call.c
@@ -21,6 +21,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <realmode.h>
 #include <comboot.h>
diff --git a/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S b/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S
index 08d7398..5c5bd13 100644
--- a/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S
+++ b/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 	.text
 	.arch i386
 	.code32
diff --git a/gpxe/src/arch/i386/interface/syslinux/comboot_call.c b/gpxe/src/arch/i386/interface/syslinux/comboot_call.c
index bf6c4c6..0a17bf1 100644
--- a/gpxe/src/arch/i386/interface/syslinux/comboot_call.c
+++ b/gpxe/src/arch/i386/interface/syslinux/comboot_call.c
@@ -21,6 +21,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <errno.h>
 #include <realmode.h>
 #include <biosint.h>
@@ -37,6 +39,8 @@
 #include <gpxe/init.h>
 #include <gpxe/image.h>
 #include <usr/imgmgmt.h>
+#include "config/console.h"
+#include "config/serial.h"
 
 /** The "SYSLINUX" version string */
 static char __data16_array ( syslinux_version, [] ) = "gPXE " VERSION;
@@ -324,7 +328,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) {
 	case 0x0001: /* Get Version */
 
 		/* Number of INT 22h API functions available */
-		ix86->regs.ax = 0x001B;
+		ix86->regs.ax = 0x001D;
 
 		/* SYSLINUX version number */
 		ix86->regs.ch = 0; /* major */
@@ -441,8 +445,10 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) {
 		break;
 
 	case 0x0009: /* Call PXE Stack */
-		pxe_api_call ( ix86 );
-		ix86->flags &= ~CF;
+		if ( pxe_api_call_weak ( ix86 ) != 0 )
+			ix86->flags |= CF;
+		else
+			ix86->flags &= ~CF;
 		break;
 
 	case 0x000A: /* Get Derivative-Specific Information */
@@ -454,8 +460,14 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) {
 		break;
 
 	case 0x000B: /* Get Serial Console Configuration */
-		/* FIXME: stub */
+#if defined(CONSOLE_SERIAL) && !defined(COMPRESERVE)
+		ix86->regs.dx = COMCONSOLE;
+		ix86->regs.cx = 115200 / COMSPEED;
+		ix86->regs.bx = 0;
+#else
 		ix86->regs.dx = 0;
+#endif
+
 		ix86->flags &= ~CF;
 		break;
 
@@ -629,6 +641,17 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) {
 
 		break;
 
+	case 0x001C: /* Get pointer to auxilliary data vector */
+		/* FIXME: stub */
+		ix86->regs.cx = 0; /* Size of the ADV */
+		ix86->flags &= ~CF;
+		break;
+
+	case 0x001D: /* Write auxilliary data vector */
+		/* FIXME: stub */
+		ix86->flags &= ~CF;
+		break;
+
 	default:
 		DBG ( "COMBOOT unknown int22 function %04x\n", ix86->regs.ax );
 		break;
diff --git a/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c b/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c
index 41c3af7..30ac502 100644
--- a/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c
+++ b/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c
@@ -5,6 +5,8 @@
 #include <gpxe/process.h>
 #include <gpxe/resolv.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static int comboot_resolv_rc;
 static struct in_addr comboot_resolv_addr;
 
diff --git a/gpxe/src/arch/i386/prefix/bImageprefix.S b/gpxe/src/arch/i386/prefix/bImageprefix.S
deleted file mode 100644
index 30020f7..0000000
--- a/gpxe/src/arch/i386/prefix/bImageprefix.S
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
-	Copyright (C) 2000, Entity Cyber, Inc.
-
-	Authors: Gary Byers (gb at thinguin.org)
-		 Marty Connor (mdc at thinguin.org)
-		 Eric Biederman (ebiederman at lnxi.com)
-
-	This code also derives a lot from arch/i386/boot/setup.S in
-	the linux kernel.
-
-	This software may be used and distributed according to the terms
-	of the GNU Public License (GPL), incorporated herein by reference.
-
-	Description:	
-
-	This is just a little bit of code and data that can get prepended
-	to an Etherboot ROM image in order to allow LILO to load the
-	result as if it were a Linux kernel image.
-
-	A real Linux kernel image consists of a one-sector boot loader
-	(to load the image from a floppy disk), followed a few sectors
-	of setup code, followed by the kernel code itself.  There's
-	a table in the first sector (starting at offset 497) that indicates
-	how many sectors of setup code follow the first sector and which
-	contains some other parameters that aren't interesting in this
-	case.
-
-	When LILO loads the sectors that comprise a kernel image, it doesn't
-	execute the code in the first sector (since that code would try to
-	load the image from a floppy disk.)  The code in the first sector
-	below doesn't expect to get executed (and prints an error message
-	if it ever -is- executed.)  LILO's only interested in knowing the
-	number of setup sectors advertised in the table (at offset 497 in
-	the first sector.)
-
-	Etherboot doesn't require much in the way of setup code.
-	Historically, the Linux kernel required at least 4 sectors of
-	setup code.  Current versions of LILO look at the byte at
-	offset 497 in the first sector to indicate how many sectors
-	of setup code are contained in the image.
-
-	The setup code that is present here does a lot of things
-	exactly the way the linux kernel does them instead of in
-	ways more typical of etherboot.  Generally this is so
-	the code can be strongly compatible with the linux kernel.
-	In addition the general etherboot technique of enabling the a20
-	after we switch into protected mode does not work if etherboot
-	is being loaded at 1MB.
-*/
-
-	.equ	CR0_PE,1
-
-#ifdef	GAS291
-#define DATA32 data32;
-#define ADDR32 addr32;
-#define	LJMPI(x)	ljmp	x
-#else
-#define DATA32 data32
-#define ADDR32 addr32
-/* newer GAS295 require #define	LJMPI(x)	ljmp	*x */
-#define	LJMPI(x)	ljmp	x
-#endif
-
-/* Simple and small GDT entries for booting only */
-#define GDT_ENTRY_BOOT_CS	2
-#define GDT_ENTRY_BOOT_DS	(GDT_ENTRY_BOOT_CS + 1)
-#define __BOOT_CS	(GDT_ENTRY_BOOT_CS * 8)
-#define __BOOT_DS	(GDT_ENTRY_BOOT_DS * 8)
-
-
-#define	SETUPSECS 4		/* Minimal nr of setup-sectors */
-#define PREFIXSIZE ((SETUPSECS+1)*512)
-#define PREFIXPGH (PREFIXSIZE / 16 )
-#define	BOOTSEG  0x07C0		/* original address of boot-sector */
-#define	INITSEG  0x9000		/* we move boot here - out of the way */
-#define	SETUPSEG 0x9020		/* setup starts here */
-#define SYSSEG   0x1000		/* system loaded at 0x10000 (65536). */
-
-#define DELTA_INITSEG  (SETUPSEG - INITSEG) /* 0x0020 */
-	
-/* Signature words to ensure LILO loaded us right */
-#define SIG1	0xAA55
-#define SIG2	0x5A5A
-
-	.text
-	.code16
-	.arch i386
-	.org	0
-	.section ".prefix", "ax", @progbits
-_prefix:
-
-/* 
-	This is a minimal boot sector.	If anyone tries to execute it (e.g., if
-	a .lkrn file is dd'ed to a floppy), print an error message. 
-*/
-
-bootsector: 
-	jmp	$BOOTSEG, $go - _prefix	/* reload cs:ip to match relocation addr */
-go: 
-	movw	$0x2000, %di		/*  0x2000 is arbitrary value >= length
-					    of bootsect + room for stack */
-
-	movw	$BOOTSEG, %ax
-	movw	%ax,%ds
-	movw	%ax,%es
-
-	cli
-	movw	%ax, %ss		/* put stack at BOOTSEG:0x2000. */
-	movw	%di,%sp
-	sti
-
-	movw	$why_end-why, %cx
-	movw	$why - _prefix, %si
-
-	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
-	movb	$0x0e, %ah		/* write char, tty mode */
-prloop: 
-	lodsb
-	int	$0x10
-	loop	prloop
-freeze: jmp	freeze
-
-why:	.ascii	"This image cannot be loaded from a floppy disk.\r\n"
-why_end: 
-
-
-	.org	497
-setup_sects: 
-	.byte	SETUPSECS
-root_flags: 
-	.word	0
-syssize: 
-	.word	_verbatim_size_pgh - PREFIXPGH
-swap_dev: 
-	.word	0
-ram_size: 
-	.word	0
-vid_mode: 
-	.word	0
-root_dev: 
-	.word	0
-boot_flag: 
-	.word	0xAA55
-
-/*
-	We're now at the beginning of the second sector of the image -
-	where the setup code goes.
-
-	We don't need to do too much setup for Etherboot.
-
-	This code gets loaded at SETUPSEG:0.  It wants to start
-	executing the Etherboot image that's loaded at SYSSEG:0 and
-	whose entry point is SYSSEG:0.
-*/
-setup_code:
-	jmp	trampoline
-# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
-
-		.ascii	"HdrS"		# header signature
-		.word	0x0203		# header version number (>= 0x0105)
-					# or else old loadlin-1.5 will fail)
-realmode_swtch:	.word	0, 0		# default_switch, SETUPSEG
-start_sys_seg:	.word	SYSSEG		# low load segment (obsolete)
-		.word	kernel_version - setup_code
-					# pointing to kernel version string
-					# above section of header is compatible
-					# with loadlin-1.5 (header v1.5). Don't
-					# change it.
-
-type_of_loader:	.byte	0		# = 0, old one (LILO, Loadlin,
-					#      Bootlin, SYSLX, bootsect...)
-					# See Documentation/i386/boot.txt for
-					# assigned ids
-	
-# flags, unused bits must be zero (RFU) bit within loadflags
-loadflags:
-LOADED_HIGH	= 1			# If set, the kernel is loaded high
-CAN_USE_HEAP	= 0x80			# If set, the loader also has set
-					# heap_end_ptr to tell how much
-					# space behind setup.S can be used for
-					# heap purposes.
-					# Only the loader knows what is free
-		.byte	LOADED_HIGH
-
-setup_move_size: .word  0x8000		# size to move, when setup is not
-					# loaded at 0x90000. We will move setup 
-					# to 0x90000 then just before jumping
-					# into the kernel. However, only the
-					# loader knows how much data behind
-					# us also needs to be loaded.
-
-code32_start:				# here loaders can put a different
-					# start address for 32-bit code.
-		.long	0x100000	# 0x100000 = default for big kernel
-
-ramdisk_image:	.long	0		# address of loaded ramdisk image
-					# Here the loader puts the 32-bit
-					# address where it loaded the image.
-					# This only will be read by the kernel.
-
-ramdisk_size:	.long	0		# its size in bytes
-
-bootsect_kludge:
-		.long	0		# obsolete
-
-heap_end_ptr:	.word	0		# (Header version 0x0201 or later)
-					# space from here (exclusive) down to
-					# end of setup code can be used by setup
-					# for local heap purposes.
-
-pad1:		.word	0
-cmd_line_ptr:	.long 0			# (Header version 0x0202 or later)
-					# If nonzero, a 32-bit pointer
-					# to the kernel command line.
-					# The command line should be
-					# located between the start of
-					# setup and the end of low
-					# memory (0xa0000), or it may
-					# get overwritten before it
-					# gets read.  If this field is
-					# used, there is no longer
-					# anything magical about the
-					# 0x90000 segment; the setup
-					# can be located anywhere in
-					# low memory 0x10000 or higher.
-
-ramdisk_max:	.long 0			# (Header version 0x0203 or later)
-					# The highest safe address for
-					# the contents of an initrd
-
-trampoline:	call	start_of_setup
-trampoline_end:
-		.space	1024
-# End of setup header #####################################################
-
-start_of_setup:
-# Set %ds = %cs, we know that SETUPSEG = %cs at this point
-	movw	%cs, %ax		# aka SETUPSEG
-	movw	%ax, %ds
-# Check signature at end of setup
-	cmpw	$SIG1, (setup_sig1 - setup_code)
-	jne	bad_sig
-
-	cmpw	$SIG2, (setup_sig2 - setup_code)
-	jne	bad_sig
-
-	jmp	good_sig1
-
-# Routine to print asciiz string at ds:si
-prtstr:
-	lodsb
-	andb	%al, %al
-	jz	fin
-
-	call	prtchr
-	jmp	prtstr
-
-fin:	ret
-
-# Part of above routine, this one just prints ascii al
-prtchr:	pushw	%ax
-	pushw	%cx
-	movw	$7,%bx
-	movw	$0x01, %cx
-	movb	$0x0e, %ah
-	int	$0x10
-	popw	%cx
-	popw	%ax
-	ret
-
-no_sig_mess: .string	"No setup signature found ..."
-
-good_sig1:
-	jmp	good_sig
-
-# We now have to find the rest of the setup code/data
-bad_sig:
-	movw	%cs, %ax			# SETUPSEG
-	subw	$DELTA_INITSEG, %ax		# INITSEG
-	movw	%ax, %ds
-	xorb	%bh, %bh
-	movb	(497), %bl			# get setup sect from bootsect
-	subw	$4, %bx				# LILO loads 4 sectors of setup
-	shlw	$8, %bx				# convert to words (1sect=2^8 words)
-	movw	%bx, %cx
-	shrw	$3, %bx				# convert to segment
-	addw	$SYSSEG, %bx
-	movw	%bx, %cs:(start_sys_seg - setup_code)
-# Move rest of setup code/data to here
-	movw	$2048, %di			# four sectors loaded by LILO
-	subw	%si, %si
-	pushw	%cs
-	popw	%es
-	movw	$SYSSEG, %ax
-	movw	%ax, %ds
-	rep
-	movsw
-	movw	%cs, %ax			# aka SETUPSEG
-	movw	%ax, %ds
-	cmpw	$SIG1, (setup_sig1 - setup_code)
-	jne	no_sig
-
-	cmpw	$SIG2, (setup_sig2 - setup_code)
-	jne	no_sig
-
-	jmp	good_sig
-
-no_sig:
-	lea	(no_sig_mess - setup_code), %si
-	call	prtstr
-
-no_sig_loop:
-	hlt
-	jmp	no_sig_loop
-
-good_sig:
-	cmpw	$0, %cs:(realmode_swtch - setup_code)
-	jz	rmodeswtch_normal
-
-	lcall	*%cs:(realmode_swtch - setup_code)
-	jmp	rmodeswtch_end
-
-rmodeswtch_normal:
-	pushw	%cs
-	call	default_switch
-
-rmodeswtch_end:
-# we get the code32 start address and modify the below 'jmpi'
-# (loader may have changed it)
-	movl	%cs:(code32_start - setup_code), %eax
-	movl	%eax, %cs:(code32 - setup_code)
-
-# then we load the segment descriptors
-	movw	%cs, %ax			# aka SETUPSEG
-	movw	%ax, %ds
-
-#
-# Enable A20.  This is at the very best an annoying procedure.
-# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin.
-#
-
-A20_TEST_LOOPS		=  32		# Iterations per wait
-A20_ENABLE_LOOPS	= 255		# Total loops to try		
-
-a20_try_loop:
-
-	# First, see if we are on a system with no A20 gate.
-a20_none:
-	call	a20_test
-	jnz	a20_done
-
-	# Next, try the BIOS (INT 0x15, AX=0x2401)
-a20_bios:
-	movw	$0x2401, %ax
-	pushfl					# Be paranoid about flags
-	int	$0x15
-	popfl
-
-	call	a20_test
-	jnz	a20_done
-
-	# Try enabling A20 through the keyboard controller
-a20_kbc:
-	call	empty_8042
-
-	call	a20_test			# Just in case the BIOS worked
-	jnz	a20_done			# but had a delayed reaction.
-
-	movb	$0xD1, %al			# command write
-	outb	%al, $0x64
-	call	empty_8042
-
-	movb	$0xDF, %al			# A20 on
-	outb	%al, $0x60
-	call	empty_8042
-
-	# Wait until a20 really *is* enabled; it can take a fair amount of
-	# time on certain systems; Toshiba Tecras are known to have this
-	# problem.
-a20_kbc_wait:
-	xorw	%cx, %cx
-a20_kbc_wait_loop:
-	call	a20_test
-	jnz	a20_done
-	loop	a20_kbc_wait_loop
-
-	# Final attempt: use "configuration port A"
-a20_fast:
-	inb	$0x92, %al			# Configuration Port A
-	orb	$0x02, %al			# "fast A20" version
-	andb	$0xFE, %al			# don't accidentally reset
-	outb	%al, $0x92
-
-	# Wait for configuration port A to take effect
-a20_fast_wait:
-	xorw	%cx, %cx
-a20_fast_wait_loop:
-	call	a20_test
-	jnz	a20_done
-	loop	a20_fast_wait_loop
-
-	# A20 is still not responding.  Try frobbing it again.
-	# 
-	decb	(a20_tries - setup_code)
-	jnz	a20_try_loop
-	
-	movw	$(a20_err_msg - setup_code), %si
-	call	prtstr
-
-a20_die:
-	hlt
-	jmp	a20_die
-
-a20_tries:
-	.byte	A20_ENABLE_LOOPS
-
-a20_err_msg:
-	.ascii	"linux: fatal error: A20 gate not responding!"
-	.byte	13, 10, 0
-
-	# If we get here, all is good
-a20_done:
-	# Leave the idt alone
-	
-	# set up gdt 
-	xorl	%eax, %eax				# Compute gdt_base
-	movw	%ds, %ax				# (Convert %ds:gdt to a linear ptr)
-	shll	$4, %eax
-	addl	$(bImage_gdt - setup_code), %eax
-	movl	%eax, (bImage_gdt_48+2 - setup_code)
-	DATA32 lgdt %ds:(bImage_gdt_48 - setup_code)	# load gdt with whatever is
-							# appropriate
-
-	# Switch to protected mode
-	movl	%cr0, %eax
-	orb	$CR0_PE, %al
-	movl	%eax, %cr0
-
-	DATA32 ljmp *%ds:(code32 - setup_code)
-code32:
-	.long	0x100000
-	.word	__BOOT_CS, 0
-	
-# Here's a bunch of information about your current kernel..
-kernel_version:	.ascii	"Etherboot "
-		.ascii	VERSION
-		.byte	0
-
-# This is the default real mode switch routine.
-# to be called just before protected mode transition
-default_switch:
-	cli					# no interrupts allowed !
-	movb	$0x80, %al			# disable NMI for bootup
-						# sequence
-	outb	%al, $0x70
-	lret
-
-# This routine tests whether or not A20 is enabled.  If so, it
-# exits with zf = 0.
-#
-# The memory address used, 0x200, is the int $0x80 vector, which
-# should be safe.
-
-A20_TEST_ADDR = 4*0x80
-
-a20_test:
-	pushw	%cx
-	pushw	%ax
-	xorw	%cx, %cx
-	movw	%cx, %fs			# Low memory
-	decw	%cx
-	movw	%cx, %gs			# High memory area
-	movw	$A20_TEST_LOOPS, %cx
-	movw	%fs:(A20_TEST_ADDR), %ax
-	pushw	%ax
-a20_test_wait:
-	incw	%ax
-	movw	%ax, %fs:(A20_TEST_ADDR)
-	call	delay				# Serialize and make delay constant
-	cmpw	%gs:(A20_TEST_ADDR+0x10), %ax
-	loope	a20_test_wait
-
-	popw	%fs:(A20_TEST_ADDR)
-	popw	%ax
-	popw	%cx
-	ret	
-
-
-# This routine checks that the keyboard command queue is empty
-# (after emptying the output buffers)
-#
-# Some machines have delusions that the keyboard buffer is always full
-# with no keyboard attached...
-#
-# If there is no keyboard controller, we will usually get 0xff
-# to all the reads.  With each IO taking a microsecond and
-# a timeout of 100,000 iterations, this can take about half a
-# second ("delay" == outb to port 0x80). That should be ok,
-# and should also be plenty of time for a real keyboard controller
-# to empty.
-#
-
-empty_8042:
-	pushl	%ecx
-	movl	$100000, %ecx
-
-empty_8042_loop:
-	decl	%ecx
-	jz	empty_8042_end_loop
-
-	call	delay
-
-	inb	$0x64, %al			# 8042 status port
-	testb	$1, %al				# output buffer?
-	jz	no_output
-
-	call	delay
-	inb	$0x60, %al			# read it
-	jmp	empty_8042_loop
-
-no_output:
-	testb	$2, %al				# is input buffer full?
-	jnz	empty_8042_loop			# yes - loop
-empty_8042_end_loop:
-	popl	%ecx
-
-		
-# Delay is needed after doing I/O
-delay:
-	outb	%al,$0x80
-	ret
-
-# Descriptor tables
-#
-# NOTE: The intel manual says gdt should be sixteen bytes aligned for
-# efficiency reasons.  However, there are machines which are known not
-# to boot with misaligned GDTs, so alter this at your peril!  If you alter
-# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two
-# empty GDT entries (one for NULL and one reserved).
-#
-# NOTE:	On some CPUs, the GDT must be 8 byte aligned.  This is
-# true for the Voyager Quad CPU card which will not boot without
-# This directive.  16 byte aligment is recommended by intel.
-#
-	.balign 16
-bImage_gdt:
-	.fill GDT_ENTRY_BOOT_CS,8,0
-
-	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
-	.word	0				# base address = 0
-	.word	0x9A00				# code read/exec
-	.word	0x00CF				# granularity = 4096, 386
-						#  (+5th nibble of limit)
-
-	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
-	.word	0				# base address = 0
-	.word	0x9200				# data read/write
-	.word	0x00CF				# granularity = 4096, 386
-						#  (+5th nibble of limit)
-bImage_gdt_end:
-	.balign	4
-	
-	.word	0				# alignment byte
-bImage_idt_48:
-	.word	0				# idt limit = 0
-	.long	0				# idt base = 0L
-
-	.word	0				# alignment byte
-bImage_gdt_48:
-	.word	bImage_gdt_end - bImage_gdt - 1	# gdt limit
-	.long	bImage_gdt_48 - setup_code	# gdt base (filled in later)
-
-	.section ".text16", "ax", @progbits
-prefix_exit:
-	int	$0x19		/* should try to boot machine */
-prefix_exit_end:
-	.previous
-	
-	
-	.org (PREFIXSIZE - 4)
-# Setup signature -- must be last
-setup_sig1:	.word	SIG1
-setup_sig2:	.word	SIG2
-	/* Etherboot expects to be contiguous in memory once loaded.
-	 * The linux bImage protocol does not do this, but since we
-	 * don't need any information that's left in the prefix, it
-	 * doesn't matter: we just have to ensure that we make it to _start
-	 *
-	 * protected_start will live at 0x100000 and it will be the
-	 * the first code called as we enter protected mode.
-	 */
-	.code32
-protected_start:
-	/* Load segment registers */
-	movw	$__BOOT_DS, %ax
-	movw	%ax, %ss
-	movw	%ax, %ds
-	movw	%ax, %es
-	movw	%ax, %fs
-	movw	%ax, %gs
-
-	/* Use the internal etherboot stack */
-	movl	$(_prefix_stack_end - protected_start + 0x100000), %esp
-
-	pushl	$0		/* No parameters to preserve for exit path */
-	pushl	$0		/* Use prefix exit path mechanism */
-	
-	jmp	_start
-/*
-	That's about it.
-*/
diff --git a/gpxe/src/arch/i386/prefix/bootpart.S b/gpxe/src/arch/i386/prefix/bootpart.S
index d60fe9b..968da1a 100644
--- a/gpxe/src/arch/i386/prefix/bootpart.S
+++ b/gpxe/src/arch/i386/prefix/bootpart.S
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL2_OR_LATER )
+
 #define BOOT_SEG	0x07c0
 #define EXEC_SEG	0x0100
 #define STACK_SEG 	0x0200
diff --git a/gpxe/src/arch/i386/prefix/comprefix.S b/gpxe/src/arch/i386/prefix/comprefix.S
deleted file mode 100644
index 2cbbca5..0000000
--- a/gpxe/src/arch/i386/prefix/comprefix.S
+++ /dev/null
@@ -1,46 +0,0 @@
-/* We need a real mode stack that won't be stomped on by Etherboot
-   which starts at 0x20000. Choose something that's sufficiently high,
-   but not in DOC territory. Note that we couldn't do this in a real
-   .com program since stack variables are in the same segment as the
-   code and data, but this isn't really a .com program, it just looks
-   like one to make DOS load it into memory. It still has the 64kB
-   limitation of .com files though. */
-#define STACK_SEG	0x7000
-#define STACK_SIZE	0x4000
-
-	.text
-	.code16
-	.arch i386
-	.section ".prefix", "ax", @progbits
-
-/* Cheat a little with the relocations: .COM files are loaded at 0x100 */
-_prefix:
-	/* Set up temporary stack */ 
-	movw	$STACK_SEG, %ax
-	movw	%ax, %ss
-	movw	$STACK_SIZE, %sp
-
-	pushl	$0	/* No parameters to preserve for exit path */
-	pushw	$0	/* Dummy return address - use prefix_exit */
-
-	/* Calculate segment address of image start */
-	pushw	%cs
-	popw	%ax
-	addw	$(0x100/16), %ax
-	pushw	%ax
-	pushw	$_start
-	/* Calculated lcall to _start with %cs:0000 = image start */
-	lret
-
-	.section ".text16", "ax", @progbits
-prefix_exit:
-	movw	$0x4c00,%ax		/* return to DOS */
-	int	$0x21			/* reach this on Quit */
-prefix_exit_end:
-	.previous
-
-/* The body of etherboot is attached here at build time.
- * Force 16 byte alignment
- */
-	.align 16,0
-_body: 
diff --git a/gpxe/src/arch/i386/prefix/dskprefix.S b/gpxe/src/arch/i386/prefix/dskprefix.S
index 0156812..60d351f 100644
--- a/gpxe/src/arch/i386/prefix/dskprefix.S
+++ b/gpxe/src/arch/i386/prefix/dskprefix.S
@@ -16,6 +16,8 @@
  * getting whole tracks at a time whenever possible.
  */
 
+FILE_LICENCE ( GPL2_ONLY )
+
 .equ	BOOTSEG, 0x07C0			/* original address of boot-sector */
 
 .equ	SYSSEG, 0x1000			/* system loaded at SYSSEG<<4 */
@@ -144,9 +146,9 @@ got_sectors:
 	/* Jump to loaded copy */
 	ljmp	$SYSSEG, $start_runtime
 
-endseg:	.word SYSSEG + _filesz_pgh
+endseg:	.word SYSSEG
 	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
-	.ascii	"SUBW"
+	.ascii	"ADDW"
 	.long	endseg
 	.long	16
 	.long	0
diff --git a/gpxe/src/arch/i386/prefix/elf_dprefix.S b/gpxe/src/arch/i386/prefix/elf_dprefix.S
deleted file mode 100644
index 0eac77e..0000000
--- a/gpxe/src/arch/i386/prefix/elf_dprefix.S
+++ /dev/null
@@ -1,94 +0,0 @@
-#include "elf.h"
-
-	.arch i386
-	.section ".prefix", "a", @progbits
-
-#define LOAD_ADDR 0x10000
-
-	/* ELF Header */
-	.globl elf_header
-elf_header:	
-e_ident:	.byte	0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
-e_type:		.short	ET_DYN
-e_machine:	.short	EM_386
-e_version:	.long	1
-e_entry:	.long	LOAD_ADDR + _start - elf_header
-e_phoff:	.long	elf_program_header - elf_header
-e_shoff:	.long	0
-e_flags:	.long	0
-e_ehsize:	.short	elf_header_end - elf_header
-e_phentsize:	.short	ELF32_PHDR_SIZE
-e_phnum:	.short	(elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
-e_shentsize:	.short	0
-e_shnum:	.short	0
-e_shstrndx:	.short	0
-elf_header_end:
-	
-elf_program_header:
-phdr1_p_type:	.long	PT_NOTE
-phdr1_p_offset:	.long	elf_note - elf_header
-phdr1_p_vaddr:	.long	elf_note
-phdr1_p_paddr:	.long	elf_note
-phdr1_p_filesz:	.long	elf_note_end - elf_note
-phdr1_p_memsz:	.long	elf_note_end - elf_note
-phdr1_p_flags:	.long	PF_R | PF_W | PF_X
-phdr1_p_align:	.long	0
-
-/* The decompressor */
-phdr2_p_type:	.long	PT_LOAD
-phdr2_p_offset:	.long	0
-phdr2_p_vaddr:	.long	elf_header
-phdr2_p_paddr:	.long	LOAD_ADDR
-phdr2_p_filesz:	.long	_verbatim_size
-phdr2_p_memsz:	.long	_image_size
-phdr2_p_flags:	.long	PF_R | PF_W | PF_X
-phdr2_p_align:	.long	16
-
-elf_program_header_end:
-
-	.globl elf_note
-elf_note:
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_NAME
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.asciz "Etherboot"
-4:
-
-
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_VERSION
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.asciz VERSION
-4:
-
-#if 0
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_CHECKSUM
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.word 0
-4:
-#endif
-	.balign 4
-elf_note_end:
-
-	/* Dummy routines to satisfy the build */
-	.section ".text16", "ax", @progbits
-prefix_exit:
-
-prefix_exit_end:
-	.previous
diff --git a/gpxe/src/arch/i386/prefix/elfprefix.S b/gpxe/src/arch/i386/prefix/elfprefix.S
deleted file mode 100644
index d712753..0000000
--- a/gpxe/src/arch/i386/prefix/elfprefix.S
+++ /dev/null
@@ -1,94 +0,0 @@
-#include "elf.h"
-
-	.arch i386
-	.section ".prefix", "a", @progbits
-
-#define LOAD_ADDR 0x10000
-
-	/* ELF Header */
-	.globl elf_header
-elf_header:	
-e_ident:	.byte	0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
-e_type:		.short	ET_EXEC
-e_machine:	.short	EM_386
-e_version:	.long	1
-e_entry:	.long	LOAD_ADDR + _start - elf_header
-e_phoff:	.long	elf_program_header - elf_header
-e_shoff:	.long	0
-e_flags:	.long	0
-e_ehsize:	.short	elf_header_end - elf_header
-e_phentsize:	.short	ELF32_PHDR_SIZE
-e_phnum:	.short	(elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
-e_shentsize:	.short	0
-e_shnum:	.short	0
-e_shstrndx:	.short	0
-elf_header_end:
-	
-elf_program_header:
-phdr1_p_type:	.long	PT_NOTE
-phdr1_p_offset:	.long	elf_note - elf_header
-phdr1_p_vaddr:	.long	elf_note
-phdr1_p_paddr:	.long	elf_note
-phdr1_p_filesz:	.long	elf_note_end - elf_note
-phdr1_p_memsz:	.long	elf_note_end - elf_note
-phdr1_p_flags:	.long	PF_R | PF_W | PF_X
-phdr1_p_align:	.long	0
-
-/* The decompressor */
-phdr2_p_type:	.long	PT_LOAD
-phdr2_p_offset:	.long	0
-phdr2_p_vaddr:	.long	elf_header
-phdr2_p_paddr:	.long	LOAD_ADDR
-phdr2_p_filesz:	.long	_verbatim_size
-phdr2_p_memsz:	.long	_image_size
-phdr2_p_flags:	.long	PF_R | PF_W | PF_X
-phdr2_p_align:	.long	16
-
-elf_program_header_end:
-
-	.globl elf_note
-elf_note:
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_NAME
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.asciz "Etherboot"
-4:
-
-
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_VERSION
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.asciz VERSION
-4:
-
-#if 0
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_CHECKSUM
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.word 0
-4:
-#endif
-	.balign 4
-elf_note_end:
-
-	/* Dummy routines to satisfy the build */
-	.section ".text16", "ax", @progbits
-prefix_exit:
-
-prefix_exit_end:
-	.previous
diff --git a/gpxe/src/arch/i386/prefix/exeprefix.S b/gpxe/src/arch/i386/prefix/exeprefix.S
deleted file mode 100755
index f1b402b..0000000
--- a/gpxe/src/arch/i386/prefix/exeprefix.S
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-	Prefix for .exe images
-	Doesn't work yet, even though it starts off the same as a .com
-	image as shown by DOS debug.
-*/
-
-	.text
-	.code16
-	.arch i386
-	.section	".prefix", "ax", @progbits
-
-_prefix:
-	.byte	'M', 'Z'
-	.short	_exe_size_tail			/* tail */
-	.short	_exe_size_pages			/* pages */
-	.short	0				/* relocations */
-	.short	2				/* header paras */
-	.short	_exe_bss_size			/* min */
-	.short	0xFFFF				/* max paras */
-	.short	_exe_ss_offset			/* SS */
-	.short	_stack_size			/* SP */
-	.short	0				/* checksum */
-	.short	0				/* IP */
-	.short	0				/* CS */
-	.short	0x1C				/* reloc offset */
-	.short	0				/* overlay number */
-	.short	0				/* fill */
-	.short	0				/* fill */
-
-	.section ".text16", "ax", @progbits
-prefix_exit:
-	movw	$0x4c00,%ax		/* return to DOS */
-	int	$0x21			/* reach this on Quit */
-prefix_exit_end:
-	.previous
-
-/* The body of etherboot is attached here at build time.
- * Force 16 byte alignment
- */
-	.align 16,0
-_body: 
diff --git a/gpxe/src/arch/i386/prefix/hdprefix.S b/gpxe/src/arch/i386/prefix/hdprefix.S
index 086d7f4..0576756 100644
--- a/gpxe/src/arch/i386/prefix/hdprefix.S
+++ b/gpxe/src/arch/i386/prefix/hdprefix.S
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL2_OR_LATER )
+
 	.text
 	.arch i386
 	.section ".prefix", "awx", @progbits
@@ -63,10 +65,10 @@ max_sector:
 max_head:
 	.byte	0
 load_length:
-	.long	_filesz_sect
+	.long	0
 	
 	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
-	.ascii	"SUBL"
+	.ascii	"ADDL"
 	.long	load_length
 	.long	512
 	.long	0
diff --git a/gpxe/src/arch/i386/prefix/hromprefix.S b/gpxe/src/arch/i386/prefix/hromprefix.S
new file mode 100644
index 0000000..03acf1e
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/hromprefix.S
@@ -0,0 +1,12 @@
+/*****************************************************************************
+ * ROM prefix that relocates to HIGHMEM_LOADPOINT during POST if PMM allocation
+ * fails. Intended to be used, with caution, on BIOSes that support PCI3.00 but
+ * have limited PMM support, such as most AMI BIOSes.
+ *****************************************************************************
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER )
+
+#define SHRINK_WITHOUT_PMM
+
+#include "romprefix.S"
diff --git a/gpxe/src/arch/i386/prefix/kkpxeprefix.S b/gpxe/src/arch/i386/prefix/kkpxeprefix.S
index e0bea0c..02cc6fe 100644
--- a/gpxe/src/arch/i386/prefix/kkpxeprefix.S
+++ b/gpxe/src/arch/i386/prefix/kkpxeprefix.S
@@ -3,6 +3,11 @@
  *****************************************************************************
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
+/* Since we have the whole stack, we can use cached DHCP information */
+REQUEST_OBJECT ( pxeparent_dhcp )
+
 #define PXELOADER_KEEP_UNDI
 #define PXELOADER_KEEP_PXE
 #include "pxeprefix.S"
diff --git a/gpxe/src/arch/i386/prefix/kpxeprefix.S b/gpxe/src/arch/i386/prefix/kpxeprefix.S
index d708604..923facc 100644
--- a/gpxe/src/arch/i386/prefix/kpxeprefix.S
+++ b/gpxe/src/arch/i386/prefix/kpxeprefix.S
@@ -3,5 +3,7 @@
  *****************************************************************************
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 #define PXELOADER_KEEP_UNDI
 #include "pxeprefix.S"
diff --git a/gpxe/src/arch/i386/prefix/libprefix.S b/gpxe/src/arch/i386/prefix/libprefix.S
index 4218913..9e6ba6f 100644
--- a/gpxe/src/arch/i386/prefix/libprefix.S
+++ b/gpxe/src/arch/i386/prefix/libprefix.S
@@ -17,6 +17,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 	.arch i386
 
 /**
diff --git a/gpxe/src/arch/i386/prefix/lkrnprefix.S b/gpxe/src/arch/i386/prefix/lkrnprefix.S
index 094263d..101d038 100644
--- a/gpxe/src/arch/i386/prefix/lkrnprefix.S
+++ b/gpxe/src/arch/i386/prefix/lkrnprefix.S
@@ -34,6 +34,8 @@
 
 */
 
+FILE_LICENCE ( GPL_ANY )
+
 #define	SETUPSECS 4		/* Minimal nr of setup-sectors */
 #define PREFIXSIZE ((SETUPSECS+1)*512)
 #define PREFIXPGH (PREFIXSIZE / 16 )
@@ -92,10 +94,10 @@ setup_sects:
 root_flags: 
 	.word	0
 syssize: 
-	.long	_filesz_pgh - PREFIXPGH
+	.long	-PREFIXPGH
 
 	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
-	.ascii	"SUBL"
+	.ascii	"ADDL"
 	.long	syssize
 	.long	16
 	.long	0
diff --git a/gpxe/src/arch/i386/prefix/lmelf_dprefix.S b/gpxe/src/arch/i386/prefix/lmelf_dprefix.S
deleted file mode 100644
index 93534df..0000000
--- a/gpxe/src/arch/i386/prefix/lmelf_dprefix.S
+++ /dev/null
@@ -1,161 +0,0 @@
-#include "elf.h"
-	.arch sledgehammer
-	.code32
-	.equ	FLAT_CODE_SEG,_pmcs-_gdt
-	.equ	FLAT_DATA_SEG,_pmds-_gdt
-	.equ	MSR_K6_EFER,   0xC0000080
-	.equ	EFER_LME,      0x00000100
-	.equ	X86_CR4_PAE,   0x00000020
-	.equ	CR0_PG,        0x80000000
-
-	.section ".prefix", "ax", @progbits
-
-#define LOAD_ADDR 0x10000
-		
-	/* ELF Header */
-	.globl elf_header
-elf_header:	
-e_ident:	.byte	0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
-e_type:		.short	ET_DYN
-e_machine:	.short	EM_X86_64
-e_version:	.long	1
-e_entry:	.long	LOAD_ADDR + elf_start - elf_header
-e_phoff:	.long	elf_program_header - elf_header
-e_shoff:	.long	0
-e_flags:	.long	0
-e_ehsize:	.short	elf_header_end - elf_header
-e_phentsize:	.short	ELF32_PHDR_SIZE
-e_phnum:	.short	(elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
-e_shentsize:	.short	0
-e_shnum:	.short	0
-e_shstrndx:	.short	0
-elf_header_end:
-	
-elf_program_header:
-phdr1_p_type:	.long	PT_NOTE
-phdr1_p_offset:	.long	elf_note - elf_header
-phdr1_p_vaddr:	.long	elf_note
-phdr1_p_paddr:	.long	elf_note
-phdr1_p_filesz:	.long	elf_note_end - elf_note
-phdr1_p_memsz:	.long	elf_note_end - elf_note
-phdr1_p_flags:	.long	PF_R | PF_W | PF_X
-phdr1_p_align:	.long	0
-
-/* The decompressor */
-phdr2_p_type:	.long	PT_LOAD
-phdr2_p_offset:	.long	0
-phdr2_p_vaddr:	.long	elf_header
-phdr2_p_paddr:	.long	LOAD_ADDR
-phdr2_p_filesz:	.long	_verbatim_size
-phdr2_p_memsz:	.long	_image_size
-phdr2_p_flags:	.long	PF_R | PF_W | PF_X
-phdr2_p_align:	.long	16
-
-elf_program_header_end:
-
-	.globl elf_note
-elf_note:
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_NAME
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.asciz "Etherboot"
-4:
-
-
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_VERSION
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.asciz VERSION
-4:
-
-#if 0
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_CHECKSUM
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.word 0
-4:
-#endif
-	.balign 4
-elf_note_end:
-
-elf_start:
-	.code64
-	/* Reload the gdt to something I know */
-	leaq	_gdt(%rip), %rax
-	movq	%rax, 0x02 + gdtptr(%rip)
-	lgdt	gdtptr(%rip)
-
-	/* Enter 32bit compatibility mode */
-	leaq	elf_start32(%rip), %rax
-	movl	%eax, 0x00 + elf_start32_addr(%rip)
-	ljmp	*elf_start32_addr(%rip)
-
-elf_start32:	
-	.code32
-	/* Reload the data segments */
-	movl	$FLAT_DATA_SEG, %eax
-	movl	%eax, %ds
-	movl	%eax, %es
-	movl	%eax, %ss
-	
-	/* Disable paging */
-	movl	%cr0, %eax
-	andl	$~CR0_PG, %eax
-	movl	%eax, %cr0
-
-	/* Disable long mode */
-	movl	$MSR_K6_EFER, %ecx
-	rdmsr
-	andl	$~EFER_LME, %eax
-	wrmsr
-
-	/* Disable PAE */
-	movl	%cr4, %eax
-	andl	$~X86_CR4_PAE, %eax
-	movl	%eax, %cr4
-
-	/* Save the first argument */
-	pushl	%ebx
-	jmp	_start
-
-gdtptr:	
-	.word	_gdt_end - _gdt -1
-	.long	_gdt
-	.long	0
-_gdt:
-elf_start32_addr:
-	.long	elf_start32
-	.long	FLAT_CODE_SEG
-_pmcs:
-	/* 32 bit protected mode code segment, base 0 */
-	.word	0xffff,0
-	.byte	0,0x9f,0xcf,0
-
-_pmds:
-	/* 32 bit protected mode data segment, base 0 */
-	.word	0xffff,0
-	.byte	0,0x93,0xcf,0
-_gdt_end:	
-
-
-	/* Dummy routines to satisfy the build */
-	.section ".text16", "ax", @progbits
-prefix_exit:
-
-prefix_exit_end:
-	.previous
diff --git a/gpxe/src/arch/i386/prefix/lmelf_prefix.S b/gpxe/src/arch/i386/prefix/lmelf_prefix.S
deleted file mode 100644
index 3c1e725..0000000
--- a/gpxe/src/arch/i386/prefix/lmelf_prefix.S
+++ /dev/null
@@ -1,161 +0,0 @@
-#include "elf.h"
-	.arch sledgehammer
-	.code32
-	.equ	FLAT_CODE_SEG,_pmcs-_gdt
-	.equ	FLAT_DATA_SEG,_pmds-_gdt
-	.equ	MSR_K6_EFER,   0xC0000080
-	.equ	EFER_LME,      0x00000100
-	.equ	X86_CR4_PAE,   0x00000020
-	.equ	CR0_PG,        0x80000000
-
-	.section ".prefix", "ax", @progbits
-
-#define LOAD_ADDR 0x10000
-		
-	/* ELF Header */
-	.globl elf_header
-elf_header:	
-e_ident:	.byte	0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
-e_type:		.short	ET_EXEC
-e_machine:	.short	EM_X86_64
-e_version:	.long	1
-e_entry:	.long	LOAD_ADDR + elf_start - elf_header
-e_phoff:	.long	elf_program_header - elf_header
-e_shoff:	.long	0
-e_flags:	.long	0
-e_ehsize:	.short	elf_header_end - elf_header
-e_phentsize:	.short	ELF32_PHDR_SIZE
-e_phnum:	.short	(elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
-e_shentsize:	.short	0
-e_shnum:	.short	0
-e_shstrndx:	.short	0
-elf_header_end:
-	
-elf_program_header:
-phdr1_p_type:	.long	PT_NOTE
-phdr1_p_offset:	.long	elf_note - elf_header
-phdr1_p_vaddr:	.long	elf_note
-phdr1_p_paddr:	.long	elf_note
-phdr1_p_filesz:	.long	elf_note_end - elf_note
-phdr1_p_memsz:	.long	elf_note_end - elf_note
-phdr1_p_flags:	.long	PF_R | PF_W | PF_X
-phdr1_p_align:	.long	0
-
-/* The decompressor */
-phdr2_p_type:	.long	PT_LOAD
-phdr2_p_offset:	.long	0
-phdr2_p_vaddr:	.long	elf_header
-phdr2_p_paddr:	.long	LOAD_ADDR
-phdr2_p_filesz:	.long	_verbatim_size
-phdr2_p_memsz:	.long	_image_size
-phdr2_p_flags:	.long	PF_R | PF_W | PF_X
-phdr2_p_align:	.long	16
-
-elf_program_header_end:
-
-	.globl elf_note
-elf_note:
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_NAME
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.asciz "Etherboot"
-4:
-
-
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_VERSION
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.asciz VERSION
-4:
-
-#if 0
-	.balign 4
-	.int 2f - 1f
-	.int 4f - 3f
-	.int EIN_PROGRAM_CHECKSUM
-1:	.asciz "ELFBoot"
-2:
-	.balign 4
-3:	
-	.word 0
-4:
-#endif
-	.balign 4
-elf_note_end:
-
-elf_start:
-	.code64
-	/* Reload the gdt to something I know */
-	leaq	_gdt(%rip), %rax
-	movq	%rax, 0x02 + gdtptr(%rip)
-	lgdt	gdtptr(%rip)
-
-	/* Enter 32bit compatibility mode */
-	leaq	elf_start32(%rip), %rax
-	movl	%eax, 0x00 + elf_start32_addr(%rip)
-	ljmp	*elf_start32_addr(%rip)
-
-elf_start32:	
-	.code32
-	/* Reload the data segments */
-	movl	$FLAT_DATA_SEG, %eax
-	movl	%eax, %ds
-	movl	%eax, %es
-	movl	%eax, %ss
-	
-	/* Disable paging */
-	movl	%cr0, %eax
-	andl	$~CR0_PG, %eax
-	movl	%eax, %cr0
-
-	/* Disable long mode */
-	movl	$MSR_K6_EFER, %ecx
-	rdmsr
-	andl	$~EFER_LME, %eax
-	wrmsr
-
-	/* Disable PAE */
-	movl	%cr4, %eax
-	andl	$~X86_CR4_PAE, %eax
-	movl	%eax, %cr4
-
-	/* Save the first argument */
-	pushl	%ebx
-	jmp	_start
-
-gdtptr:	
-	.word	_gdt_end - _gdt -1
-	.long	_gdt
-	.long	0
-_gdt:
-elf_start32_addr:
-	.long	elf_start32
-	.long	FLAT_CODE_SEG
-_pmcs:
-	/* 32 bit protected mode code segment, base 0 */
-	.word	0xffff,0
-	.byte	0,0x9f,0xcf,0
-
-_pmds:
-	/* 32 bit protected mode data segment, base 0 */
-	.word	0xffff,0
-	.byte	0,0x93,0xcf,0
-_gdt_end:	
-
-
-	/* Dummy routines to satisfy the build */
-	.section ".text16", "ax", @progbits
-prefix_exit:
-
-prefix_exit_end:
-	.previous
diff --git a/gpxe/src/arch/i386/prefix/nbiprefix.S b/gpxe/src/arch/i386/prefix/nbiprefix.S
index 4fb4acb..607d80f 100644
--- a/gpxe/src/arch/i386/prefix/nbiprefix.S
+++ b/gpxe/src/arch/i386/prefix/nbiprefix.S
@@ -30,16 +30,16 @@ segment_header:
 	.byte	0
 	.byte	0x04		/* Last segment */
 	.long	0x00007e00
-imglen:	.long	_filesz - 512
-memlen:	.long	_filesz - 512
+imglen:	.long	-512
+memlen:	.long	-512
 	.size	segment_header, . - segment_header
 
 	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
-	.ascii	"SUBL"
+	.ascii	"ADDL"
 	.long	imglen
 	.long	1
 	.long	0
-	.ascii	"SUBL"
+	.ascii	"ADDL"
 	.long	memlen
 	.long	1
 	.long	0
diff --git a/gpxe/src/arch/i386/prefix/pxeprefix.S b/gpxe/src/arch/i386/prefix/pxeprefix.S
index b3b7947..e728c48 100644
--- a/gpxe/src/arch/i386/prefix/pxeprefix.S
+++ b/gpxe/src/arch/i386/prefix/pxeprefix.S
@@ -1,8 +1,13 @@
+FILE_LICENCE ( GPL2_OR_LATER )
+
 #define PXENV_UNDI_SHUTDOWN		0x0005
 #define	PXENV_UNDI_GET_NIC_TYPE		0x0012
+#define PXENV_UNDI_GET_IFACE_INFO	0x0013
 #define	PXENV_STOP_UNDI			0x0015
 #define PXENV_UNLOAD_STACK		0x0070
 
+#define PXE_HACK_EB54			0x0001
+
 	.text
 	.arch i386
 	.org 0
@@ -11,6 +16,8 @@
 #include <undi.h>
 
 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
+#define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
+#define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
 
 /*****************************************************************************
  * Entry point:	set operating context, print welcome message
@@ -105,20 +112,13 @@ have_pxenv:
 
 	/* Record entry point and UNDI segments */
 	pushl	%es:0x0a(%bx)		/* Entry point */
-	popl	entry_segoff
 	pushw	%es:0x24(%bx)		/* UNDI code segment */
 	pushw	%es:0x26(%bx)		/* UNDI code size */
-	popl	undi_code_segoff
 	pushw	%es:0x20(%bx)		/* UNDI data segment */
 	pushw	%es:0x22(%bx)		/* UNDI data size */
-	popl	undi_data_segoff
 
 	/* Print "PXENV+ at <address>" */
 	movw	$10f, %si
-	call	print_message
-	call	print_segoff
-	movb	$( ',' ), %al
-	call	print_character
 	jmp	check_have_stack
 	.section ".prefix.data", "aw", @progbits
 10:	.asciz	" PXENV+ at "
@@ -129,19 +129,13 @@ have_ppxe:
 	movw	%es, ppxe_segment
 	
 	pushl	%es:0x10(%bx)		/* Entry point */
-	popl	entry_segoff
 	pushw	%es:0x30(%bx)		/* UNDI code segment */
 	pushw	%es:0x36(%bx)		/* UNDI code size */
-	popl	undi_code_segoff
 	pushw	%es:0x28(%bx)		/* UNDI data segment */
 	pushw	%es:0x2e(%bx)		/* UNDI data size */
-	popl	undi_data_segoff
+
 	/* Print "!PXE at <address>" */
 	movw	$10f, %si
-	call	print_message
-	call	print_segoff
-	movb	$( ',' ), %al
-	call	print_character
 	jmp	check_have_stack
 	.section ".prefix.data", "aw", @progbits
 10:	.asciz	" !PXE at "
@@ -201,6 +195,17 @@ memory_scan_common:
  *****************************************************************************
  */
 check_have_stack:
+	/* Save common values pushed onto the stack */
+	popl	undi_data_segoff
+	popl	undi_code_segoff
+	popl	entry_segoff
+
+	/* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */
+	call	print_message
+	call	print_segoff
+	movb	$( ',' ), %al
+	call	print_character
+
 	/* Check for entry point */
 	movl	entry_segoff, %eax
 	testl	%eax, %eax
@@ -309,8 +314,6 @@ pci_physical_device:
 	movw	$10f, %si
 	call	print_message
 	call	print_pci_busdevfn
-	movb	$0x0a, %al
-	call	print_character
 	jmp	99f
 	.section ".prefix.data", "aw", @progbits
 10:	.asciz	"         UNDI device is PCI "
@@ -321,12 +324,47 @@ no_physical_device:
 	movw	$10f, %si
 	call	print_message
 	.section ".prefix.data", "aw", @progbits
-10:	.asciz	"         Unable to determine UNDI physical device\n"
+10:	.asciz	"         Unable to determine UNDI physical device"
 	.previous
 
 99:
 
 /*****************************************************************************
+ * Determine interface type
+ *****************************************************************************
+ */
+get_iface_type:
+	/* Issue PXENV_UNDI_GET_IFACE_INFO */
+	movw	$PXENV_UNDI_GET_IFACE_INFO, %bx
+	call	pxe_call
+	jnc	1f
+	call	print_pxe_error
+	jmp	99f
+1:	/* Print interface type */
+	movw	$10f, %si
+	call	print_message
+	leaw	( pxe_parameter_structure + 0x02 ), %si
+	call	print_message
+	.section ".prefix.data", "aw", @progbits
+10:	.asciz	", type "
+	.previous
+	/* Check for "Etherboot" interface type */
+	cmpl	$EB_MAGIC_1, ( pxe_parameter_structure + 0x02 )
+	jne	99f
+	cmpl	$EB_MAGIC_2, ( pxe_parameter_structure + 0x06 )
+	jne	99f
+	movw	$10f, %si
+	call	print_message
+	.section ".prefix.data", "aw", @progbits
+10:	.asciz	" (workaround enabled)"
+	.previous
+	/* Flag Etherboot workarounds as required */
+	orw	$PXE_HACK_EB54, pxe_hacks
+
+99:	movb	$0x0a, %al
+	call	print_character
+
+/*****************************************************************************
  * Leave NIC in a safe state
  *****************************************************************************
  */
@@ -339,6 +377,14 @@ shutdown_nic:
 	call	print_pxe_error
 1:
 unload_base_code:
+	/* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so
+	 * we must not issue this call if the underlying stack is
+	 * Etherboot and we were not intending to issue a PXENV_STOP_UNDI.
+	 */
+#ifdef PXELOADER_KEEP_UNDI
+	testw	$PXE_HACK_EB54, pxe_hacks
+	jnz	99f
+#endif /* PXELOADER_KEEP_UNDI */
 	/* Issue PXENV_UNLOAD_STACK */
 	movw	$PXENV_UNLOAD_STACK, %bx
 	call	pxe_call
@@ -551,7 +597,9 @@ pxe_call:
 	testw	%ax, %ax
 	jz	1f
 	stc
-1:	/* Restore registers and return */
+1:	/* Clear direction flag, for the sake of sanity */
+	cld
+	/* Restore registers and return */
 	popw	%es
 	popw	%di
 	ret
@@ -595,7 +643,7 @@ print_pxe_error:
 pxe_esp:		.long 0
 pxe_ss:			.word 0
 
-pxe_parameter_structure: .fill 20
+pxe_parameter_structure: .fill 64
 
 undi_code_segoff:
 undi_code_size:		.word 0
@@ -605,6 +653,8 @@ undi_data_segoff:
 undi_data_size:		.word 0
 undi_data_segment:	.word 0
 
+pxe_hacks:		.word 0
+
 /* The following fields are part of a struct undi_device */
 
 undi_device:
@@ -668,6 +718,13 @@ run_gpxe:
 	lret
 	.section ".text16", "ax", @progbits
 1:
+	/* Update the exit hook */
+	movw	%cs,pxe_exit_hook+2
+	push	%ax
+	mov	$2f,%ax
+	mov	%ax,pxe_exit_hook
+	pop	%ax
+
 	/* Run main program */
 	pushl	$main
 	pushw	%cs
@@ -681,7 +738,10 @@ run_gpxe:
 	movw	%di, %ss
 	movl	%ebp, %esp
 
-	/* Check PXE stack magic */
+	/* Jump to hook if applicable */
+	ljmpw	*pxe_exit_hook
+
+2:	/* Check PXE stack magic */
 	popl	%eax
 	cmpl	$STACK_MAGIC, %eax
 	jne	1f
diff --git a/gpxe/src/arch/i386/prefix/romprefix.S b/gpxe/src/arch/i386/prefix/romprefix.S
index 7d53237..02e5497 100644
--- a/gpxe/src/arch/i386/prefix/romprefix.S
+++ b/gpxe/src/arch/i386/prefix/romprefix.S
@@ -6,6 +6,8 @@
  * table so using a noticeable amount of stack space is a no-no.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 #include <config/general.h>
 
 #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
@@ -23,6 +25,19 @@
  */
 #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
 
+/* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix)
+ * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix).
+ * The latter is not as widely supported, but allows the use of large ROMs
+ * on some systems with crowded option ROM space.
+ */
+
+#ifdef LOAD_ROM_FROM_PCI
+#define ROM_SIZE_VALUE	_prefix_filesz_sect /* Amount to load in BIOS */
+#else
+#define ROM_SIZE_VALUE	0		/* Load amount (before compr. fixup) */
+#endif
+
+
 	.text
 	.code16
 	.arch i386
@@ -31,10 +46,12 @@
 	.org	0x00
 romheader:
 	.word	0xAA55			/* BIOS extension signature */
-romheader_size:	.byte _filesz_sect	/* Size in 512-byte blocks */
+romheader_size:	.byte ROM_SIZE_VALUE	/* Size in 512-byte blocks */
 	jmp	init			/* Initialisation vector */
 checksum:
-	.byte	0
+	.byte	0, 0
+real_size:
+	.word	0
 	.org	0x16
 	.word	undiheader
 	.org	0x18
@@ -42,12 +59,18 @@ checksum:
 	.org	0x1a
 	.word	pnpheader
 	.size romheader, . - romheader
-	
+
 	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
-	.ascii	"SUBB"
+#ifndef LOAD_ROM_FROM_PCI
+	.ascii	"ADDB"
 	.long	romheader_size
 	.long	512
 	.long	0
+#endif
+	.ascii	"ADDB"
+	.long	real_size
+	.long	512
+	.long	0
 	.previous
 
 pciheader:
@@ -59,27 +82,29 @@ pciheader:
 	.byte	0x03			/* PCI data structure revision */
 	.byte	0x02, 0x00, 0x00	/* Class code */
 pciheader_image_length:
-	.word	_filesz_sect		/* Image length */
+	.word	ROM_SIZE_VALUE		/* Image length */
 	.word	0x0001			/* Revision level */
 	.byte	0x00			/* Code type */
 	.byte	0x80			/* Last image indicator */
 pciheader_runtime_length:
-	.word	_filesz_sect		/* Maximum run-time image length */
+	.word	ROM_SIZE_VALUE		/* Maximum run-time image length */
 	.word	0x0000			/* Configuration utility code header */
 	.word	0x0000			/* DMTF CLP entry point */
 	.equ pciheader_len, . - pciheader
 	.size pciheader, . - pciheader
-	
+
+#ifndef LOAD_ROM_FROM_PCI
 	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
-	.ascii	"SUBW"
+	.ascii	"ADDW"
 	.long	pciheader_image_length
 	.long	512
 	.long	0
-	.ascii	"SUBW"
+	.ascii	"ADDW"
 	.long	pciheader_runtime_length
 	.long	512
 	.long	0
 	.previous
+#endif
 
 pnpheader:
 	.ascii	"$PnP"			/* Signature */
@@ -124,6 +149,7 @@ prodstr_pci_id:
 	.size prodstr, . - prodstr
 
 	.globl	undiheader	
+	.weak	undiloader
 undiheader:
 	.ascii	"UNDI"			/* Signature */
 	.byte	undiheader_len		/* Length of structure */
@@ -172,6 +198,11 @@ init:
 	call	print_message
 	call	print_pci_busdevfn
 
+#ifdef LOAD_ROM_FROM_PCI
+	/* Save PCI bus:dev.fn for later use */
+	movw	%ax, pci_busdevfn
+#endif
+
 	/* Fill in product name string, if possible */
 	movw	$prodstr_pci_id, %di
 	call	print_pci_busdevfn
@@ -196,6 +227,9 @@ init:
 	jne	no_pci3
 	testb	%ah, %ah
 	jnz	no_pci3
+#ifdef LOAD_ROM_FROM_PCI
+	incb	pcibios_present
+#endif
 	movw	$init_message_pci, %si
 	xorw	%di, %di
 	call	print_message
@@ -236,24 +270,37 @@ no_pci3:
 	popl	%edx
 	popl	%ebx
 
-	/* Check for PnP BIOS */
-	testw	$0x0f, %bx	/* PnP signature must be aligned - bochs    */
-	jnz	no_bbs		/* uses unalignment to indicate 'fake' PnP. */
-	cmpl	$PNP_SIGNATURE, %es:0(%bx)
-	jne	no_bbs
+	/* Check for PnP BIOS.  Although %es:di should point to the
+	 * PnP BIOS signature on entry, some BIOSes fail to do this.
+	 */
+	movw	$( 0xf000 - 1 ), %bx
+pnp_scan:
+	incw	%bx
+	jz	no_pnp
+	movw	%bx, %es
+	cmpl	$PNP_SIGNATURE, %es:0
+	jne	pnp_scan
+	xorw	%dx, %dx
+	xorw	%si, %si
+	movzbw	%es:5, %cx
+1:	es lodsb
+	addb	%al, %dl
+	loop	1b
+	jnz	pnp_scan
 	/* Is PnP: print PnP message */
 	movw	$init_message_pnp, %si
 	xorw	%di, %di
 	call	print_message
 	/* Check for BBS */
-	pushw	%es:0x1b(%bx)	/* Real-mode data segment */
+	pushw	%es:0x1b	/* Real-mode data segment */
 	pushw	%ds		/* &(bbs_version) */
 	pushw	$bbs_version
 	pushw	$PNP_GET_BBS_VERSION
-	lcall	*%es:0xd(%bx)
+	lcall	*%es:0xd
 	addw	$8, %sp
 	testw	%ax, %ax
 	je	got_bbs
+no_pnp:	/* Not PnP-compliant - therefore cannot be BBS-compliant */
 no_bbs:	/* Not BBS-compliant - must hook INT 19 */
 	movw	$init_message_int19, %si
 	xorw	%di, %di
@@ -294,7 +341,7 @@ pmm_scan:
 	/* We have PMM and so a 1kB stack: preserve upper register halves */
 	pushal
 	/* Calculate required allocation size in %esi */
-	movzbl	romheader_size, %eax
+	movzwl	real_size, %eax
 	shll	$9, %eax
 	addl	$_textdata_memsz, %eax
 	orw	$0xffff, %ax	/* Ensure allocation size is at least 64kB */
@@ -336,6 +383,7 @@ got_pmm: /* PMM allocation succeeded */
 	call	print_character
 	movw	%si, %ax
 	call	print_hex_byte
+pmm_copy:
 	/* Copy ROM to PMM block */
 	xorw	%ax, %ax
 	movw	%ax, %es
@@ -347,11 +395,43 @@ got_pmm: /* PMM allocation succeeded */
 	movl	%edi, decompress_to
 	/* Shrink ROM */
 	movb	$_prefix_memsz_sect, romheader_size
+#if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI)
+	jmp	pmm_done
+pmm_fail:
+	/* Print marker and copy ourselves to high memory */
+	movl	$HIGHMEM_LOADPOINT, image_source
+	xorw	%di, %di
+	movb	$( '!' ), %al
+	call	print_character
+	jmp	pmm_copy
+pmm_done:
+#else
 pmm_fail:
+#endif
 	/* Restore upper register halves */
 	popal
+#if defined(LOAD_ROM_FROM_PCI)
+	call	load_from_pci
+	jc	load_err
+	jmp	load_ok
 no_pmm:
+	/* Cannot continue without PMM - print error message */
+	xorw	%di, %di
+	movw	$init_message_no_pmm, %si
+	call	print_message
+load_err:
+	/* Wait for five seconds to let user see message */
+	movw	$90, %cx
+1:	call	wait_for_tick
+	loop	1b
+	/* Mark environment as invalid and return */
+	movl	$0, decompress_to
+	jmp	out
 
+load_ok:
+#else
+no_pmm:
+#endif
 	/* Update checksum */
 	xorw	%bx, %bx
 	xorw	%si, %si
@@ -396,14 +476,14 @@ no_pmm:
 	movw	$init_message_done, %si
 	call	print_message
 	popf
-	jnz	2f
+	jnz	out
 	/* Ctrl-B was pressed: invoke gPXE.  The keypress will be
 	 * picked up by the initial shell prompt, and we will drop
 	 * into a shell.
 	 */
 	pushw	%cs
 	call	exec
-2:
+out:
 	/* Restore registers */
 	popw	%gs
 	popw	%fs
@@ -450,6 +530,11 @@ init_message_bbs:
 init_message_pmm:
 	.asciz	" PMM"
 	.size	init_message_pmm, . - init_message_pmm
+#ifdef LOAD_ROM_FROM_PCI
+init_message_no_pmm:
+	.asciz	"\nPMM required but not present!\n"
+	.size	init_message_no_pmm, . - init_message_no_pmm
+#endif
 init_message_int19:
 	.asciz	" INT19"
 	.size	init_message_int19, . - init_message_int19
@@ -467,6 +552,7 @@ init_message_done:
  *
  * May be either within option ROM space, or within PMM-allocated block.
  */
+	.globl	image_source
 image_source:
 	.long	0
 	.size	image_source, . - image_source
@@ -474,11 +560,32 @@ image_source:
 /* Temporary decompression area
  *
  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
+ * If a PCI ROM load fails, this will be set to zero.
  */
+	.globl	decompress_to
 decompress_to:
 	.long	HIGHMEM_LOADPOINT
 	.size	decompress_to, . - decompress_to
 
+#ifdef LOAD_ROM_FROM_PCI
+
+/* Set if the PCI BIOS is present, even <3.0 */
+pcibios_present:
+	.byte	0
+	.byte	0		/* for alignment */
+	.size	pcibios_present, . - pcibios_present
+
+/* PCI bus:device.function word
+ *
+ * Filled in by init in the .xrom case, so the remainder of the ROM
+ * can be located.
+ */
+pci_busdevfn:
+	.word	0
+	.size	pci_busdevfn, . - pci_busdevfn
+
+#endif
+
 /* BBS version
  *
  * Filled in by BBS BIOS.  We ignore the value.
@@ -497,6 +604,289 @@ bev_entry:
 	lret
 	.size	bev_entry, . - bev_entry
 
+
+#ifdef LOAD_ROM_FROM_PCI
+
+#define PCI_ROM_ADDRESS		0x30	/* Bits 31:11 address, 10:1 reserved */
+#define PCI_ROM_ADDRESS_ENABLE	 0x00000001
+#define PCI_ROM_ADDRESS_MASK	 0xfffff800
+
+#define PCIBIOS_READ_WORD	0xb109
+#define PCIBIOS_READ_DWORD	0xb10a
+#define PCIBIOS_WRITE_WORD	0xb10c
+#define PCIBIOS_WRITE_DWORD	0xb10d
+
+/* Determine size of PCI BAR
+ *
+ *  %bx : PCI bus:dev.fn to probe
+ *  %di : Address of BAR to find size of
+ * %edx : Mask of address bits within BAR
+ *
+ * %ecx : Size for a memory resource,
+ *	  1 for an I/O resource (bit 0 set).
+ *   CF : Set on error or nonexistent device (all-ones read)
+ *
+ * All other registers saved.
+ */
+pci_bar_size:
+	/* Save registers */
+	pushw	%ax
+	pushl	%esi
+	pushl	%edx
+
+	/* Read current BAR value */
+	movw	$PCIBIOS_READ_DWORD, %ax
+	int	$0x1a
+
+	/* Check for device existence and save it */
+	testb	$1, %cl		/* I/O bit? */
+	jz	1f
+	andl	$1, %ecx	/* If so, exit with %ecx = 1 */
+	jmp	99f
+1:	notl	%ecx
+	testl	%ecx, %ecx	/* Set ZF iff %ecx was all-ones */
+	notl	%ecx
+	jnz	1f
+	stc			/* All ones - exit with CF set */
+	jmp	99f
+1:	movl	%ecx, %esi	/* Save in %esi */
+
+	/* Write all ones to BAR */
+	movl	%edx, %ecx
+	movw	$PCIBIOS_WRITE_DWORD, %ax
+	int	$0x1a
+
+	/* Read back BAR */
+	movw	$PCIBIOS_READ_DWORD, %ax
+	int	$0x1a
+
+	/* Find decode size from least set bit in mask BAR */
+	bsfl	%ecx, %ecx	/* Find least set bit, log2(decode size) */
+	jz	1f		/* Mask BAR should not be zero */
+	xorl	%edx, %edx
+	incl	%edx
+	shll	%cl, %edx	/* %edx = decode size */
+	jmp	2f
+1:	xorl	%edx, %edx	/* Return zero size for mask BAR zero */
+
+	/* Restore old BAR value */
+2:	movl	%esi, %ecx
+	movw	$PCIBIOS_WRITE_DWORD, %ax
+	int	$0x1a
+
+	movl	%edx, %ecx	/* Return size in %ecx */
+
+	/* Restore registers and return */
+99:	popl	%edx
+	popl	%esi
+	popw	%ax
+	ret
+
+	.size	pci_bar_size, . - pci_bar_size
+
+/* PCI ROM loader
+ *
+ * Called from init in the .xrom case to load the non-prefix code
+ * using the PCI ROM BAR.
+ *
+ * Returns with carry flag set on error. All registers saved.
+ */
+load_from_pci:
+	/*
+	 * Use PCI BIOS access to config space. The calls take
+	 *
+	 *   %ah : 0xb1		%al : function
+	 *   %bx : bus/dev/fn
+	 *   %di : config space address
+	 *  %ecx : value to write (for writes)
+	 *
+	 *  %ecx : value read (for reads)
+	 *   %ah : return code
+	 *    CF : error indication
+	 *
+	 * All registers not used for return are preserved.
+	 */
+
+	/* Save registers and set up %es for big real mode */
+	pushal
+	pushw	%es
+	xorw	%ax, %ax
+	movw	%ax, %es
+
+	/* Check PCI BIOS presence */
+	cmpb	$0, pcibios_present
+	jz	err_pcibios
+
+	/* Load existing PCI ROM BAR */
+	movw	$PCIBIOS_READ_DWORD, %ax
+	movw	pci_busdevfn, %bx
+	movw	$PCI_ROM_ADDRESS, %di
+	int	$0x1a
+
+	/* Maybe it's already enabled? */
+	testb	$PCI_ROM_ADDRESS_ENABLE, %cl
+	jz	1f
+	movb	$1, %dl		/* Flag indicating no deinit required */
+	movl	%ecx, %ebp
+	jmp	check_rom
+
+	/* Determine PCI BAR decode size */
+1:	movl	$PCI_ROM_ADDRESS_MASK, %edx
+	call	pci_bar_size	/* Returns decode size in %ecx */
+	jc	err_size_insane	/* CF => no ROM BAR, %ecx == ffffffff */
+
+	/* Check sanity of decode size */
+	xorl	%eax, %eax
+	movw	real_size, %ax
+	shll	$9, %eax	/* %eax = ROM size */
+	cmpl	%ecx, %eax
+	ja	err_size_insane	/* Insane if decode size < ROM size */
+	cmpl	$0x100000, %ecx
+	jae	err_size_insane	/* Insane if decode size >= 1MB */
+
+	/* Find a place to map the BAR
+	 * In theory we should examine e820 and all PCI BARs to find a
+	 * free region. However, we run at POST when e820 may not be
+	 * available, and memory reads of an unmapped location are
+	 * de facto standardized to return all-ones. Thus, we can get
+	 * away with searching high memory (0xf0000000 and up) on
+	 * multiples of the ROM BAR decode size for a sufficiently
+	 * large all-ones region.
+	 */
+	movl	%ecx, %edx	/* Save ROM BAR size in %edx */
+	movl	$0xf0000000, %ebp
+	xorl	%eax, %eax
+	notl	%eax		/* %eax = all ones */
+bar_search:
+	movl	%ebp, %edi
+	movl	%edx, %ecx
+	shrl	$2, %ecx
+	addr32 repe scasl	/* Scan %es:edi for anything not all-ones */
+	jz	bar_found
+	addl	%edx, %ebp
+	testl	$0x80000000, %ebp
+	jz	err_no_bar
+	jmp	bar_search
+
+bar_found:
+	movl	%edi, %ebp
+	/* Save current BAR value on stack to restore later */
+	movw	$PCIBIOS_READ_DWORD, %ax
+	movw	$PCI_ROM_ADDRESS, %di
+	int	$0x1a
+	pushl	%ecx
+
+	/* Map the ROM */
+	movw	$PCIBIOS_WRITE_DWORD, %ax
+	movl	%ebp, %ecx
+	orb	$PCI_ROM_ADDRESS_ENABLE, %cl
+	int	$0x1a
+
+	xorb	%dl, %dl	/* %dl = 0 : ROM was not already mapped */
+check_rom:
+	/* Check and copy ROM - enter with %dl set to skip unmapping,
+	 * %ebp set to mapped ROM BAR address.
+	 * We check up to prodstr_separator for equality, since anything past
+	 * that may have been modified. Since our check includes the checksum
+	 * byte over the whole ROM stub, that should be sufficient.
+	 */
+	xorb	%dh, %dh	/* %dh = 0 : ROM did not fail integrity check */
+
+	/* Verify ROM integrity */
+	xorl	%esi, %esi
+	movl	%ebp, %edi
+	movl	$prodstr_separator, %ecx
+	addr32 repe cmpsb
+	jz	copy_rom
+	incb	%dh		/* ROM failed integrity check */
+	movl	%ecx, %ebp	/* Save number of bytes left */
+	jmp	skip_load
+
+copy_rom:
+	/* Print BAR address and indicate whether we mapped it ourselves */
+	movb	$( ' ' ), %al
+	xorw	%di, %di
+	call	print_character
+	movl	%ebp, %eax
+	call	print_hex_dword
+	movb	$( '-' ), %al	/* '-' for self-mapped */
+	subb	%dl, %al
+	subb	%dl, %al	/* '+' = '-' - 2 for BIOS-mapped */
+	call	print_character
+
+	/* Copy ROM at %ebp to PMM or highmem block */
+	movl	%ebp, %esi
+	movl	image_source, %edi
+	movzwl	real_size, %ecx
+	shll	$9, %ecx
+	addr32 es rep movsb
+	movl	%edi, decompress_to
+skip_load:
+	testb	%dl, %dl	/* Was ROM already mapped? */
+	jnz	skip_unmap
+
+	/* Unmap the ROM by restoring old ROM BAR */
+	movw	$PCIBIOS_WRITE_DWORD, %ax
+	movw	$PCI_ROM_ADDRESS, %di
+	popl	%ecx
+	int	$0x1a
+
+skip_unmap:
+	/* Error handling */
+	testb	%dh, %dh
+	jnz	err_rom_invalid
+	clc
+	jmp	99f
+
+err_pcibios:			/* No PCI BIOS available */
+	movw	$load_message_no_pcibios, %si
+	xorl	%eax, %eax	/* "error code" is zero */
+	jmp	1f
+err_size_insane:		/* BAR has size (%ecx) that is insane */
+	movw	$load_message_size_insane, %si
+	movl	%ecx, %eax
+	jmp	1f
+err_no_bar:			/* No space of sufficient size (%edx) found */
+	movw	$load_message_no_bar, %si
+	movl	%edx, %eax
+	jmp	1f
+err_rom_invalid:		/* Loaded ROM does not match (%ebp bytes left) */
+	movw	$load_message_rom_invalid, %si
+	movzbl	romheader_size, %eax
+	shll	$9, %eax
+	subl	%ebp, %eax
+	decl	%eax		/* %eax is now byte index of failure */
+
+1:	/* Error handler - print message at %si and dword in %eax */
+	xorw	%di, %di
+	call	print_message
+	call	print_hex_dword
+	stc
+99:	popw	%es
+	popal
+	ret
+
+	.size	load_from_pci, . - load_from_pci
+
+load_message_no_pcibios:
+	.asciz	"\nNo PCI BIOS found! "
+	.size	load_message_no_pcibios, . - load_message_no_pcibios
+
+load_message_size_insane:
+	.asciz	"\nROM resource has invalid size "
+	.size	load_message_size_insane, . - load_message_size_insane
+
+load_message_no_bar:
+	.asciz	"\nNo memory hole of sufficient size "
+	.size	load_message_no_bar, . - load_message_no_bar
+
+load_message_rom_invalid:
+	.asciz	"\nLoaded ROM is invalid at "
+	.size	load_message_rom_invalid, . - load_message_rom_invalid
+
+#endif /* LOAD_ROM_FROM_PCI */
+
+
 /* INT19 entry point
  *
  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
@@ -557,6 +947,14 @@ exec:	/* Set %ds = %cs */
 	pushw	%cs
 	popw	%ds
 
+#ifdef LOAD_ROM_FROM_PCI
+	/* Don't execute if load was invalid */
+	cmpl	$0, decompress_to
+	jne	1f
+	lret
+1:
+#endif
+
 	/* Print message as soon as possible */
 	movw	$prodstr, %si
 	xorw	%di, %di
@@ -616,50 +1014,6 @@ exec_message:
 	.asciz	" starting execution\n"
 	.size exec_message, . - exec_message
 
-/* UNDI loader
- *
- * Called by an external program to load our PXE stack.
- */
-undiloader:
-	/* Save registers */
-	pushl	%esi
-	pushl	%edi
-	pushw	%ds
-	pushw	%es
-	pushw	%bx
-	/* ROM segment address to %ds */
-	pushw	%cs
-	popw	%ds
-	/* UNDI loader parameter structure address into %es:%di */
-	movw	%sp, %bx
-	movw	%ss:18(%bx), %di
-	movw	%ss:20(%bx), %es
-	/* Install to specified real-mode addresses */
-	pushw	%di
-	movw	%es:12(%di), %bx
-	movw	%es:14(%di), %ax
-	movl	image_source, %esi
-	movl	decompress_to, %edi
-	call	install_prealloc
-	popw	%di
-	/* Call UNDI loader C code */
-	pushl	$pxe_loader_call
-	pushw	%cs
-	pushw	$1f
-	pushw	%ax
-	pushw	$prot_call
-	lret
-1:	popw	%bx	/* discard */
-	popw	%bx	/* discard */
-	/* Restore registers and return */
-	popw	%bx
-	popw	%es
-	popw	%ds
-	popl	%edi
-	popl	%esi
-	lret
-	.size undiloader, . - undiloader
-
 /* Wait for key press specified by %bl (masked by %bh)
  *
  * Used by init and INT19 code when prompting user.  If the specified
diff --git a/gpxe/src/arch/i386/prefix/undiloader.S b/gpxe/src/arch/i386/prefix/undiloader.S
new file mode 100644
index 0000000..36c1bef
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/undiloader.S
@@ -0,0 +1,49 @@
+	.text
+	.code16
+	.arch i386
+	.section ".prefix", "ax", @progbits
+
+/* UNDI loader
+ *
+ * Called by an external program to load our PXE stack.
+ */
+	.globl	undiloader
+undiloader:
+	/* Save registers */
+	pushl	%esi
+	pushl	%edi
+	pushw	%ds
+	pushw	%es
+	pushw	%bx
+	/* ROM segment address to %ds */
+	pushw	%cs
+	popw	%ds
+	/* UNDI loader parameter structure address into %es:%di */
+	movw	%sp, %bx
+	movw	%ss:18(%bx), %di
+	movw	%ss:20(%bx), %es
+	/* Install to specified real-mode addresses */
+	pushw	%di
+	movw	%es:12(%di), %bx
+	movw	%es:14(%di), %ax
+	movl	image_source, %esi
+	movl	decompress_to, %edi
+	call	install_prealloc
+	popw	%di
+	/* Call UNDI loader C code */
+	pushl	$pxe_loader_call
+	pushw	%cs
+	pushw	$1f
+	pushw	%ax
+	pushw	$prot_call
+	lret
+1:	popw	%bx	/* discard */
+	popw	%bx	/* discard */
+	/* Restore registers and return */
+	popw	%bx
+	popw	%es
+	popw	%ds
+	popl	%edi
+	popl	%esi
+	lret
+	.size undiloader, . - undiloader
diff --git a/gpxe/src/arch/i386/prefix/unnrv2b.S b/gpxe/src/arch/i386/prefix/unnrv2b.S
index 70167a1..f5724c1 100644
--- a/gpxe/src/arch/i386/prefix/unnrv2b.S
+++ b/gpxe/src/arch/i386/prefix/unnrv2b.S
@@ -20,6 +20,8 @@
  *   Michael Brown 9 Mar 2005
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 /****************************************************************************
  * This file provides the decompress() and decompress16() functions
  * which can be called in order to decompress an image compressed with
diff --git a/gpxe/src/arch/i386/prefix/unnrv2b16.S b/gpxe/src/arch/i386/prefix/unnrv2b16.S
new file mode 100644
index 0000000..b24c284
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/unnrv2b16.S
@@ -0,0 +1,9 @@
+/*
+ * 16-bit version of the decompressor
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER )
+
+#define CODE16
+#include "unnrv2b.S"
diff --git a/gpxe/src/arch/i386/prefix/xromprefix.S b/gpxe/src/arch/i386/prefix/xromprefix.S
new file mode 100644
index 0000000..d7c861f
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/xromprefix.S
@@ -0,0 +1,9 @@
+/*
+ * ROM prefix that loads the bulk of the ROM using direct PCI accesses,
+ * so as not to take up much option ROM space on PCI <3.0 systems.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER )
+
+#define LOAD_ROM_FROM_PCI
+#include "romprefix.S"
diff --git a/gpxe/src/arch/i386/scripts/i386-kir.lds b/gpxe/src/arch/i386/scripts/i386-kir.lds
index b213c60..c19480f 100644
--- a/gpxe/src/arch/i386/scripts/i386-kir.lds
+++ b/gpxe/src/arch/i386/scripts/i386-kir.lds
@@ -130,6 +130,7 @@ SECTIONS {
     /DISCARD/ : {
 	*(.comment)
 	*(.note)
+	*(.discard)
     }
 
     /*
diff --git a/gpxe/src/arch/i386/scripts/i386.lds b/gpxe/src/arch/i386/scripts/i386.lds
index 8a0c673..33c75f9 100644
--- a/gpxe/src/arch/i386/scripts/i386.lds
+++ b/gpxe/src/arch/i386/scripts/i386.lds
@@ -152,6 +152,7 @@ SECTIONS {
 	*(.eh_frame.*)
 	*(.rel)
 	*(.rel.*)
+	*(.discard)
     }
 
     /*
@@ -193,17 +194,9 @@ SECTIONS {
      * Values calculated to save code from doing it
      *
      */
+    _prefix_filesz_sect = ( ( _prefix_filesz + 511 ) / 512 );
     _prefix_memsz_pgh	= ( ( _prefix_memsz + 15 ) / 16 );
     _prefix_memsz_sect	= ( ( _prefix_memsz + 511 ) / 512 );
     _text16_memsz_pgh	= ( ( _text16_memsz + 15 ) / 16 );
     _data16_memsz_pgh	= ( ( _data16_memsz + 15 ) / 16 );
-
-    /*
-     * File size in paragraphs and sectors.  Note that wherever the
-     * _filesz variables are used, there must be a corresponding
-     * .zinfo.fixup section.
-     *
-     */
-    _filesz_pgh		= ( ( _filesz + 15 ) / 16 );
-    _filesz_sect	= ( ( _filesz + 511 ) / 512 );
 }
diff --git a/gpxe/src/arch/i386/transitions/libkir.S b/gpxe/src/arch/i386/transitions/libkir.S
index 1023ddd..1176fcc 100644
--- a/gpxe/src/arch/i386/transitions/libkir.S
+++ b/gpxe/src/arch/i386/transitions/libkir.S
@@ -5,6 +5,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 /****************************************************************************
  * This file defines libkir: an interface between external and
  * internal environments when -DKEEP_IT_REAL is used, so that both
diff --git a/gpxe/src/arch/i386/transitions/librm.S b/gpxe/src/arch/i386/transitions/librm.S
old mode 100755
new mode 100644
index 8cf1f7f..cb27ef3
--- a/gpxe/src/arch/i386/transitions/librm.S
+++ b/gpxe/src/arch/i386/transitions/librm.S
@@ -5,6 +5,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER )
+
 /* Drag in local definitions */
 #include "librm.h"
 
diff --git a/gpxe/src/arch/i386/transitions/librm_mgmt.c b/gpxe/src/arch/i386/transitions/librm_mgmt.c
old mode 100755
new mode 100644
index 50569f8..f00be81
--- a/gpxe/src/arch/i386/transitions/librm_mgmt.c
+++ b/gpxe/src/arch/i386/transitions/librm_mgmt.c
@@ -5,6 +5,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <realmode.h>
 
diff --git a/gpxe/src/arch/x86/Makefile b/gpxe/src/arch/x86/Makefile
index a7c4bc0..f5f67ac 100644
--- a/gpxe/src/arch/x86/Makefile
+++ b/gpxe/src/arch/x86/Makefile
@@ -1,6 +1,6 @@
 # Include common x86 headers
 #
-CFLAGS		+= -Iarch/x86/include
+INCDIRS		+= arch/x86/include
 
 # x86-specific directories containing source files
 #
diff --git a/gpxe/src/arch/x86/core/pcidirect.c b/gpxe/src/arch/x86/core/pcidirect.c
index db17215..2c61d9c 100644
--- a/gpxe/src/arch/x86/core/pcidirect.c
+++ b/gpxe/src/arch/x86/core/pcidirect.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/io.h>
 #include <gpxe/pci.h>
 
diff --git a/gpxe/src/arch/x86/core/x86_string.c b/gpxe/src/arch/x86/core/x86_string.c
index c0224c7..5838eba 100644
--- a/gpxe/src/arch/x86/core/x86_string.c
+++ b/gpxe/src/arch/x86/core/x86_string.c
@@ -22,6 +22,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 
 /**
diff --git a/gpxe/src/arch/x86/include/bits/pci_io.h b/gpxe/src/arch/x86/include/bits/pci_io.h
index 0fbb439..f6efcda 100644
--- a/gpxe/src/arch/x86/include/bits/pci_io.h
+++ b/gpxe/src/arch/x86/include/bits/pci_io.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/pcibios.h>
 #include <gpxe/pcidirect.h>
 
diff --git a/gpxe/src/arch/x86/include/bits/string.h b/gpxe/src/arch/x86/include/bits/string.h
index 42ddedd..a68868a 100644
--- a/gpxe/src/arch/x86/include/bits/string.h
+++ b/gpxe/src/arch/x86/include/bits/string.h
@@ -21,6 +21,8 @@
  *		consider these trivial functions to be PD.
  */
 
+FILE_LICENCE ( PUBLIC_DOMAIN );
+
 #define __HAVE_ARCH_MEMCPY
 
 extern void * __memcpy ( void *dest, const void *src, size_t len );
diff --git a/gpxe/src/arch/x86/include/gpxe/efi/efix86_nap.h b/gpxe/src/arch/x86/include/gpxe/efi/efix86_nap.h
index 91424c5..833c922 100644
--- a/gpxe/src/arch/x86/include/gpxe/efi/efix86_nap.h
+++ b/gpxe/src/arch/x86/include/gpxe/efi/efix86_nap.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef NAP_EFIX86
 #define NAP_PREFIX_efix86
 #else
diff --git a/gpxe/src/arch/x86/include/gpxe/pcibios.h b/gpxe/src/arch/x86/include/gpxe/pcibios.h
index b86f5ab..93a6eb8 100644
--- a/gpxe/src/arch/x86/include/gpxe/pcibios.h
+++ b/gpxe/src/arch/x86/include/gpxe/pcibios.h
@@ -9,6 +9,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef PCIAPI_PCBIOS
 #define PCIAPI_PREFIX_pcbios
 #else
diff --git a/gpxe/src/arch/x86/include/gpxe/pcidirect.h b/gpxe/src/arch/x86/include/gpxe/pcidirect.h
index fe433c6..8b705fb 100644
--- a/gpxe/src/arch/x86/include/gpxe/pcidirect.h
+++ b/gpxe/src/arch/x86/include/gpxe/pcidirect.h
@@ -1,6 +1,8 @@
 #ifndef _PCIDIRECT_H
 #define _PCIDIRECT_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/io.h>
 
diff --git a/gpxe/src/arch/x86/interface/efi/efix86_nap.c b/gpxe/src/arch/x86/interface/efi/efix86_nap.c
index 45e99a6..89a4e3b 100644
--- a/gpxe/src/arch/x86/interface/efi/efix86_nap.c
+++ b/gpxe/src/arch/x86/interface/efi/efix86_nap.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/nap.h>
 #include <gpxe/efi/efi.h>
 
diff --git a/gpxe/src/arch/x86/prefix/efidrvprefix.c b/gpxe/src/arch/x86/prefix/efidrvprefix.c
index 5f63158..36d5650 100644
--- a/gpxe/src/arch/x86/prefix/efidrvprefix.c
+++ b/gpxe/src/arch/x86/prefix/efidrvprefix.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <gpxe/init.h>
 #include <gpxe/efi/efi.h>
diff --git a/gpxe/src/arch/x86/prefix/efiprefix.c b/gpxe/src/arch/x86/prefix/efiprefix.c
index b05b744..4cc9e04 100644
--- a/gpxe/src/arch/x86/prefix/efiprefix.c
+++ b/gpxe/src/arch/x86/prefix/efiprefix.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <gpxe/efi/efi.h>
 
diff --git a/gpxe/src/arch/x86/scripts/efi.lds b/gpxe/src/arch/x86/scripts/efi.lds
index aac3105..7525b81 100644
--- a/gpxe/src/arch/x86/scripts/efi.lds
+++ b/gpxe/src/arch/x86/scripts/efi.lds
@@ -101,5 +101,6 @@ SECTIONS {
 	*(.eh_frame.*)
 	*(.rel)
 	*(.rel.*)
+	*(.discard)
     }
 }
diff --git a/gpxe/src/arch/x86_64/include/bits/stdint.h b/gpxe/src/arch/x86_64/include/bits/stdint.h
index 23bae9c..9eb72e9 100644
--- a/gpxe/src/arch/x86_64/include/bits/stdint.h
+++ b/gpxe/src/arch/x86_64/include/bits/stdint.h
@@ -1,7 +1,7 @@
 #ifndef _BITS_STDINT_H
 #define _BITS_STDINT_H
 
-typedef unsigned long		size_t;
+typedef __SIZE_TYPE__		size_t;
 typedef signed long		ssize_t;
 typedef signed long		off_t;
 
diff --git a/gpxe/src/config/config.c b/gpxe/src/config/config.c
new file mode 100644
index 0000000..a6e7622
--- /dev/null
+++ b/gpxe/src/config/config.c
@@ -0,0 +1,262 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <config/general.h>
+#include <config/console.h>
+
+/** @file
+ *
+ * Configuration options
+ *
+ * This file contains macros that pull various objects into the link
+ * based on definitions in configuration header files. Ideally it
+ * should be the only place in gPXE where one might need to use #ifdef
+ * for compile-time options.
+ *
+ * In the fairly common case where an object should only be considered
+ * for inclusion if the subsystem it depends on is present, its
+ * configuration macros should be placed in a file named
+ * <tt>config_<i>subsystem</i>.c</tt>, where @e subsystem is the
+ * object basename of the main source file for that subsystem. The
+ * build system will pull in that file if @c subsystem.c is included
+ * in the final gPXE executable built.
+ */
+
+/*
+ * Build ID string calculations
+ *
+ */
+#undef XSTR
+#undef STR
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+#ifdef BUILD_SERIAL
+#include "config/.buildserial.h"
+#define BUILD_SERIAL_STR " #" XSTR(BUILD_SERIAL_NUM)
+#else
+#define BUILD_SERIAL_STR ""
+#endif
+
+#ifdef BUILD_ID
+#define BUILD_ID_STR " " BUILD_ID
+#else
+#define BUILD_ID_STR ""
+#endif
+
+#if defined(BUILD_ID) || defined(BUILD_SERIAL)
+#define BUILD_STRING " [build" BUILD_ID_STR BUILD_SERIAL_STR "]"
+#else
+#define BUILD_STRING ""
+#endif
+
+/*
+ * Drag in all requested console types
+ *
+ */
+
+#ifdef CONSOLE_PCBIOS
+REQUIRE_OBJECT ( bios_console );
+#endif
+#ifdef CONSOLE_SERIAL
+REQUIRE_OBJECT ( serial_console );
+#endif
+#ifdef CONSOLE_DIRECT_VGA
+REQUIRE_OBJECT ( video_subr );
+#endif
+#ifdef CONSOLE_BTEXT
+REQUIRE_OBJECT ( btext );
+#endif
+#ifdef CONSOLE_PC_KBD
+REQUIRE_OBJECT ( pc_kbd );
+#endif
+#ifdef CONSOLE_SYSLOG
+REQUIRE_OBJECT ( syslog );
+#endif
+#ifdef CONSOLE_EFI
+REQUIRE_OBJECT ( efi_console );
+#endif
+
+/*
+ * Drag in all requested network protocols
+ *
+ */
+#ifdef NET_PROTO_IPV4
+REQUIRE_OBJECT ( ipv4 );
+#endif
+
+/*
+ * Drag in all requested PXE support
+ *
+ */
+#ifdef PXE_MENU
+REQUIRE_OBJECT ( pxemenu );
+#endif
+#ifdef PXE_STACK
+REQUIRE_OBJECT ( pxe_call );
+#endif
+
+/*
+ * Drag in all requested download protocols
+ *
+ */
+#ifdef DOWNLOAD_PROTO_TFTP
+REQUIRE_OBJECT ( tftp );
+#endif
+#ifdef DOWNLOAD_PROTO_HTTP
+REQUIRE_OBJECT ( http );
+#endif
+#ifdef DOWNLOAD_PROTO_HTTPS
+REQUIRE_OBJECT ( https );
+#endif
+#ifdef DOWNLOAD_PROTO_FTP
+REQUIRE_OBJECT ( ftp );
+#endif
+#ifdef DOWNLOAD_PROTO_TFTM
+REQUIRE_OBJECT ( tftm );
+#endif
+#ifdef DOWNLOAD_PROTO_SLAM
+REQUIRE_OBJECT ( slam );
+#endif
+
+/*
+ * Drag in all requested SAN boot protocols
+ *
+ */
+#ifdef SANBOOT_PROTO_ISCSI
+REQUIRE_OBJECT ( iscsiboot );
+#endif
+#ifdef SANBOOT_PROTO_AOE
+REQUIRE_OBJECT ( aoeboot );
+#endif
+#ifdef SANBOOT_PROTO_IB_SRP
+REQUIRE_OBJECT ( ib_srpboot );
+#endif
+
+/*
+ * Drag in all requested resolvers
+ *
+ */
+#ifdef DNS_RESOLVER
+REQUIRE_OBJECT ( dns );
+#endif
+
+/*
+ * Drag in all requested image formats
+ *
+ */
+#ifdef IMAGE_NBI
+REQUIRE_OBJECT ( nbi );
+#endif
+#ifdef IMAGE_ELF
+REQUIRE_OBJECT ( elfboot );
+#endif
+#ifdef IMAGE_FREEBSD
+REQUIRE_OBJECT ( freebsd );
+#endif
+#ifdef IMAGE_MULTIBOOT
+REQUIRE_OBJECT ( multiboot );
+#endif
+#ifdef IMAGE_AOUT
+REQUIRE_OBJECT ( aout );
+#endif
+#ifdef IMAGE_WINCE
+REQUIRE_OBJECT ( wince );
+#endif
+#ifdef IMAGE_PXE
+REQUIRE_OBJECT ( pxe_image );
+#endif
+#ifdef IMAGE_SCRIPT
+REQUIRE_OBJECT ( script );
+#endif
+#ifdef IMAGE_BZIMAGE
+REQUIRE_OBJECT ( bzimage );
+#endif
+#ifdef IMAGE_ELTORITO
+REQUIRE_OBJECT ( eltorito );
+#endif
+#ifdef IMAGE_COMBOOT
+REQUIRE_OBJECT ( comboot );
+REQUIRE_OBJECT ( com32 );
+REQUIRE_OBJECT ( comboot_call );
+REQUIRE_OBJECT ( com32_call );
+REQUIRE_OBJECT ( com32_wrapper );
+REQUIRE_OBJECT ( comboot_resolv );
+#endif
+#ifdef IMAGE_EFI
+REQUIRE_OBJECT ( efi_image );
+#endif
+
+/*
+ * Drag in all requested commands
+ *
+ */
+#ifdef AUTOBOOT_CMD
+REQUIRE_OBJECT ( autoboot_cmd );
+#endif
+#ifdef NVO_CMD
+REQUIRE_OBJECT ( nvo_cmd );
+#endif
+#ifdef CONFIG_CMD
+REQUIRE_OBJECT ( config_cmd );
+#endif
+#ifdef IFMGMT_CMD
+REQUIRE_OBJECT ( ifmgmt_cmd );
+#endif
+/* IWMGMT_CMD is brought in by net80211.c if requested */
+#ifdef ROUTE_CMD
+REQUIRE_OBJECT ( route_cmd );
+#endif
+#ifdef IMAGE_CMD
+REQUIRE_OBJECT ( image_cmd );
+#endif
+#ifdef DHCP_CMD
+REQUIRE_OBJECT ( dhcp_cmd );
+#endif
+#ifdef SANBOOT_CMD
+REQUIRE_OBJECT ( sanboot_cmd );
+#endif
+#ifdef LOGIN_CMD
+REQUIRE_OBJECT ( login_cmd );
+#endif
+#ifdef TIME_CMD
+REQUIRE_OBJECT ( time_cmd );
+#endif
+#ifdef DIGEST_CMD
+REQUIRE_OBJECT ( digest_cmd );
+#endif
+#ifdef PXE_CMD
+REQUIRE_OBJECT ( pxe_cmd );
+#endif
+
+/*
+ * Drag in miscellaneous objects
+ *
+ */
+#ifdef NULL_TRAP
+REQUIRE_OBJECT ( nulltrap );
+#endif
+#ifdef GDBSERIAL
+REQUIRE_OBJECT ( gdbidt );
+REQUIRE_OBJECT ( gdbserial );
+REQUIRE_OBJECT ( gdbstub_cmd );
+#endif
+#ifdef GDBUDP
+REQUIRE_OBJECT ( gdbidt );
+REQUIRE_OBJECT ( gdbudp );
+REQUIRE_OBJECT ( gdbstub_cmd );
+#endif
+
+/*
+ * Drag in objects that are always required, but not dragged in via
+ * symbol dependencies.
+ *
+ */
+REQUIRE_OBJECT ( device );
+REQUIRE_OBJECT ( embedded );
diff --git a/gpxe/src/config/config_net80211.c b/gpxe/src/config/config_net80211.c
new file mode 100644
index 0000000..b33c363
--- /dev/null
+++ b/gpxe/src/config/config_net80211.c
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <config/general.h>
+
+/** @file
+ *
+ * 802.11 configuration options
+ *
+ */
+
+/*
+ * Drag in 802.11-specific commands
+ *
+ */
+#ifdef IWMGMT_CMD
+REQUIRE_OBJECT ( iwmgmt_cmd );
+#endif
+
+/*
+ * Drag in 802.11 error message tables
+ *
+ */
+#ifdef ERRMSG_80211
+REQUIRE_OBJECT ( wireless_errors );
+#endif
+
+/*
+ * Drag in 802.11 cryptosystems and handshaking protocols
+ *
+ */
+#ifdef CRYPTO_80211_WEP
+REQUIRE_OBJECT ( wep );
+#endif
+
+#ifdef CRYPTO_80211_WPA2
+#define CRYPTO_80211_WPA
+REQUIRE_OBJECT ( wpa_ccmp );
+#endif
+
+#ifdef CRYPTO_80211_WPA
+REQUIRE_OBJECT ( wpa_psk );
+REQUIRE_OBJECT ( wpa_tkip );
+#endif
diff --git a/gpxe/src/config/config_romprefix.c b/gpxe/src/config/config_romprefix.c
new file mode 100644
index 0000000..85f1e78
--- /dev/null
+++ b/gpxe/src/config/config_romprefix.c
@@ -0,0 +1,24 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <config/general.h>
+
+/** @file
+ *
+ * ROM prefix configuration options
+ *
+ */
+
+/*
+ * Provide UNDI loader if PXE stack is requested
+ *
+ */
+#ifdef PXE_STACK
+REQUIRE_OBJECT ( undiloader );
+#endif
diff --git a/gpxe/src/config/console.h b/gpxe/src/config/console.h
index b4ea1dd..be3242d 100644
--- a/gpxe/src/config/console.h
+++ b/gpxe/src/config/console.h
@@ -10,6 +10,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <config/defaults.h>
 
 //#define	CONSOLE_PCBIOS		/* Default BIOS console */
diff --git a/gpxe/src/config/defaults.h b/gpxe/src/config/defaults.h
index 1f55ef3..389c0b0 100644
--- a/gpxe/src/config/defaults.h
+++ b/gpxe/src/config/defaults.h
@@ -1,6 +1,8 @@
 #ifndef CONFIG_DEFAULTS_H
 #define CONFIG_DEFAULTS_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define CONFIG_DEFAULTS(_platform) <config/defaults/_platform.h>
 
 #include CONFIG_DEFAULTS(PLATFORM)
diff --git a/gpxe/src/config/defaults/pcbios.h b/gpxe/src/config/defaults/pcbios.h
index 4359e1a..c09105c 100644
--- a/gpxe/src/config/defaults/pcbios.h
+++ b/gpxe/src/config/defaults/pcbios.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define UACCESS_LIBRM
 #define IOAPI_X86
 #define PCIAPI_PCBIOS
@@ -23,6 +25,10 @@
 #define IMAGE_BZIMAGE		/* Linux bzImage image support */
 #define IMAGE_COMBOOT		/* SYSLINUX COMBOOT image support */
 
+#define PXE_STACK		/* PXE stack in gPXE - required for PXELINUX */
+#define PXE_MENU		/* PXE menu booting */
+#define	PXE_CMD			/* PXE commands */
+
 #define	SANBOOT_PROTO_ISCSI	/* iSCSI protocol */
 #define	SANBOOT_PROTO_AOE	/* AoE protocol */
 
diff --git a/gpxe/src/config/general.h b/gpxe/src/config/general.h
index a3d563c..de51f9f 100644
--- a/gpxe/src/config/general.h
+++ b/gpxe/src/config/general.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <config/defaults.h>
 
 /*
@@ -40,18 +42,23 @@
 #define	NET_PROTO_IPV4		/* IPv4 protocol */
 
 /*
+ * PXE support
+ *
+ */
+//#undef	PXE_STACK		/* PXE stack in gPXE - you want this! */
+//#undef	PXE_MENU		/* PXE menu booting */
+
+/*
  * Download protocols
  *
  */
 
 #define	DOWNLOAD_PROTO_TFTP	/* Trivial File Transfer Protocol */
-#define	DOWNLOAD_PROTO_NFS	/* Network File System */
 #define	DOWNLOAD_PROTO_HTTP	/* Hypertext Transfer Protocol */
 #define	DOWNLOAD_PROTO_HTTPS	/* Secure Hypertext Transfer Protocol */
 #define	DOWNLOAD_PROTO_FTP	/* File Transfer Protocol */
 #undef	DOWNLOAD_PROTO_TFTM	/* Multicast Trivial File Transfer Protocol */
 #undef	DOWNLOAD_PROTO_SLAM	/* Scalable Local Area Multicast */
-#undef	DOWNLOAD_PROTO_FSP	/* FSP? */
 
 /*
  * SAN boot protocols
@@ -60,6 +67,15 @@
 
 //#undef	SANBOOT_PROTO_ISCSI	/* iSCSI protocol */
 //#undef	SANBOOT_PROTO_AOE	/* AoE protocol */
+//#undef	SANBOOT_PROTO_IB_SRP	/* Infiniband SCSI RDMA protocol */
+
+/*
+ * 802.11 cryptosystems and handshaking protocols
+ *
+ */
+#define	CRYPTO_80211_WEP	/* WEP encryption (deprecated and insecure!) */
+#define	CRYPTO_80211_WPA	/* WPA Personal, authenticating with passphrase */
+#define	CRYPTO_80211_WPA2	/* Add support for stronger WPA cryptography */
 
 /*
  * Name resolution modules
@@ -67,7 +83,6 @@
  */
 
 #define	DNS_RESOLVER		/* DNS resolver */
-#undef	NMB_RESOLVER		/* NMB resolver */
 
 /*
  * Image types
@@ -96,11 +111,21 @@
 #define	NVO_CMD			/* Non-volatile option storage commands */
 #define	CONFIG_CMD		/* Option configuration console */
 #define	IFMGMT_CMD		/* Interface management commands */
+#define	IWMGMT_CMD		/* Wireless interface management commands */
 #define	ROUTE_CMD		/* Routing table management commands */
 #define IMAGE_CMD		/* Image management commands */
 #define DHCP_CMD		/* DHCP management commands */
 #define SANBOOT_CMD		/* SAN boot commands */
 #define LOGIN_CMD		/* Login command */
+#undef	TIME_CMD		/* Time commands */
+#undef	DIGEST_CMD		/* Image crypto digest commands */
+//#undef	PXE_CMD			/* PXE commands */
+
+/*
+ * Error message tables to include
+ *
+ */
+#undef	ERRMSG_80211		/* All 802.11 error descriptions (~3.3kb) */
 
 /*
  * Obscure configuration options
diff --git a/gpxe/src/config/ioapi.h b/gpxe/src/config/ioapi.h
index 7726a0f..8ddd557 100644
--- a/gpxe/src/config/ioapi.h
+++ b/gpxe/src/config/ioapi.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <config/defaults.h>
 
 //#undef	PCIAPI_PCBIOS		/* Access via PCI BIOS */
diff --git a/gpxe/src/config/nap.h b/gpxe/src/config/nap.h
index 8648d92..1b98135 100644
--- a/gpxe/src/config/nap.h
+++ b/gpxe/src/config/nap.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <config/defaults.h>
 
 //#undef		NAP_PCBIOS
diff --git a/gpxe/src/config/serial.h b/gpxe/src/config/serial.h
index 984a7a9..44272d1 100644
--- a/gpxe/src/config/serial.h
+++ b/gpxe/src/config/serial.h
@@ -11,7 +11,14 @@
  *
  */
 
-#define	COMCONSOLE	0x3f8		/* I/O port address */
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#define	COM1		0x3f8
+#define	COM2		0x2f8
+#define	COM3		0x3e8
+#define	COM4		0x2e8
+
+#define	COMCONSOLE	COM1		/* I/O port address */
 
 /* Keep settings from a previous user of the serial port (e.g. lilo or
  * LinuxBIOS), ignoring COMSPEED, COMDATA, COMPARITY and COMSTOP.
diff --git a/gpxe/src/config/timer.h b/gpxe/src/config/timer.h
index 7c3f352..cc6a93d 100644
--- a/gpxe/src/config/timer.h
+++ b/gpxe/src/config/timer.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <config/defaults.h>
 
 //#undef		TIMER_PCBIOS
diff --git a/gpxe/src/config/umalloc.h b/gpxe/src/config/umalloc.h
index de4019e..65febf1 100644
--- a/gpxe/src/config/umalloc.h
+++ b/gpxe/src/config/umalloc.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <config/defaults.h>
 
 #endif /* CONFIG_UMALLOC_H */
diff --git a/gpxe/src/core/acpi.c b/gpxe/src/core/acpi.c
index 94b7b2a..b65f4d4 100644
--- a/gpxe/src/core/acpi.c
+++ b/gpxe/src/core/acpi.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/acpi.h>
 
 /** @file
diff --git a/gpxe/src/core/ansiesc.c b/gpxe/src/core/ansiesc.c
index 6b820ad..31306e2 100644
--- a/gpxe/src/core/ansiesc.c
+++ b/gpxe/src/core/ansiesc.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <assert.h>
 #include <gpxe/ansiesc.h>
diff --git a/gpxe/src/core/asprintf.c b/gpxe/src/core/asprintf.c
index 94d7e7c..03cf45c 100644
--- a/gpxe/src/core/asprintf.c
+++ b/gpxe/src/core/asprintf.c
@@ -4,6 +4,8 @@
 #include <stdio.h>
 #include <errno.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * Write a formatted string to newly allocated memory.
  *
diff --git a/gpxe/src/core/base64.c b/gpxe/src/core/base64.c
index e54821e..5619ef7 100644
--- a/gpxe/src/core/base64.c
+++ b/gpxe/src/core/base64.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <assert.h>
diff --git a/gpxe/src/core/basename.c b/gpxe/src/core/basename.c
index 7340c0d..a481c54 100644
--- a/gpxe/src/core/basename.c
+++ b/gpxe/src/core/basename.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/core/bitmap.c b/gpxe/src/core/bitmap.c
index d026647..bbe9cba 100644
--- a/gpxe/src/core/bitmap.c
+++ b/gpxe/src/core/bitmap.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <errno.h>
 #include <gpxe/bitmap.h>
 
diff --git a/gpxe/src/core/bitops.c b/gpxe/src/core/bitops.c
index 53abaae..1bca9e4 100644
--- a/gpxe/src/core/bitops.c
+++ b/gpxe/src/core/bitops.c
@@ -1,5 +1,7 @@
 #include <strings.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 int __flsl ( long x ) {
 	unsigned long value = x;
 	int ls = 0;
diff --git a/gpxe/src/core/config.c b/gpxe/src/core/config.c
deleted file mode 100644
index bd0d66e..0000000
--- a/gpxe/src/core/config.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
- */
-
-#include <config/general.h>
-#include <config/console.h>
-
-/*
- * Build ID string calculations
- *
- */
-#undef XSTR
-#undef STR
-#define XSTR(s) STR(s)
-#define STR(s) #s
-
-#ifdef BUILD_SERIAL
-#include "config/.buildserial.h"
-#define BUILD_SERIAL_STR " #" XSTR(BUILD_SERIAL_NUM)
-#else
-#define BUILD_SERIAL_STR ""
-#endif
-
-#ifdef BUILD_ID
-#define BUILD_ID_STR " " BUILD_ID
-#else
-#define BUILD_ID_STR ""
-#endif
-
-#if defined(BUILD_ID) || defined(BUILD_SERIAL)
-#define BUILD_STRING " [build" BUILD_ID_STR BUILD_SERIAL_STR "]"
-#else
-#define BUILD_STRING ""
-#endif
-
-/*
- * Drag in all requested console types
- *
- */
-
-#ifdef CONSOLE_PCBIOS
-REQUIRE_OBJECT ( bios_console );
-#endif
-#ifdef CONSOLE_SERIAL
-REQUIRE_OBJECT ( serial_console );
-#endif
-#ifdef CONSOLE_DIRECT_VGA
-REQUIRE_OBJECT ( video_subr );
-#endif
-#ifdef CONSOLE_BTEXT
-REQUIRE_OBJECT ( btext );
-#endif
-#ifdef CONSOLE_PC_KBD
-REQUIRE_OBJECT ( pc_kbd );
-#endif
-#ifdef CONSOLE_SYSLOG
-REQUIRE_OBJECT ( syslog );
-#endif
-#ifdef CONSOLE_EFI
-REQUIRE_OBJECT ( efi_console );
-#endif
-
-/*
- * Drag in all requested network protocols
- *
- */
-#ifdef NET_PROTO_IPV4
-REQUIRE_OBJECT ( ipv4 );
-#endif
-
-/*
- * Drag in all requested download protocols
- *
- */
-#ifdef DOWNLOAD_PROTO_TFTP
-REQUIRE_OBJECT ( tftp );
-#endif
-#ifdef DOWNLOAD_PROTO_NFS
-REQUIRE_OBJECT ( nfs );
-#endif
-#ifdef DOWNLOAD_PROTO_HTTP
-REQUIRE_OBJECT ( http );
-#endif
-#ifdef DOWNLOAD_PROTO_HTTPS
-REQUIRE_OBJECT ( https );
-#endif
-#ifdef DOWNLOAD_PROTO_FTP
-REQUIRE_OBJECT ( ftp );
-#endif
-#ifdef DOWNLOAD_PROTO_TFTM
-REQUIRE_OBJECT ( tftm );
-#endif
-#ifdef DOWNLOAD_PROTO_SLAM
-REQUIRE_OBJECT ( slam );
-#endif
-
-/*
- * Drag in all requested SAN boot protocols
- *
- */
-#ifdef SANBOOT_PROTO_ISCSI
-REQUIRE_OBJECT ( iscsiboot );
-#endif
-#ifdef SANBOOT_PROTO_AOE
-REQUIRE_OBJECT ( aoeboot );
-#endif
-
-/*
- * Drag in all requested resolvers
- *
- */
-#ifdef DNS_RESOLVER
-REQUIRE_OBJECT ( dns );
-#endif
-#ifdef NMB_RESOLVER
-REQUIRE_OBJECT ( nmb );
-#endif
-
-/*
- * Drag in all requested image formats
- *
- */
-#ifdef IMAGE_NBI
-REQUIRE_OBJECT ( nbi );
-#endif
-#ifdef IMAGE_ELF
-REQUIRE_OBJECT ( elfboot );
-#endif
-#ifdef IMAGE_FREEBSD
-REQUIRE_OBJECT ( freebsd );
-#endif
-#ifdef IMAGE_MULTIBOOT
-REQUIRE_OBJECT ( multiboot );
-#endif
-#ifdef IMAGE_AOUT
-REQUIRE_OBJECT ( aout );
-#endif
-#ifdef IMAGE_WINCE
-REQUIRE_OBJECT ( wince );
-#endif
-#ifdef IMAGE_PXE
-REQUIRE_OBJECT ( pxe_image );
-#endif
-#ifdef IMAGE_SCRIPT
-REQUIRE_OBJECT ( script );
-#endif
-#ifdef IMAGE_BZIMAGE
-REQUIRE_OBJECT ( bzimage );
-#endif
-#ifdef IMAGE_ELTORITO
-REQUIRE_OBJECT ( eltorito );
-#endif
-#ifdef IMAGE_COMBOOT
-REQUIRE_OBJECT ( comboot );
-REQUIRE_OBJECT ( com32 );
-REQUIRE_OBJECT ( comboot_call );
-REQUIRE_OBJECT ( com32_call );
-REQUIRE_OBJECT ( com32_wrapper );
-REQUIRE_OBJECT ( comboot_resolv );
-#endif
-#ifdef IMAGE_EFI
-REQUIRE_OBJECT ( efi_image );
-#endif
-
-/*
- * Drag in all requested commands
- *
- */
-#ifdef AUTOBOOT_CMD
-REQUIRE_OBJECT ( autoboot_cmd );
-#endif
-#ifdef NVO_CMD
-REQUIRE_OBJECT ( nvo_cmd );
-#endif
-#ifdef CONFIG_CMD
-REQUIRE_OBJECT ( config_cmd );
-#endif
-#ifdef IFMGMT_CMD
-REQUIRE_OBJECT ( ifmgmt_cmd );
-#endif
-#ifdef ROUTE_CMD
-REQUIRE_OBJECT ( route_cmd );
-#endif
-#ifdef IMAGE_CMD
-REQUIRE_OBJECT ( image_cmd );
-#endif
-#ifdef DHCP_CMD
-REQUIRE_OBJECT ( dhcp_cmd );
-#endif
-#ifdef SANBOOT_CMD
-REQUIRE_OBJECT ( sanboot_cmd );
-#endif
-#ifdef LOGIN_CMD
-REQUIRE_OBJECT ( login_cmd );
-#endif
-
-/*
- * Drag in miscellaneous objects
- *
- */
-#ifdef NULL_TRAP
-REQUIRE_OBJECT ( nulltrap );
-#endif
-#ifdef GDBSERIAL
-REQUIRE_OBJECT ( gdbidt );
-REQUIRE_OBJECT ( gdbserial );
-REQUIRE_OBJECT ( gdbstub_cmd );
-#endif
-#ifdef GDBUDP
-REQUIRE_OBJECT ( gdbidt );
-REQUIRE_OBJECT ( gdbudp );
-REQUIRE_OBJECT ( gdbstub_cmd );
-#endif
-
-/*
- * Drag in objects that are always required, but not dragged in via
- * symbol dependencies.
- *
- */
-REQUIRE_OBJECT ( device );
-REQUIRE_OBJECT ( embedded );
diff --git a/gpxe/src/core/console.c b/gpxe/src/core/console.c
index c9773f7..e22d260 100644
--- a/gpxe/src/core/console.c
+++ b/gpxe/src/core/console.c
@@ -5,10 +5,7 @@
 
 /** @file */
 
-static struct console_driver console_drivers[0]
-	__table_start ( struct console_driver, console );
-static struct console_driver console_drivers_end[0]
-	__table_end ( struct console_driver, console );
+FILE_LICENCE ( GPL2_OR_LATER );
 
 /**
  * Write a single character to each console device.
@@ -28,8 +25,7 @@ void putchar ( int character ) {
 	if ( character == '\n' )
 		putchar ( '\r' );
 
-	for ( console = console_drivers; console < console_drivers_end ;
-	      console++ ) {
+	for_each_table_entry ( console, CONSOLES ) {
 		if ( ( ! console->disabled ) && console->putchar )
 			console->putchar ( character );
 	}
@@ -51,8 +47,7 @@ void putchar ( int character ) {
 static struct console_driver * has_input ( void ) {
 	struct console_driver *console;
 
-	for ( console = console_drivers; console < console_drivers_end ;
-	      console++ ) {
+	for_each_table_entry ( console, CONSOLES ) {
 		if ( ( ! console->disabled ) && console->iskey ) {
 			if ( console->iskey () )
 				return console;
diff --git a/gpxe/src/core/cpio.c b/gpxe/src/core/cpio.c
index 7d2e882..b303fa3 100644
--- a/gpxe/src/core/cpio.c
+++ b/gpxe/src/core/cpio.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** @file
  *
  * CPIO archives
diff --git a/gpxe/src/core/ctype.c b/gpxe/src/core/ctype.c
new file mode 100644
index 0000000..6185bb2
--- /dev/null
+++ b/gpxe/src/core/ctype.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/**
+ * @file
+ *
+ * Character types
+ *
+ */
+
+#include <ctype.h>
+
+/**
+ * Check to see if character is a space
+ *
+ * @v c			Character
+ * @ret isspace		Character is a space
+ */
+int isspace ( int c ) {
+	switch ( c ) {
+	case ' ' :
+	case '\f' :
+	case '\n' :
+	case '\r' :
+	case '\t' :
+	case '\v' :
+		return 1;
+	default:
+		return 0;
+	}
+}
diff --git a/gpxe/src/core/cwuri.c b/gpxe/src/core/cwuri.c
index 81fd900..65e01b2 100644
--- a/gpxe/src/core/cwuri.c
+++ b/gpxe/src/core/cwuri.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <gpxe/uri.h>
 
diff --git a/gpxe/src/core/debug.c b/gpxe/src/core/debug.c
index 500a7ac..8f92930 100644
--- a/gpxe/src/core/debug.c
+++ b/gpxe/src/core/debug.c
@@ -36,7 +36,8 @@ static void dbg_hex_dump_da_row ( unsigned long dispaddr, const void *data,
 			printf ( "   " );
 			continue;
 		}
-		printf ( " %02x", bytes[i] );
+		printf ( "%c%02x",
+			 ( ( ( i % 16 ) == 8 ) ? '-' : ' ' ), bytes[i] );
 	}
 	printf ( " : " );
 	for ( i = offset ; i < ( offset + 16 ) ; i++ ) {
diff --git a/gpxe/src/core/device.c b/gpxe/src/core/device.c
index 84915c2..96ccc9f 100644
--- a/gpxe/src/core/device.c
+++ b/gpxe/src/core/device.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <gpxe/list.h>
 #include <gpxe/tables.h>
@@ -29,11 +31,6 @@
  *
  */
 
-static struct root_device root_devices[0]
-	__table_start ( struct root_device, root_devices );
-static struct root_device root_devices_end[0]
-	__table_end ( struct root_device, root_devices );
-
 /** Registered root devices */
 static LIST_HEAD ( devices );
 
@@ -77,7 +74,7 @@ static void probe_devices ( void ) {
 	struct root_device *rootdev;
 	int rc;
 
-	for ( rootdev = root_devices; rootdev < root_devices_end; rootdev++ ) {
+	for_each_table_entry ( rootdev, ROOT_DEVICES ) {
 		list_add ( &rootdev->dev.siblings, &devices );
 		INIT_LIST_HEAD ( &rootdev->dev.children );
 		if ( ( rc = rootdev_probe ( rootdev ) ) != 0 )
diff --git a/gpxe/src/core/downloader.c b/gpxe/src/core/downloader.c
index 0a443db..86c144d 100644
--- a/gpxe/src/core/downloader.c
+++ b/gpxe/src/core/downloader.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <stdarg.h>
 #include <errno.h>
@@ -124,18 +126,37 @@ static int downloader_ensure_size ( struct downloader *downloader,
  * @v job		Downloader job control interface
  */
 static void downloader_job_kill ( struct job_interface *job ) {
-	struct downloader *downloader = 
+	struct downloader *downloader =
 		container_of ( job, struct downloader, job );
 
 	/* Terminate download */
 	downloader_finished ( downloader, -ECANCELED );
 }
 
+/**
+ * Report progress of download job
+ *
+ * @v job		Downloader job control interface
+ * @v progress		Progress report to fill in
+ */
+static void downloader_job_progress ( struct job_interface *job,
+				      struct job_progress *progress ) {
+	struct downloader *downloader =
+		container_of ( job, struct downloader, job );
+
+	/* This is not entirely accurate, since downloaded data may
+	 * arrive out of order (e.g. with multicast protocols), but
+	 * it's a reasonable first approximation.
+	 */
+	progress->completed = downloader->pos;
+	progress->total = downloader->image->len;
+}
+
 /** Downloader job control interface operations */
 static struct job_interface_operations downloader_job_operations = {
 	.done		= ignore_job_done,
 	.kill		= downloader_job_kill,
-	.progress	= ignore_job_progress,
+	.progress	= downloader_job_progress,
 };
 
 /****************************************************************************
@@ -205,7 +226,7 @@ static void downloader_xfer_close ( struct xfer_interface *xfer, int rc ) {
 /** Downloader data transfer interface operations */
 static struct xfer_interface_operations downloader_xfer_operations = {
 	.close		= downloader_xfer_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= downloader_xfer_deliver_iob,
diff --git a/gpxe/src/core/exec.c b/gpxe/src/core/exec.c
index a9861b6..6c16aa4 100644
--- a/gpxe/src/core/exec.c
+++ b/gpxe/src/core/exec.c
@@ -16,10 +16,13 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <ctype.h>
 #include <unistd.h>
 #include <getopt.h>
 #include <errno.h>
@@ -34,11 +37,6 @@
  *
  */
 
-static struct command commands[0]
-	__table_start ( struct command, commands );
-static struct command commands_end[0]
-	__table_end ( struct command, commands );
-
 /* Avoid dragging in getopt.o unless a command really uses it */
 int optind;
 int nextchar;
@@ -78,7 +76,7 @@ int execv ( const char *command, char * const argv[] ) {
 	reset_getopt();
 
 	/* Hand off to command implementation */
-	for ( cmd = commands ; cmd < commands_end ; cmd++ ) {
+	for_each_table_entry ( cmd, COMMANDS ) {
 		if ( strcmp ( command, cmd->name ) == 0 )
 			return cmd->exec ( argc, ( char ** ) argv );
 	}
@@ -173,7 +171,7 @@ static int split_args ( char *args, char * argv[] ) {
 
 	while ( 1 ) {
 		/* Skip over any whitespace / convert to NUL */
-		while ( *args == ' ' ) {
+		while ( isspace ( *args ) ) {
 			if ( argv )
 				*args = '\0';
 			args++;
@@ -186,7 +184,7 @@ static int split_args ( char *args, char * argv[] ) {
 			argv[argc] = args;
 		argc++;
 		/* Skip to start of next whitespace, if any */
-		while ( *args && ( *args != ' ' ) ) {
+		while ( *args && ! isspace ( *args ) ) {
 			args++;
 		}
 	}
diff --git a/gpxe/src/core/filter.c b/gpxe/src/core/filter.c
index f9ebe7c..a8bee7d 100644
--- a/gpxe/src/core/filter.c
+++ b/gpxe/src/core/filter.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/xfer.h>
 #include <gpxe/filter.h>
 
diff --git a/gpxe/src/core/gdbstub.c b/gpxe/src/core/gdbstub.c
index bbed344..c808395 100644
--- a/gpxe/src/core/gdbstub.c
+++ b/gpxe/src/core/gdbstub.c
@@ -54,10 +54,6 @@ struct gdbstub {
 	int len;                         /* length of payload */
 };
 
-/* Transports */
-static struct gdb_transport gdb_transport_start[0] __table_start ( struct gdb_transport, gdb_transports );
-static struct gdb_transport gdb_transport_end[0] __table_end ( struct gdb_transport, gdb_transports );
-
 /* Packet parser states */
 static void gdbstub_state_new ( struct gdbstub *stub, char ch );
 static void gdbstub_state_data ( struct gdbstub *stub, char ch );
@@ -348,12 +344,10 @@ static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ) {
 static void gdbstub_state_wait_ack ( struct gdbstub *stub, char ch ) {
 	if ( ch == '+' ) {
 		stub->parse = gdbstub_state_new;
-	} else if ( ch == '-' ) {
-		gdbstub_tx_packet ( stub ); /* retransmit */
-	} else if ( ch == '$' ) {
-		/* GDB is reconnecting, drop our packet and listen to GDB */
-		stub->trans->send ( "-", 1 );
-		stub->parse = gdbstub_state_new;
+	} else {
+		/* This retransmit is very aggressive but necessary to keep
+		 * in sync with GDB. */
+		gdbstub_tx_packet ( stub );
 	}
 }
 
@@ -387,7 +381,8 @@ void gdbstub_handler ( int signo, gdbreg_t *regs ) {
 
 struct gdb_transport *find_gdb_transport ( const char *name ) {
 	struct gdb_transport *trans;
-	for ( trans = gdb_transport_start; trans < gdb_transport_end; trans++ ) {
+
+	for_each_table_entry ( trans, GDB_TRANSPORTS ) {
 		if ( strcmp ( trans->name, name ) == 0 ) {
 			return trans;
 		}
diff --git a/gpxe/src/core/getkey.c b/gpxe/src/core/getkey.c
index 787c902..dbd074c 100644
--- a/gpxe/src/core/getkey.c
+++ b/gpxe/src/core/getkey.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <ctype.h>
 #include <console.h>
 #include <gpxe/process.h>
diff --git a/gpxe/src/core/getopt.c b/gpxe/src/core/getopt.c
index 6de412b..b67da0c 100644
--- a/gpxe/src/core/getopt.c
+++ b/gpxe/src/core/getopt.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <stdio.h>
diff --git a/gpxe/src/core/image.c b/gpxe/src/core/image.c
index 277d09a..24fe51a 100644
--- a/gpxe/src/core/image.c
+++ b/gpxe/src/core/image.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <string.h>
 #include <stdlib.h>
@@ -37,12 +39,6 @@
 /** List of registered images */
 struct list_head images = LIST_HEAD_INIT ( images );
 
-/** List of image types */
-static struct image_type image_types[0]
-	__table_start ( struct image_type, image_types );
-static struct image_type image_types_end[0]
-	__table_end ( struct image_type, image_types );
-
 /**
  * Free executable/loadable image
  *
@@ -219,7 +215,7 @@ int image_autoload ( struct image *image ) {
 		return image_load ( image );
 
 	/* Otherwise probe for a suitable type */
-	for ( type = image_types ; type < image_types_end ; type++ ) {
+	for_each_table_entry ( type, IMAGE_TYPES ) {
 		DBGC ( image, "IMAGE %p trying type %s\n", image, type->name );
 		rc = image_load_type ( image, type );
 		if ( image->type == NULL )
diff --git a/gpxe/src/core/init.c b/gpxe/src/core/init.c
index 50e199c..cd0f6dc 100644
--- a/gpxe/src/core/init.c
+++ b/gpxe/src/core/init.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/device.h>
 #include <gpxe/init.h>
 
@@ -25,18 +27,6 @@
  *
  */
 
-/** Registered initialisation functions */
-static struct init_fn init_fns[0]
-	__table_start ( struct init_fn, init_fns );
-static struct init_fn init_fns_end[0]
-	__table_end ( struct init_fn, init_fns );
-
-/** Registered startup/shutdown functions */
-static struct startup_fn startup_fns[0]
-	__table_start ( struct startup_fn, startup_fns );
-static struct startup_fn startup_fns_end[0]
-	__table_end ( struct startup_fn, startup_fns );
-
 /** "startup() has been called" flag */
 static int started = 0;
 
@@ -54,9 +44,8 @@ void initialise ( void ) {
 	struct init_fn *init_fn;
 
 	/* Call registered initialisation functions */
-	for ( init_fn = init_fns ; init_fn < init_fns_end ; init_fn++ ) {
+	for_each_table_entry ( init_fn, INIT_FNS )
 		init_fn->initialise ();
-	}
 }
 
 /**
@@ -73,8 +62,7 @@ void startup ( void ) {
 		return;
 
 	/* Call registered startup functions */
-	for ( startup_fn = startup_fns ; startup_fn < startup_fns_end ;
-	      startup_fn++ ) {
+	for_each_table_entry ( startup_fn, STARTUP_FNS ) {
 		if ( startup_fn->startup )
 			startup_fn->startup();
 	}
@@ -90,7 +78,7 @@ void startup ( void ) {
  * This function reverses the actions of startup(), and leaves gPXE in
  * a state ready to be removed from memory.  You may call startup()
  * again after calling shutdown().
-
+ *
  * Call this function only once, before either exiting main() or
  * starting up a non-returnable image.
  */
@@ -101,8 +89,7 @@ void shutdown ( int flags ) {
 		return;
 
 	/* Call registered shutdown functions (in reverse order) */
-	for ( startup_fn = startup_fns_end - 1 ; startup_fn >= startup_fns ;
-	      startup_fn-- ) {
+	for_each_table_entry_reverse ( startup_fn, STARTUP_FNS ) {
 		if ( startup_fn->shutdown )
 			startup_fn->shutdown ( flags );
 	}
diff --git a/gpxe/src/core/interface.c b/gpxe/src/core/interface.c
index 37aabfe..43d58ed 100644
--- a/gpxe/src/core/interface.c
+++ b/gpxe/src/core/interface.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/interface.h>
 
 /** @file
diff --git a/gpxe/src/core/iobuf.c b/gpxe/src/core/iobuf.c
index cc4aede..1ce7890 100644
--- a/gpxe/src/core/iobuf.c
+++ b/gpxe/src/core/iobuf.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <errno.h>
 #include <gpxe/malloc.h>
diff --git a/gpxe/src/core/job.c b/gpxe/src/core/job.c
index 6c2faf3..438064e 100644
--- a/gpxe/src/core/job.c
+++ b/gpxe/src/core/job.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <errno.h>
 #include <gpxe/job.h>
@@ -42,6 +44,14 @@ void job_kill ( struct job_interface *job ) {
 	job_put ( dest );
 }
 
+void job_progress ( struct job_interface *job,
+		    struct job_progress *progress ) {
+	struct job_interface *dest = job_get_dest ( job );
+
+	dest->op->progress ( dest, progress );
+	job_put ( dest );
+}
+
 /****************************************************************************
  *
  * Helper methods
diff --git a/gpxe/src/core/linebuf.c b/gpxe/src/core/linebuf.c
index d02f37c..221f4e1 100644
--- a/gpxe/src/core/linebuf.c
+++ b/gpxe/src/core/linebuf.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/core/main.c b/gpxe/src/core/main.c
index bd2428f..d07d24c 100644
--- a/gpxe/src/core/main.c
+++ b/gpxe/src/core/main.c
@@ -9,11 +9,11 @@ Literature dealing with the network protocols:
 	DHCP - RFC2131, RFC2132 (options)
 	TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
 	RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
-	NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
-	IGMP - RFC1112
 
 **************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <gpxe/init.h>
 #include <gpxe/features.h>
@@ -27,9 +27,6 @@ Literature dealing with the network protocols:
 #define BOLD	"\033[1m"
 #define CYAN	"\033[36m"
 
-static struct feature features[0] __table_start ( struct feature, features );
-static struct feature features_end[0] __table_end ( struct feature, features );
-
 /**
  * Main entry point
  *
@@ -61,7 +58,7 @@ __asmcall int main ( void ) {
 		 NORMAL " -- Open Source Boot Firmware -- "
 		 CYAN "http://etherboot.org" NORMAL "\n"
 		 "Features:" );
-	for ( feature = features ; feature < features_end ; feature++ )
+	for_each_table_entry ( feature, FEATURES )
 		printf ( " %s", feature->name );
 	printf ( "\n" );
 
diff --git a/gpxe/src/core/malloc.c b/gpxe/src/core/malloc.c
index db7f1be..8b0bc24 100644
--- a/gpxe/src/core/malloc.c
+++ b/gpxe/src/core/malloc.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdint.h>
 #include <string.h>
diff --git a/gpxe/src/core/misc.c b/gpxe/src/core/misc.c
index 1f51272..c19591b 100644
--- a/gpxe/src/core/misc.c
+++ b/gpxe/src/core/misc.c
@@ -2,7 +2,10 @@
 MISC Support Routines
 **************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
+#include <ctype.h>
 #include <byteswap.h>
 #include <gpxe/in.h>
 #include <gpxe/timer.h>
@@ -30,20 +33,6 @@ int inet_aton ( const char *cp, struct in_addr *inp ) {
 	return 0;
 }
 
-int isspace ( int c ) {
-	switch ( c ) {
-	case ' ':
-	case '\f':
-	case '\n':
-	case '\r':
-	case '\t':
-	case '\v':
-		return 1;
-	default:
-		return 0;
-	}
-}
-
 unsigned long strtoul ( const char *p, char **endp, int base ) {
 	unsigned long ret = 0;
 	unsigned int charval;
diff --git a/gpxe/src/core/monojob.c b/gpxe/src/core/monojob.c
index 657bfd7..a24b559 100644
--- a/gpxe/src/core/monojob.c
+++ b/gpxe/src/core/monojob.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
diff --git a/gpxe/src/core/nvo.c b/gpxe/src/core/nvo.c
index e5c07d9..3dbf51d 100644
--- a/gpxe/src/core/nvo.c
+++ b/gpxe/src/core/nvo.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/gpxe/src/core/open.c b/gpxe/src/core/open.c
index db8d92e..70b427b 100644
--- a/gpxe/src/core/open.c
+++ b/gpxe/src/core/open.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdarg.h>
 #include <string.h>
 #include <errno.h>
@@ -30,18 +32,6 @@
  *
  */
 
-/** Registered URI openers */
-static struct uri_opener uri_openers[0]
-	__table_start ( struct uri_opener, uri_openers );
-static struct uri_opener uri_openers_end[0]
-	__table_end ( struct uri_opener, uri_openers );
-
-/** Registered socket openers */
-static struct socket_opener socket_openers[0]
-	__table_start ( struct socket_opener, socket_openers );
-static struct socket_opener socket_openers_end[0]
-	__table_end ( struct socket_opener, socket_openers );
-
 /**
  * Open URI
  *
@@ -63,8 +53,10 @@ int xfer_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
 		return -ENOMEM;
 
 	/* Find opener which supports this URI scheme */
-	for ( opener = uri_openers ; opener < uri_openers_end ; opener++ ) {
+	for_each_table_entry ( opener, URI_OPENERS ) {
 		if ( strcmp ( resolved_uri->scheme, opener->scheme ) == 0 ) {
+			DBGC ( xfer, "XFER %p opening %s URI\n",
+			       xfer, opener->scheme );
 			rc = opener->open ( xfer, resolved_uri );
 			goto done;
 		}
@@ -121,7 +113,7 @@ int xfer_open_socket ( struct xfer_interface *xfer, int semantics,
 	       socket_semantics_name ( semantics ),
 	       socket_family_name ( peer->sa_family ) );
 
-	for ( opener = socket_openers; opener < socket_openers_end; opener++ ){
+	for_each_table_entry ( opener, SOCKET_OPENERS ) {
 		if ( ( opener->semantics == semantics ) &&
 		     ( opener->family == peer->sa_family ) ) {
 			return opener->open ( xfer, peer, local );
@@ -182,3 +174,24 @@ int xfer_open ( struct xfer_interface *xfer, int type, ... ) {
 	va_end ( args );
 	return rc;
 }
+
+/**
+ * Reopen location
+ *
+ * @v xfer		Data transfer interface
+ * @v type		Location type
+ * @v args		Remaining arguments depend upon location type
+ * @ret rc		Return status code
+ *
+ * This will close the existing connection and open a new connection
+ * using xfer_vopen().  It is intended to be used as a .vredirect
+ * method handler.
+ */
+int xfer_vreopen ( struct xfer_interface *xfer, int type, va_list args ) {
+
+	/* Close existing connection */
+	xfer_close ( xfer, 0 );
+
+	/* Open new location */
+	return xfer_vopen ( xfer, type, args );
+}
diff --git a/gpxe/src/core/posix_io.c b/gpxe/src/core/posix_io.c
index e0459bd..e6b1a0f 100644
--- a/gpxe/src/core/posix_io.c
+++ b/gpxe/src/core/posix_io.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -137,7 +139,7 @@ posix_file_xfer_deliver_iob ( struct xfer_interface *xfer,
 /** POSIX file data transfer interface operations */
 static struct xfer_interface_operations posix_file_xfer_operations = {
 	.close		= posix_file_xfer_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= posix_file_xfer_deliver_iob,
diff --git a/gpxe/src/core/process.c b/gpxe/src/core/process.c
index 6a68714..9c13e02 100644
--- a/gpxe/src/core/process.c
+++ b/gpxe/src/core/process.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/list.h>
 #include <gpxe/init.h>
 #include <gpxe/process.h>
@@ -31,21 +33,22 @@
 /** Process run queue */
 static LIST_HEAD ( run_queue );
 
-/** Registered permanent processes */
-static struct process processes[0]
-	__table_start ( struct process, processes );
-static struct process processes_end[0]
-	__table_end ( struct process, processes );
-
 /**
  * Add process to process list
  *
  * @v process		Process
+ *
+ * It is safe to call process_add() multiple times; further calls will
+ * have no effect.
  */
 void process_add ( struct process *process ) {
-	DBGC ( process, "PROCESS %p starting\n", process );
-	ref_get ( process->refcnt );
-	list_add_tail ( &process->list, &run_queue );
+	if ( list_empty ( &process->list ) ) {
+		DBGC ( process, "PROCESS %p starting\n", process );
+		ref_get ( process->refcnt );
+		list_add_tail ( &process->list, &run_queue );
+	} else {
+		DBGC ( process, "PROCESS %p already started\n", process );
+	}
 }
 
 /**
@@ -93,9 +96,8 @@ void step ( void ) {
 static void init_processes ( void ) {
 	struct process *process;
 
-	for ( process = processes ; process < processes_end ; process++ ) {
+	for_each_table_entry ( process, PERMANENT_PROCESSES )
 		process_add ( process );
-	}
 }
 
 /** Process initialiser */
diff --git a/gpxe/src/core/random.c b/gpxe/src/core/random.c
index d34e763..6e7374e 100644
--- a/gpxe/src/core/random.c
+++ b/gpxe/src/core/random.c
@@ -4,6 +4,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <gpxe/timer.h>
 
diff --git a/gpxe/src/core/refcnt.c b/gpxe/src/core/refcnt.c
index 30bb6de..f2286ca 100644
--- a/gpxe/src/core/refcnt.c
+++ b/gpxe/src/core/refcnt.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <gpxe/refcnt.h>
 
diff --git a/gpxe/src/core/resolv.c b/gpxe/src/core/resolv.c
index f4a587f..6f01f93 100644
--- a/gpxe/src/core/resolv.c
+++ b/gpxe/src/core/resolv.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -150,12 +152,6 @@ struct resolver numeric_resolver __resolver ( RESOLV_NUMERIC ) = {
  ***************************************************************************
  */
 
-/** Registered name resolvers */
-static struct resolver resolvers[0]
-	__table_start ( struct resolver, resolvers );
-static struct resolver resolvers_end[0]
-	__table_end ( struct resolver, resolvers );
-
 /** A name resolution multiplexer */
 struct resolv_mux {
 	/** Reference counter */
@@ -223,7 +219,7 @@ static void resolv_mux_done ( struct resolv_interface *resolv,
 
 	/* Attempt next child resolver, if possible */
 	mux->resolver++;
-	if ( mux->resolver >= resolvers_end ) {
+	if ( mux->resolver >= table_end ( RESOLVERS ) ) {
 		DBGC ( mux, "RESOLV %p failed to resolve name\n", mux );
 		goto finished;
 	}
@@ -262,7 +258,7 @@ int resolv ( struct resolv_interface *resolv, const char *name,
 		return -ENOMEM;
 	resolv_init ( &mux->parent, &null_resolv_ops, &mux->refcnt );
 	resolv_init ( &mux->child, &resolv_mux_child_ops, &mux->refcnt );
-	mux->resolver = resolvers;
+	mux->resolver = table_start ( RESOLVERS );
 	memcpy ( &mux->sa, sa, sizeof ( mux->sa ) );
 	memcpy ( mux->name, name, name_len );
 
@@ -308,9 +304,36 @@ struct named_socket {
 	int have_local;
 };
 
+/**
+ * Finish using named socket
+ *
+ * @v named		Named socket
+ * @v rc		Reason for finish
+ */
+static void named_done ( struct named_socket *named, int rc ) {
+
+	/* Close all interfaces */
+	resolv_nullify ( &named->resolv );
+	xfer_nullify ( &named->xfer );
+	xfer_close ( &named->xfer, rc );
+}
+
+/**
+ * Handle close() event
+ *
+ * @v xfer		Data transfer interface
+ * @v rc		Reason for close
+ */
+static void named_xfer_close ( struct xfer_interface *xfer, int rc ) {
+	struct named_socket *named =
+		container_of ( xfer, struct named_socket, xfer );
+
+	named_done ( named, rc );
+}
+
 /** Named socket opener data transfer interface operations */
 static struct xfer_interface_operations named_xfer_ops = {
-	.close		= ignore_xfer_close,
+	.close		= named_xfer_close,
 	.vredirect	= ignore_xfer_vredirect,
 	.window		= no_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
@@ -330,10 +353,6 @@ static void named_resolv_done ( struct resolv_interface *resolv,
 	struct named_socket *named =
 		container_of ( resolv, struct named_socket, resolv );
 
-	/* Unplug resolver and nullify data transfer interface */
-	resolv_unplug ( &named->resolv );
-	xfer_nullify ( &named->xfer );
-
 	/* Redirect if name resolution was successful */
 	if ( rc == 0 ) {
 		rc = xfer_redirect ( &named->xfer, LOCATION_SOCKET,
@@ -342,12 +361,8 @@ static void named_resolv_done ( struct resolv_interface *resolv,
 				       &named->local : NULL ) );
 	}
 
-	/* Close data transfer interface if redirection failed */
-	if ( rc != 0 )
-		xfer_close ( &named->xfer, rc );
-
-	/* Unplug data transfer interface */
-	xfer_unplug ( &named->xfer );
+	/* Terminate resolution */
+	named_done ( named, rc );
 }
 
 /** Named socket opener name resolution interface operations */
diff --git a/gpxe/src/core/serial.c b/gpxe/src/core/serial.c
index 5b3be39..d35e89e 100644
--- a/gpxe/src/core/serial.c
+++ b/gpxe/src/core/serial.c
@@ -11,6 +11,8 @@
  * parity, 1 stop bit (8N1).  This can be changed in init_serial().
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "stddef.h"
 #include <gpxe/init.h>
 #include <gpxe/io.h>
diff --git a/gpxe/src/core/settings.c b/gpxe/src/core/settings.c
index f34eb66..7d83101 100644
--- a/gpxe/src/core/settings.c
+++ b/gpxe/src/core/settings.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -37,85 +39,326 @@
  *
  */
 
-/** Registered settings */
-static struct setting settings[0]
-	__table_start ( struct setting, settings );
-static struct setting settings_end[0]
-	__table_end ( struct setting, settings );
+/******************************************************************************
+ *
+ * Generic settings blocks
+ *
+ ******************************************************************************
+ */
 
-/** Registered setting types */
-static struct setting_type setting_types[0]
-	__table_start ( struct setting_type, setting_types );
-static struct setting_type setting_types_end[0]
-	__table_end ( struct setting_type, setting_types );
+/**
+ * A generic setting
+ *
+ */
+struct generic_setting {
+	/** List of generic settings */
+	struct list_head list;
+	/** Setting */
+	struct setting setting;
+	/** Size of setting name */
+	size_t name_len;
+	/** Size of setting data */
+	size_t data_len;
+};
 
-/** Registered settings applicators */
-static struct settings_applicator settings_applicators[0]
-	__table_start ( struct settings_applicator, settings_applicators );
-static struct settings_applicator settings_applicators_end[0]
-	__table_end ( struct settings_applicator, settings_applicators );
+/**
+ * Get generic setting name
+ *
+ * @v generic		Generic setting
+ * @ret name		Generic setting name
+ */
+static inline void * generic_setting_name ( struct generic_setting *generic ) {
+	return ( ( ( void * ) generic ) + sizeof ( *generic ) );
+}
 
-/******************************************************************************
+/**
+ * Get generic setting data
  *
- * Registered settings blocks
+ * @v generic		Generic setting
+ * @ret data		Generic setting data
+ */
+static inline void * generic_setting_data ( struct generic_setting *generic ) {
+	return ( ( ( void * ) generic ) + sizeof ( *generic ) +
+		 generic->name_len );
+}
+
+/**
+ * Find generic setting
  *
- ******************************************************************************
+ * @v generics		Generic settings block
+ * @v setting		Setting to find
+ * @ret generic		Generic setting, or NULL
  */
+static struct generic_setting *
+find_generic_setting ( struct generic_settings *generics,
+		       struct setting *setting ) {
+	struct generic_setting *generic;
+
+	list_for_each_entry ( generic, &generics->list, list ) {
+		if ( setting_cmp ( &generic->setting, setting ) == 0 )
+			return generic;
+	}
+	return NULL;
+}
 
 /**
- * Store value of simple setting
+ * Store value of generic setting
  *
- * @v options		DHCP option block
+ * @v settings		Settings block
  * @v setting		Setting to store
  * @v data		Setting data, or NULL to clear setting
  * @v len		Length of setting data
  * @ret rc		Return status code
  */
-int simple_settings_store ( struct settings *settings, struct setting *setting,
-			    const void *data, size_t len ) {
-	struct simple_settings *simple =
-		container_of ( settings, struct simple_settings, settings );
-	return dhcpopt_extensible_store ( &simple->dhcpopts, setting->tag,
-					  data, len );
+int generic_settings_store ( struct settings *settings,
+			     struct setting *setting,
+			     const void *data, size_t len ) {
+	struct generic_settings *generics =
+		container_of ( settings, struct generic_settings, settings );
+	struct generic_setting *old;
+	struct generic_setting *new = NULL;
+	size_t name_len;
+
+	/* Identify existing generic setting, if any */
+	old = find_generic_setting ( generics, setting );
+
+	/* Create new generic setting, if required */
+	if ( len ) {
+		/* Allocate new generic setting */
+		name_len = ( strlen ( setting->name ) + 1 );
+		new = zalloc ( sizeof ( *new ) + name_len + len );
+		if ( ! new )
+			return -ENOMEM;
+
+		/* Populate new generic setting */
+		new->name_len = name_len;
+		new->data_len = len;
+		memcpy ( &new->setting, setting, sizeof ( new->setting ) );
+		new->setting.name = generic_setting_name ( new );
+		memcpy ( generic_setting_name ( new ),
+			 setting->name, name_len );
+		memcpy ( generic_setting_data ( new ), data, len );
+	}
+
+	/* Delete existing generic setting, if any */
+	if ( old ) {
+		list_del ( &old->list );
+		free ( old );
+	}
+
+	/* Add new setting to list, if any */
+	if ( new )
+		list_add ( &new->list, &generics->list );
+
+	return 0;
 }
 
 /**
- * Fetch value of simple setting
+ * Fetch value of generic setting
  *
- * @v options		DHCP option block
+ * @v settings		Settings block
  * @v setting		Setting to fetch
  * @v data		Buffer to fill with setting data
  * @v len		Length of buffer
  * @ret len		Length of setting data, or negative error
  */
-int simple_settings_fetch ( struct settings *settings, struct setting *setting,
-			    void *data, size_t len ) {
-	struct simple_settings *simple =
-		container_of ( settings, struct simple_settings, settings );
-	return dhcpopt_fetch ( &simple->dhcpopts, setting->tag, data, len );
+int generic_settings_fetch ( struct settings *settings,
+			     struct setting *setting,
+			     void *data, size_t len ) {
+	struct generic_settings *generics =
+		container_of ( settings, struct generic_settings, settings );
+	struct generic_setting *generic;
+
+	/* Find generic setting */
+	generic = find_generic_setting ( generics, setting );
+	if ( ! generic )
+		return -ENOENT;
+
+	/* Copy out generic setting data */
+	if ( len > generic->data_len )
+		len = generic->data_len;
+	memcpy ( data, generic_setting_data ( generic ), len );
+	return generic->data_len;
+}
+
+/**
+ * Clear generic settings block
+ *
+ * @v settings		Settings block
+ */
+void generic_settings_clear ( struct settings *settings ) {
+	struct generic_settings *generics =
+		container_of ( settings, struct generic_settings, settings );
+	struct generic_setting *generic;
+	struct generic_setting *tmp;
+
+	list_for_each_entry_safe ( generic, tmp, &generics->list, list ) {
+		list_del ( &generic->list );
+		free ( generic );
+	}
+	assert ( list_empty ( &generics->list ) );
 }
 
-/** Simple settings operations */
-struct settings_operations simple_settings_operations = {
-	.store = simple_settings_store,
-	.fetch = simple_settings_fetch,
+/** Generic settings operations */
+struct settings_operations generic_settings_operations = {
+	.store = generic_settings_store,
+	.fetch = generic_settings_fetch,
+	.clear = generic_settings_clear,
 };
 
-/** Root simple settings block */
-struct simple_settings simple_settings_root = {
+/******************************************************************************
+ *
+ * Registered settings blocks
+ *
+ ******************************************************************************
+ */
+
+/** Root generic settings block */
+struct generic_settings generic_settings_root = {
 	.settings = {
 		.refcnt = NULL,
 		.name = "",
 		.siblings =
-		     LIST_HEAD_INIT ( simple_settings_root.settings.siblings ),
+		    LIST_HEAD_INIT ( generic_settings_root.settings.siblings ),
 		.children =
-		     LIST_HEAD_INIT ( simple_settings_root.settings.children ),
-		.op = &simple_settings_operations,
+		    LIST_HEAD_INIT ( generic_settings_root.settings.children ),
+		.op = &generic_settings_operations,
 	},
+	.list = LIST_HEAD_INIT ( generic_settings_root.list ),
 };
 
 /** Root settings block */
-#define settings_root simple_settings_root.settings
+#define settings_root generic_settings_root.settings
+
+/**
+ * Find child named settings block
+ *
+ * @v parent		Parent settings block
+ * @v name		Name within this parent
+ * @ret settings	Settings block, or NULL
+ */
+static struct settings * find_child_settings ( struct settings *parent,
+					       const char *name ) {
+	struct settings *settings;
+
+	/* Treat empty name as meaning "this block" */
+	if ( ! *name )
+		return parent;
+
+	/* Look for child with matching name */
+	list_for_each_entry ( settings, &parent->children, siblings ) {
+		if ( strcmp ( settings->name, name ) == 0 )
+			return settings;
+	}
+
+	return NULL;
+}
+
+/**
+ * Find or create child named settings block
+ *
+ * @v parent		Parent settings block
+ * @v name		Name within this parent
+ * @ret settings	Settings block, or NULL
+ */
+static struct settings * autovivify_child_settings ( struct settings *parent,
+						     const char *name ) {
+	struct {
+		struct generic_settings generic;
+		char name[ strlen ( name ) + 1 /* NUL */ ];
+	} *new_child;
+	struct settings *settings;
+
+	/* Return existing settings, if existent */
+	if ( ( settings = find_child_settings ( parent, name ) ) != NULL )
+		return settings;
+
+	/* Create new generic settings block */
+	new_child = zalloc ( sizeof ( *new_child ) );
+	if ( ! new_child ) {
+		DBGC ( parent, "Settings %p could not create child %s\n",
+		       parent, name );
+		return NULL;
+	}
+	memcpy ( new_child->name, name, sizeof ( new_child->name ) );
+	generic_settings_init ( &new_child->generic, NULL, new_child->name );
+	settings = &new_child->generic.settings;
+	register_settings ( settings, parent );
+	return settings;
+}
+
+/**
+ * Return settings block name (for debug only)
+ *
+ * @v settings		Settings block
+ * @ret name		Settings block name
+ */
+static const char * settings_name ( struct settings *settings ) {
+	static char buf[64];
+	char tmp[ sizeof ( buf ) ];
+	int count;
+
+	for ( count = 0 ; settings ; settings = settings->parent ) {
+		memcpy ( tmp, buf, sizeof ( tmp ) );
+		snprintf ( buf, sizeof ( buf ), "%s%c%s", settings->name,
+			   ( count++ ? '.' : '\0' ), tmp );
+	}
+	return ( buf + 1 );
+}
+
+/**
+ * Parse settings block name
+ *
+ * @v name		Name
+ * @v get_child		Function to find or create child settings block
+ * @ret settings	Settings block, or NULL
+ */
+static struct settings *
+parse_settings_name ( const char *name,
+		      struct settings * ( * get_child ) ( struct settings *,
+							  const char * ) ) {
+	struct settings *settings = &settings_root;
+	char name_copy[ strlen ( name ) + 1 ];
+	char *subname;
+	char *remainder;
+
+	/* Create modifiable copy of name */
+	memcpy ( name_copy, name, sizeof ( name_copy ) );
+	remainder = name_copy;
+
+	/* Parse each name component in turn */
+	while ( remainder ) {
+		struct net_device *netdev;
+
+		subname = remainder;
+		remainder = strchr ( subname, '.' );
+		if ( remainder )
+			*(remainder++) = '\0';
+
+		/* Special case "netX" root settings block */
+		if ( ( subname == name_copy ) && ! strcmp ( subname, "netX" ) &&
+		     ( ( netdev = last_opened_netdev() ) != NULL ) )
+			settings = get_child ( settings, netdev->name );
+		else
+			settings = get_child ( settings, subname );
+
+		if ( ! settings )
+			break;
+	}
+
+	return settings;
+}
+
+/**
+ * Find named settings block
+ *
+ * @v name		Name
+ * @ret settings	Settings block, or NULL
+ */
+struct settings * find_settings ( const char *name ) {
+
+	return parse_settings_name ( name, find_child_settings );
+}
 
 /**
  * Apply all settings
@@ -127,8 +370,7 @@ static int apply_settings ( void ) {
 	int rc;
 
 	/* Call all settings applicators */
-	for ( applicator = settings_applicators ;
-	      applicator < settings_applicators_end ; applicator++ ) {
+	for_each_table_entry ( applicator, SETTINGS_APPLICATORS ) {
 		if ( ( rc = applicator->apply() ) != 0 ) {
 			DBG ( "Could not apply settings using applicator "
 			      "%p: %s\n", applicator, strerror ( rc ) );
@@ -199,7 +441,8 @@ int register_settings ( struct settings *settings, struct settings *parent ) {
 	ref_get ( parent->refcnt );
 	settings->parent = parent;
 	list_add_tail ( &settings->siblings, &parent->children );
-	DBGC ( settings, "Settings %p registered\n", settings );
+	DBGC ( settings, "Settings %p (\"%s\") registered\n",
+	       settings, settings_name ( settings ) );
 
 	/* Fix up settings priority */
 	reprioritise_settings ( settings );
@@ -217,63 +460,19 @@ int register_settings ( struct settings *settings, struct settings *parent ) {
  */
 void unregister_settings ( struct settings *settings ) {
 
+	DBGC ( settings, "Settings %p (\"%s\") unregistered\n",
+	       settings, settings_name ( settings ) );
+
 	/* Remove from list of settings */
 	ref_put ( settings->refcnt );
 	ref_put ( settings->parent->refcnt );
 	settings->parent = NULL;
 	list_del ( &settings->siblings );
-	DBGC ( settings, "Settings %p unregistered\n", settings );
 
 	/* Apply potentially-updated settings */
 	apply_settings();
 }
 
-/**
- * Find child named settings block
- *
- * @v parent		Parent settings block
- * @v name		Name within this parent
- * @ret settings	Settings block, or NULL
- */
-struct settings * find_child_settings ( struct settings *parent,
-					const char *name ) {
-	struct settings *settings;
-	size_t len;
-
-	/* NULL parent => add to settings root */
-	if ( parent == NULL )
-		parent = &settings_root;
-
-	/* Look for a child whose name matches the initial component */
-	list_for_each_entry ( settings, &parent->children, siblings ) {
-		len = strlen ( settings->name );
-		if ( strncmp ( name, settings->name, len ) != 0 )
-			continue;
-		if ( name[len] == 0 )
-			return settings;
-		if ( name[len] == '.' )
-			return find_child_settings ( settings,
-						     ( name + len + 1 ) );
-	}
-
-	return NULL;
-}
-
-/**
- * Find named settings block
- *
- * @v name		Name
- * @ret settings	Settings block, or NULL
- */
-struct settings * find_settings ( const char *name ) {
-
-	/* If name is empty, use the root */
-	if ( ! *name )
-		return &settings_root;
-
-	return find_child_settings ( &settings_root, name );
-}
-
 /******************************************************************************
  *
  * Core settings routines
@@ -298,6 +497,10 @@ int store_setting ( struct settings *settings, struct setting *setting,
 	if ( ! settings )
 		settings = &settings_root;
 
+	/* Sanity check */
+	if ( ! settings->op->store )
+		return -ENOTSUP;
+
 	/* Store setting */
 	if ( ( rc = settings->op->store ( settings, setting,
 					  data, len ) ) != 0 )
@@ -345,6 +548,10 @@ int fetch_setting ( struct settings *settings, struct setting *setting,
 	if ( ! settings )
 		settings = &settings_root;
 
+	/* Sanity check */
+	if ( ! settings->op->fetch )
+		return -ENOTSUP;
+
 	/* Try this block first */
 	if ( ( ret = settings->op->fetch ( settings, setting,
 					   data, len ) ) >= 0 )
@@ -411,7 +618,7 @@ int fetch_string_setting_copy ( struct settings *settings,
 				struct setting *setting,
 				char **data ) {
 	int len;
-	int check_len;
+	int check_len = 0;
 
 	len = fetch_setting_len ( settings, setting );
 	if ( len < 0 )
@@ -421,7 +628,8 @@ int fetch_string_setting_copy ( struct settings *settings,
 	if ( ! *data )
 		return -ENOMEM;
 
-	fetch_string_setting ( settings, setting, *data, ( len + 1 ) );
+	check_len = fetch_string_setting ( settings, setting, *data,
+					   ( len + 1 ) );
 	assert ( check_len == len );
 	return len;
 }
@@ -504,7 +712,8 @@ int fetch_uint_setting ( struct settings *settings, struct setting *setting,
 		return len;
 
 	/* Mask off sign-extended bits */
-	*value = ( svalue & ( -1UL >> ( sizeof ( long ) - len ) ) );
+	assert ( len <= ( int ) sizeof ( long ) );
+	*value = ( svalue & ( -1UL >> ( 8 * ( sizeof ( long ) - len ) ) ) );
 
 	return len;
 }
@@ -559,6 +768,16 @@ int fetch_uuid_setting ( struct settings *settings, struct setting *setting,
 }
 
 /**
+ * Clear settings block
+ *
+ * @v settings		Settings block
+ */
+void clear_settings ( struct settings *settings ) {
+	if ( settings->op->clear )
+		settings->op->clear ( settings );
+}
+
+/**
  * Compare two settings
  *
  * @v a			Setting to compare
@@ -572,8 +791,12 @@ int setting_cmp ( struct setting *a, struct setting *b ) {
 	if ( a->tag && ( a->tag == b->tag ) )
 		return 0;
 
-	/* Otherwise, compare the names */
-	return strcmp ( a->name, b->name );
+	/* Otherwise, if the settings have names, compare them */
+	if ( a->name && b->name && a->name[0] )
+		return strcmp ( a->name, b->name );
+
+	/* Otherwise, return a non-match */
+	return ( ! 0 );
 }
 
 /******************************************************************************
@@ -614,7 +837,7 @@ int storef_setting ( struct settings *settings, struct setting *setting,
 static struct setting * find_setting ( const char *name ) {
 	struct setting *setting;
 
-	for ( setting = settings ; setting < settings_end ; setting++ ) {
+	for_each_table_entry ( setting, SETTINGS ) {
 		if ( strcmp ( name, setting->name ) == 0 )
 			return setting;
 	}
@@ -622,6 +845,26 @@ static struct setting * find_setting ( const char *name ) {
 }
 
 /**
+ * Parse setting name as tag number
+ *
+ * @v name		Name
+ * @ret tag		Tag number, or 0 if not a valid number
+ */
+static unsigned int parse_setting_tag ( const char *name ) {
+	char *tmp = ( ( char * ) name );
+	unsigned int tag = 0;
+
+	while ( 1 ) {
+		tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) );
+		if ( *tmp == 0 )
+			return tag;
+		if ( *tmp != '.' )
+			return 0;
+		tmp++;
+	}
+}
+
+/**
  * Find setting type
  *
  * @v name		Name
@@ -630,7 +873,7 @@ static struct setting * find_setting ( const char *name ) {
 static struct setting_type * find_setting_type ( const char *name ) {
 	struct setting_type *type;
 
-	for ( type = setting_types ; type < setting_types_end ; type++ ) {
+	for_each_table_entry ( type, SETTING_TYPES ) {
 		if ( strcmp ( name, type->name ) == 0 )
 			return type;
 	}
@@ -641,30 +884,38 @@ static struct setting_type * find_setting_type ( const char *name ) {
  * Parse setting name
  *
  * @v name		Name of setting
+ * @v get_child		Function to find or create child settings block
  * @v settings		Settings block to fill in
  * @v setting		Setting to fill in
+ * @v tmp_name		Buffer for copy of setting name
  * @ret rc		Return status code
  *
  * Interprets a name of the form
  * "[settings_name/]tag_name[:type_name]" and fills in the appropriate
  * fields.
+ *
+ * The @c tmp_name buffer must be large enough to hold a copy of the
+ * setting name.
  */
-static int parse_setting_name ( const char *name, struct settings **settings,
-				struct setting *setting ) {
-	char tmp_name[ strlen ( name ) + 1 ];
+static int
+parse_setting_name ( const char *name,
+		     struct settings * ( * get_child ) ( struct settings *,
+							 const char * ),
+		     struct settings **settings, struct setting *setting,
+		     char *tmp_name ) {
 	char *settings_name;
 	char *setting_name;
 	char *type_name;
 	struct setting *named_setting;
-	char *tmp;
 
 	/* Set defaults */
 	*settings = &settings_root;
 	memset ( setting, 0, sizeof ( *setting ) );
-	setting->type = &setting_type_hex;
+	setting->name = "";
+	setting->type = &setting_type_string;
 
 	/* Split name into "[settings_name/]setting_name[:type_name]" */
-	memcpy ( tmp_name, name, sizeof ( tmp_name ) );
+	strcpy ( tmp_name, name );
 	if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) {
 		*(setting_name++) = 0;
 		settings_name = tmp_name;
@@ -677,7 +928,7 @@ static int parse_setting_name ( const char *name, struct settings **settings,
 
 	/* Identify settings block, if specified */
 	if ( settings_name ) {
-		*settings = find_settings ( settings_name );
+		*settings = parse_settings_name ( settings_name, get_child );
 		if ( *settings == NULL ) {
 			DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n",
 			      settings_name, name );
@@ -685,25 +936,16 @@ static int parse_setting_name ( const char *name, struct settings **settings,
 		}
 	}
 
-	/* Identify tag number */
+	/* Identify setting */
 	if ( ( named_setting = find_setting ( setting_name ) ) != NULL ) {
+		/* Matches a defined named setting; use that setting */
 		memcpy ( setting, named_setting, sizeof ( *setting ) );
-	} else {
-		/* Unrecognised name: try to interpret as a tag number */
-		tmp = setting_name;
-		while ( 1 ) {
-			setting->tag = ( ( setting->tag << 8 ) |
-					 strtoul ( tmp, &tmp, 0 ) );
-			if ( *tmp == 0 )
-				break;
-			if ( *tmp != '.' ) {
-				DBG ( "Invalid setting \"%s\" in \"%s\"\n",
-				      setting_name, name );
-				return -ENOENT;
-			}
-			tmp++;
-		}
+	} else if ( ( setting->tag = parse_setting_tag ( setting_name ) ) !=0){
+		/* Is a valid numeric tag; use the tag */
 		setting->tag |= (*settings)->tag_magic;
+	} else {
+		/* Use the arbitrary name */
+		setting->name = setting_name;
 	}
 
 	/* Identify setting type, if specified */
@@ -729,9 +971,11 @@ static int parse_setting_name ( const char *name, struct settings **settings,
 int storef_named_setting ( const char *name, const char *value ) {
 	struct settings *settings;
 	struct setting setting;
+	char tmp_name[ strlen ( name ) + 1 ];
 	int rc;
 
-	if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 )
+	if ( ( rc = parse_setting_name ( name, autovivify_child_settings,
+					 &settings, &setting, tmp_name )) != 0)
 		return rc;
 	return storef_setting ( settings, &setting, value );
 }
@@ -747,9 +991,11 @@ int storef_named_setting ( const char *name, const char *value ) {
 int fetchf_named_setting ( const char *name, char *buf, size_t len ) {
 	struct settings *settings;
 	struct setting setting;
+	char tmp_name[ strlen ( name ) + 1 ];
 	int rc;
 
-	if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 )
+	if ( ( rc = parse_setting_name ( name, find_child_settings,
+					 &settings, &setting, tmp_name )) != 0)
 		return rc;
 	return fetchf_setting ( settings, &setting, buf, len );
 }
@@ -839,7 +1085,7 @@ static int fetchf_uristring ( struct settings *settings,
        
 		fetch_string_setting ( settings, setting, raw_buf,
 				       sizeof ( raw_buf ) );
-		return uri_encode ( raw_buf, buf, len );
+		return uri_encode ( raw_buf, buf, len, URI_FRAGMENT );
 	}
 }
 
@@ -1180,7 +1426,7 @@ struct setting filename_setting __setting = {
 /** Root path setting */
 struct setting root_path_setting __setting = {
 	.name = "root-path",
-	.description = "NFS/iSCSI root path",
+	.description = "iSCSI root path",
 	.tag = DHCP_ROOT_PATH,
 	.type = &setting_type_string,
 };
diff --git a/gpxe/src/core/string.c b/gpxe/src/core/string.c
index 2e17bdc..190007a 100644
--- a/gpxe/src/core/string.c
+++ b/gpxe/src/core/string.c
@@ -10,6 +10,8 @@
  * published by the Free Software Foundation.
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /*
  * stupid library routines.. The optimized versions should generally be found
  * as inline code in <asm-xx/string.h>
diff --git a/gpxe/src/core/timer.c b/gpxe/src/core/timer.c
index d71e3da..096d07e 100644
--- a/gpxe/src/core/timer.c
+++ b/gpxe/src/core/timer.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <unistd.h>
 
 /**
diff --git a/gpxe/src/core/uri.c b/gpxe/src/core/uri.c
index 7bb46da..6a1f2e5 100644
--- a/gpxe/src/core/uri.c
+++ b/gpxe/src/core/uri.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** @file
  *
  * Uniform Resource Identifiers
@@ -74,6 +76,7 @@ struct uri * parse_uri ( const char *uri_string ) {
 	char *tmp;
 	char *path = NULL;
 	char *authority = NULL;
+	int i;
 	size_t raw_len;
 
 	/* Allocate space for URI struct and a copy of the string */
@@ -83,7 +86,7 @@ struct uri * parse_uri ( const char *uri_string ) {
 		return NULL;
 	raw = ( ( ( char * ) uri ) + sizeof ( *uri ) );
 
-	/* Zero URI struct and copy in the raw string */
+	/* Copy in the raw string */
 	memcpy ( raw, uri_string, raw_len );
 
 	/* Start by chopping off the fragment, if it exists */
@@ -169,6 +172,14 @@ struct uri * parse_uri ( const char *uri_string ) {
 		uri->port = tmp;
 	}
 
+	/* Decode fields that should be decoded */
+	for ( i = URI_FIRST_FIELD; i <= URI_LAST_FIELD; i++ ) {
+		const char *field = uri_get_field ( uri, i );
+		if ( field && ( URI_ENCODED & ( 1 << i ) ) )
+			uri_decode ( field, ( char * ) field,
+				     strlen ( field ) + 1 /* NUL */ );
+	}
+
  done:
 	DBG ( "URI \"%s\" split into", uri_string );
 	dump_uri ( uri );
@@ -196,71 +207,65 @@ unsigned int uri_port ( struct uri *uri, unsigned int default_port ) {
  * @v buf		Buffer to fill with URI string
  * @v size		Size of buffer
  * @v uri		URI to write into buffer, or NULL
+ * @v fields		Bitmask of fields to include in URI string, or URI_ALL
  * @ret len		Length of URI string
  */
-int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
+int unparse_uri ( char *buf, size_t size, struct uri *uri,
+		  unsigned int fields ) {
+	/* List of characters that typically go before certain fields */
+	static char separators[] = { /* scheme */ 0, /* opaque */ ':',
+				     /* user */ 0, /* password */ ':',
+				     /* host */ '@', /* port */ ':',
+				     /* path */ 0, /* query */ '?',
+				     /* fragment */ '#' };
 	int used = 0;
+	int i;
 
 	DBG ( "URI unparsing" );
 	dump_uri ( uri );
 	DBG ( "\n" );
 
+	/* Ensure buffer is NUL-terminated */
+	if ( size )
+		buf[0] = '\0';
+
 	/* Special-case NULL URI */
-	if ( ! uri ) {
-		if ( size )
-			buf[0] = '\0';
+	if ( ! uri )
 		return 0;
-	}
-
-	/* Special-case opaque URIs */
-	if ( uri->opaque ) {
-		return ssnprintf ( ( buf + used ), ( size - used ),
-				   "%s:%s", uri->scheme, uri->opaque );
-	}
 
-	/* scheme:// */
-	if ( uri->scheme ) {
-		used += ssnprintf ( ( buf + used ), ( size - used ),
-				    "%s://", uri->scheme );
-	}
-
-	/* [user[:password]@]host[:port] */
-	if ( uri->host ) {
-		if ( uri->user ) {
-			used += ssnprintf ( ( buf + used ), ( size - used ),
-					    "%s", uri->user );
-			if ( uri->password ) {
-				used += ssnprintf ( ( buf + used ),
-						    ( size - used ),
-						    ":%s", uri->password );
+	/* Iterate through requested fields */
+	for ( i = URI_FIRST_FIELD; i <= URI_LAST_FIELD; i++ ) {
+		const char *field = uri_get_field ( uri, i );
+		char sep = separators[i];
+
+		/* Ensure `fields' only contains bits for fields that exist */
+		if ( ! field )
+			fields &= ~( 1 << i );
+
+		/* Store this field if we were asked to */
+		if ( fields & ( 1 << i ) ) {
+			/* Print :// if we're non-opaque and had a scheme */
+			if ( ( fields & URI_SCHEME_BIT ) &&
+			     ( i > URI_OPAQUE ) ) {
+				used += ssnprintf ( buf + used, size - used,
+						    "://" );
+				/* Only print :// once */
+				fields &= ~URI_SCHEME_BIT;
 			}
-			used += ssnprintf ( ( buf + used ), ( size - used ),
-					    "@" );
-		}
-		used += ssnprintf ( ( buf + used ), ( size - used ), "%s",
-				    uri->host );
-		if ( uri->port ) {
-			used += ssnprintf ( ( buf + used ), ( size - used ),
-					    ":%s", uri->port );
-		}
-	}
-
-	/* /path */
-	if ( uri->path ) {
-		used += ssnprintf ( ( buf + used ), ( size - used ),
-				    "%s", uri->path );
-	}
 
-	/* ?query */
-	if ( uri->query ) {
-		used += ssnprintf ( ( buf + used ), ( size - used ),
-				    "?%s", uri->query );
-	}
-
-	/* #fragment */
-	if ( uri->fragment ) {
-		used += ssnprintf ( ( buf + used ), ( size - used ),
-				    "#%s", uri->fragment );
+			/* Only print separator if an earlier field exists */
+			if ( sep && ( fields & ( ( 1 << i ) - 1 ) ) )
+				used += ssnprintf ( buf + used, size - used,
+						    "%c", sep );
+
+			/* Print contents of field, possibly encoded */
+			if ( URI_ENCODED & ( 1 << i ) )
+				used += uri_encode ( field, buf + used,
+						     size - used, i );
+			else
+				used += ssnprintf ( buf + used, size - used,
+						    "%s", field );
+		}
 	}
 
 	return used;
@@ -275,10 +280,10 @@ int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
  * Creates a modifiable copy of a URI.
  */
 struct uri * uri_dup ( struct uri *uri ) {
-	size_t len = ( unparse_uri ( NULL, 0, uri ) + 1 );
+	size_t len = ( unparse_uri ( NULL, 0, uri, URI_ALL ) + 1 );
 	char buf[len];
 
-	unparse_uri ( buf, len, uri );
+	unparse_uri ( buf, len, uri, URI_ALL );
 	return parse_uri ( buf );
 }
 
@@ -391,16 +396,31 @@ struct uri * resolve_uri ( struct uri *base_uri,
  * Test for unreserved URI characters
  *
  * @v c			Character to test
+ * @v field		Field of URI in which character lies
  * @ret is_unreserved	Character is an unreserved character
  */
-static int is_unreserved_uri_char ( int c ) {
+static int is_unreserved_uri_char ( int c, int field ) {
 	/* According to RFC3986, the unreserved character set is
 	 *
 	 * A-Z a-z 0-9 - _ . ~
+	 *
+	 * but we also pass & ; = in queries, / in paths,
+	 * and everything in opaques
 	 */
-	return ( isupper ( c ) || islower ( c ) || isdigit ( c ) ||
-		 ( c == '-' ) || ( c == '_' ) ||
-		 ( c == '.' ) || ( c == '~' ) );
+	int ok = ( isupper ( c ) || islower ( c ) || isdigit ( c ) ||
+		    ( c == '-' ) || ( c == '_' ) ||
+		    ( c == '.' ) || ( c == '~' ) );
+
+	if ( field == URI_QUERY )
+		ok = ok || ( c == ';' ) || ( c == '&' ) || ( c == '=' );
+
+	if ( field == URI_PATH )
+		ok = ok || ( c == '/' );
+
+	if ( field == URI_OPAQUE )
+		ok = 1;
+
+	return ok;
 }
 
 /**
@@ -409,18 +429,20 @@ static int is_unreserved_uri_char ( int c ) {
  * @v raw_string	String to be URI-encoded
  * @v buf		Buffer to contain encoded string
  * @v len		Length of buffer
+ * @v field		Field of URI in which string lies
  * @ret len		Length of encoded string (excluding NUL)
  */
-size_t uri_encode ( const char *raw_string, char *buf, size_t len ) {
+size_t uri_encode ( const char *raw_string, char *buf, ssize_t len,
+		    int field ) {
 	ssize_t remaining = len;
 	size_t used;
 	unsigned char c;
 
-	if ( len )
+	if ( len > 0 )
 		buf[0] = '\0';
 
 	while ( ( c = *(raw_string++) ) ) {
-		if ( is_unreserved_uri_char ( c ) ) {
+		if ( is_unreserved_uri_char ( c, field ) ) {
 			used = ssnprintf ( buf, remaining, "%c", c );
 		} else {
 			used = ssnprintf ( buf, remaining, "%%%02X", c );
@@ -439,17 +461,17 @@ size_t uri_encode ( const char *raw_string, char *buf, size_t len ) {
  * @v buf		Buffer to contain decoded string
  * @v len		Length of buffer
  * @ret len		Length of decoded string (excluding NUL)
+ *
+ * This function may be used in-place, with @a buf the same as
+ * @a encoded_string.
  */
-size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
-	ssize_t remaining = len;
+size_t uri_decode ( const char *encoded_string, char *buf, ssize_t len ) {
+	ssize_t remaining;
 	char hexbuf[3];
 	char *hexbuf_end;
 	unsigned char c;
 
-	if ( len )
-		buf[0] = '\0';
-
-	while ( *encoded_string ) {
+	for ( remaining = len; *encoded_string; remaining-- ) {
 		if ( *encoded_string == '%' ) {
 			encoded_string++;
 			snprintf ( hexbuf, sizeof ( hexbuf ), "%s",
@@ -459,7 +481,12 @@ size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
 		} else {
 			c = *(encoded_string++);
 		}
-		ssnprintf ( buf++, remaining--, "%c", c );
+		if ( remaining > 1 )
+			*buf++ = c;
 	}
+
+	if ( len )
+		*buf = 0;
+
 	return ( len - remaining );
 }
diff --git a/gpxe/src/core/uuid.c b/gpxe/src/core/uuid.c
index a3a82c6..2b67d55 100644
--- a/gpxe/src/core/uuid.c
+++ b/gpxe/src/core/uuid.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdio.h>
 #include <byteswap.h>
diff --git a/gpxe/src/core/vsprintf.c b/gpxe/src/core/vsprintf.c
index 4457fe4..21ab242 100644
--- a/gpxe/src/core/vsprintf.c
+++ b/gpxe/src/core/vsprintf.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdarg.h>
 #include <stdio.h>
diff --git a/gpxe/src/core/xfer.c b/gpxe/src/core/xfer.c
index 9ed19da..1ec6f9d 100644
--- a/gpxe/src/core/xfer.c
+++ b/gpxe/src/core/xfer.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
@@ -43,11 +45,14 @@ static struct xfer_metadata dummy_metadata;
  */
 void xfer_close ( struct xfer_interface *xfer, int rc ) {
 	struct xfer_interface *dest = xfer_get_dest ( xfer );
+	struct xfer_interface_operations *op = xfer->op;
 
 	DBGC ( xfer, "XFER %p->%p close\n", xfer, dest );
 
 	xfer_unplug ( xfer );
+	xfer_nullify ( xfer );
 	dest->op->close ( dest, rc );
+	xfer->op = op;
 	xfer_put ( dest );
 }
 
diff --git a/gpxe/src/crypto/aes_wrap.c b/gpxe/src/crypto/aes_wrap.c
new file mode 100644
index 0000000..d7f94af
--- /dev/null
+++ b/gpxe/src/crypto/aes_wrap.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <string.h>
+#include <gpxe/crypto.h>
+#include <gpxe/aes.h>
+
+/**
+ * Wrap a key or other data using AES Key Wrap (RFC 3394)
+ *
+ * @v kek	Key Encryption Key, 16 bytes
+ * @v src	Data to encrypt
+ * @v nblk	Number of 8-byte blocks in @a data
+ * @ret dest	Encrypted data (8 bytes longer than input)
+ *
+ * The algorithm is implemented such that @a src and @a dest may point
+ * to the same buffer.
+ */
+int aes_wrap ( const void *kek, const void *src, void *dest, int nblk )
+{
+	u8 *A = dest;
+	u8 B[16];
+	u8 *R;
+	int i, j;
+	void *aes_ctx = malloc ( AES_CTX_SIZE );
+
+	if ( ! aes_ctx )
+		return -1;
+
+	cipher_setkey ( &aes_algorithm, aes_ctx, kek, 16 );
+
+	/* Set up */
+	memset ( A, 0xA6, sizeof ( A ) );
+	memmove ( dest + 8, src, nblk * 8 );
+
+	/* Wrap */
+	for ( j = 0; j < 6; j++ ) {
+		R = dest + 8;
+		for ( i = 1; i <= nblk; i++ ) {
+			memcpy ( B, A, 8 );
+			memcpy ( B + 8, R, 8 );
+			cipher_encrypt ( &aes_algorithm, aes_ctx, B, B, 16 );
+			memcpy ( A, B, 8 );
+			A[7] ^= ( nblk * j ) + i;
+			memcpy ( R, B + 8, 8 );
+			R += 8;
+		}
+	}
+
+	free ( aes_ctx );
+	return 0;
+}
+
+/**
+ * Unwrap a key or other data using AES Key Wrap (RFC 3394)
+ *
+ * @v kek	Key Encryption Key, 16 bytes
+ * @v src	Data to decrypt
+ * @v nblk	Number of 8-byte blocks in @e plaintext key
+ * @ret dest	Decrypted data (8 bytes shorter than input)
+ * @ret rc	Zero on success, nonzero on IV mismatch
+ *
+ * The algorithm is implemented such that @a src and @a dest may point
+ * to the same buffer.
+ */
+int aes_unwrap ( const void *kek, const void *src, void *dest, int nblk )
+{
+	u8 A[8], B[16];
+	u8 *R;
+	int i, j;
+	void *aes_ctx = malloc ( AES_CTX_SIZE );
+
+	if ( ! aes_ctx )
+		return -1;
+
+	cipher_setkey ( &aes_algorithm, aes_ctx, kek, 16 );
+
+	/* Set up */
+	memcpy ( A, src, 8 );
+	memmove ( dest, src + 8, nblk * 8 );
+
+	/* Unwrap */
+	for ( j = 5; j >= 0; j-- ) {
+		R = dest + ( nblk - 1 ) * 8;
+		for ( i = nblk; i >= 1; i-- ) {
+			memcpy ( B, A, 8 );
+			memcpy ( B + 8, R, 8 );
+			B[7] ^= ( nblk * j ) + i;
+			cipher_decrypt ( &aes_algorithm, aes_ctx, B, B, 16 );
+			memcpy ( A, B, 8 );
+			memcpy ( R, B + 8, 8 );
+			R -= 8;
+		}
+	}
+
+	free ( aes_ctx );
+
+	/* Check IV */
+	for ( i = 0; i < 8; i++ ) {
+		if ( A[i] != 0xA6 )
+			return -1;
+	}
+
+	return 0;
+}
diff --git a/gpxe/src/crypto/arc4.c b/gpxe/src/crypto/arc4.c
new file mode 100644
index 0000000..e58fba7
--- /dev/null
+++ b/gpxe/src/crypto/arc4.c
@@ -0,0 +1,131 @@
+/*
+ * The ARC4 stream cipher.
+ *
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/crypto.h>
+#include <gpxe/arc4.h>
+
+#define SWAP( ary, i, j )	\
+	({ u8 temp = ary[i]; ary[i] = ary[j]; ary[j] = temp; })
+
+/**
+ * Set ARC4 key
+ *
+ * @v ctxv	ARC4 encryption context
+ * @v keyv	Key to set
+ * @v keylen	Length of key
+ *
+ * If an initialisation vector is to be used, it should be prepended
+ * to the key; ARC4 does not implement the @c setiv function because
+ * there is no standard length for an initialisation vector in the
+ * cipher.
+ */
+static int arc4_setkey ( void *ctxv, const void *keyv, size_t keylen )
+{
+	struct arc4_ctx *ctx = ctxv;
+	const u8 *key = keyv;
+	u8 *S = ctx->state;
+	int i, j;
+
+	for ( i = 0; i < 256; i++ ) {
+		S[i] = i;
+	}
+
+	for ( i = j = 0; i < 256; i++ ) {
+		j = ( j + S[i] + key[i % keylen] ) & 0xff;
+		SWAP ( S, i, j );
+	}
+
+	ctx->i = ctx->j = 0;
+	return 0;
+}
+
+/**
+ * Perform ARC4 encryption or decryption
+ *
+ * @v ctxv	ARC4 encryption context
+ * @v srcv	Data to encrypt or decrypt
+ * @v dstv	Location to store encrypted or decrypted data
+ * @v len	Length of data to operate on
+ *
+ * ARC4 is a stream cipher that works by generating a stream of PRNG
+ * data based on the key, and XOR'ing it with the data to be
+ * encrypted. Since XOR is symmetric, encryption and decryption in
+ * ARC4 are the same operation.
+ *
+ * If you pass a @c NULL source or destination pointer, @a len
+ * keystream bytes will be consumed without encrypting any data.
+ */
+static void arc4_xor ( void *ctxv, const void *srcv, void *dstv,
+		       size_t len )
+{
+	struct arc4_ctx *ctx = ctxv;
+	const u8 *src = srcv;
+	u8 *dst = dstv;
+	u8 *S = ctx->state;
+	int i = ctx->i, j = ctx->j;
+
+	while ( len-- ) {
+		i = ( i + 1 ) & 0xff;
+		j = ( j + S[i] ) & 0xff;
+		SWAP ( S, i, j );
+		if ( srcv && dstv )
+			*dst++ = *src++ ^ S[(S[i] + S[j]) & 0xff];
+	}
+
+	ctx->i = i;
+	ctx->j = j;
+}
+
+static void arc4_setiv ( void *ctx __unused, const void *iv __unused )
+{
+	/* ARC4 does not use a fixed-length IV */
+}
+
+
+/**
+ * Perform ARC4 encryption or decryption, skipping initial keystream bytes
+ *
+ * @v key	ARC4 encryption key
+ * @v keylen	Key length
+ * @v skip	Number of bytes of keystream to skip
+ * @v src	Message to encrypt or decrypt
+ * @v msglen	Length of message
+ * @ret dst	Encrypted or decrypted message
+ */
+void arc4_skip ( const void *key, size_t keylen, size_t skip,
+		 const void *src, void *dst, size_t msglen )
+{
+	struct arc4_ctx ctx;
+	arc4_setkey ( &ctx, key, keylen );
+	arc4_xor ( &ctx, NULL, NULL, skip );
+	arc4_xor ( &ctx, src, dst, msglen );
+}
+
+struct cipher_algorithm arc4_algorithm = {
+	.name = "ARC4",
+	.ctxsize = ARC4_CTX_SIZE,
+	.blocksize = 1,
+	.setkey = arc4_setkey,
+	.setiv = arc4_setiv,
+	.encrypt = arc4_xor,
+	.decrypt = arc4_xor,
+};
diff --git a/gpxe/src/crypto/asn1.c b/gpxe/src/crypto/asn1.c
index 25e7495..154a8a8 100644
--- a/gpxe/src/crypto/asn1.c
+++ b/gpxe/src/crypto/asn1.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stddef.h>
 #include <errno.h>
diff --git a/gpxe/src/crypto/axtls_aes.c b/gpxe/src/crypto/axtls_aes.c
index 51e1924..8bd3758 100644
--- a/gpxe/src/crypto/axtls_aes.c
+++ b/gpxe/src/crypto/axtls_aes.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <errno.h>
 #include <byteswap.h>
@@ -30,17 +32,6 @@
  *
  */
 
-/** Basic AES blocksize */
-#define AES_BLOCKSIZE 16
-
-/** AES context */
-struct aes_context {
-	/** AES context for AXTLS */
-	AES_CTX axtls_ctx;
-	/** Cipher is being used for decrypting */
-	int decrypting;
-};
-
 /**
  * Set key
  *
@@ -152,7 +143,7 @@ static void aes_decrypt ( void *ctx, const void *src, void *dst,
 }
 
 /** Basic AES algorithm */
-static struct cipher_algorithm aes_algorithm = {
+struct cipher_algorithm aes_algorithm = {
 	.name = "aes",
 	.ctxsize = sizeof ( struct aes_context ),
 	.blocksize = AES_BLOCKSIZE,
diff --git a/gpxe/src/crypto/cbc.c b/gpxe/src/crypto/cbc.c
index c7116ea..1710203 100644
--- a/gpxe/src/crypto/cbc.c
+++ b/gpxe/src/crypto/cbc.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <assert.h>
 #include <gpxe/crypto.h>
diff --git a/gpxe/src/crypto/chap.c b/gpxe/src/crypto/chap.c
index d0784d2..8aa224c 100644
--- a/gpxe/src/crypto/chap.c
+++ b/gpxe/src/crypto/chap.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/gpxe/src/crypto/crandom.c b/gpxe/src/crypto/crandom.c
new file mode 100644
index 0000000..9828482
--- /dev/null
+++ b/gpxe/src/crypto/crandom.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** @file
+ *
+ * Cryptographically strong random number generator
+ *
+ * Currently the cryptographic part is not implemented, and this just
+ * uses random().
+ */
+
+#include <gpxe/crypto.h>
+#include <stdlib.h>
+
+/**
+ * Get cryptographically strong random bytes
+ *
+ * @v buf	Buffer in which to store random bytes
+ * @v len	Number of random bytes to generate
+ *
+ * @b WARNING: This function is currently underimplemented, and does
+ * not give numbers any stronger than random()!
+ */
+void get_random_bytes ( void *buf, size_t len )
+{
+	u8 *bufp = buf;
+
+	/*
+	 * Somewhat arbitrarily, choose the 0x00FF0000-masked byte
+	 * returned by random() as having good entropy. PRNGs often
+	 * don't provide good entropy in lower bits, and the top byte
+	 * might show a pattern because of sign issues.
+	 */
+
+	while ( len-- ) {
+		*bufp++ = ( random() >> 16 ) & 0xFF;
+	}
+}
diff --git a/gpxe/src/crypto/crc32.c b/gpxe/src/crypto/crc32.c
new file mode 100644
index 0000000..0cc314e
--- /dev/null
+++ b/gpxe/src/crypto/crc32.c
@@ -0,0 +1,54 @@
+/*
+ * Little-endian CRC32 implementation.
+ *
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/crc32.h>
+
+#define CRCPOLY		0xedb88320
+
+/**
+ * Calculate 32-bit little-endian CRC checksum
+ *
+ * @v seed	Initial value
+ * @v data	Data to checksum
+ * @v len	Length of data
+ *
+ * Usually @a seed is initially zero or all one bits, depending on the
+ * protocol. To continue a CRC checksum over multiple calls, pass the
+ * return value from one call as the @a seed parameter to the next.
+ */
+u32 crc32_le ( u32 seed, const void *data, size_t len )
+{
+	u32 crc = seed;
+	const u8 *src = data;
+	u32 mult;
+	int i;
+
+	while ( len-- ) {
+		crc ^= *src++;
+		for ( i = 0; i < 8; i++ ) {
+			mult = ( crc & 1 ) ? CRCPOLY : 0;
+			crc = ( crc >> 1 ) ^ mult;
+		}
+	}
+
+	return crc;
+}
diff --git a/gpxe/src/crypto/crypto_null.c b/gpxe/src/crypto/crypto_null.c
index 8cc9217..61efb34 100644
--- a/gpxe/src/crypto/crypto_null.c
+++ b/gpxe/src/crypto/crypto_null.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/crypto/hmac.c b/gpxe/src/crypto/hmac.c
index be0298a..d64730c 100644
--- a/gpxe/src/crypto/hmac.c
+++ b/gpxe/src/crypto/hmac.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/crypto/md5.c b/gpxe/src/crypto/md5.c
index 76fb8a6..8c60639 100644
--- a/gpxe/src/crypto/md5.c
+++ b/gpxe/src/crypto/md5.c
@@ -20,36 +20,36 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <byteswap.h>
 #include <gpxe/crypto.h>
 #include <gpxe/md5.h>
 
-#define __md5step __attribute__ (( regparm ( 3 ) ))
-
 struct md5_step {
-	u32 __md5step ( * f ) ( u32 b, u32 c, u32 d );
+	u32 ( * f ) ( u32 b, u32 c, u32 d );
 	u8 coefficient;
 	u8 constant;
 };
 
-static u32 __md5step f1(u32 b, u32 c, u32 d)
+static u32 f1(u32 b, u32 c, u32 d)
 {
 	return ( d ^ ( b & ( c ^ d ) ) );
 }
 
-static u32 __md5step f2(u32 b, u32 c, u32 d)
+static u32 f2(u32 b, u32 c, u32 d)
 {
 	return ( c ^ ( d & ( b ^ c ) ) );
 }
 
-static u32 __md5step f3(u32 b, u32 c, u32 d)
+static u32 f3(u32 b, u32 c, u32 d)
 {
 	return ( b ^ c ^ d );
 }
 
-static u32 __md5step f4(u32 b, u32 c, u32 d)
+static u32 f4(u32 b, u32 c, u32 d)
 {
 	return ( c ^ ( b | ~d ) );
 }
diff --git a/gpxe/src/crypto/sha1extra.c b/gpxe/src/crypto/sha1extra.c
new file mode 100644
index 0000000..c144a0f
--- /dev/null
+++ b/gpxe/src/crypto/sha1extra.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/crypto.h>
+#include <gpxe/sha1.h>
+#include <gpxe/hmac.h>
+#include <stdint.h>
+#include <byteswap.h>
+
+/**
+ * SHA1 pseudorandom function for creating derived keys
+ *
+ * @v key	Master key with which this call is associated
+ * @v key_len	Length of key
+ * @v label	NUL-terminated ASCII string describing purpose of PRF data
+ * @v data	Further data that should be included in the PRF
+ * @v data_len	Length of further PRF data
+ * @v prf_len	Bytes of PRF to generate
+ * @ret prf	Pseudorandom function bytes
+ *
+ * This is the PRF variant used by 802.11, defined in IEEE 802.11-2007
+ * 8.5.5.1. EAP-FAST uses a different SHA1-based PRF, and TLS uses an
+ * MD5-based PRF.
+ */
+void prf_sha1 ( const void *key, size_t key_len, const char *label,
+		const void *data, size_t data_len, void *prf, size_t prf_len )
+{
+	u32 blk;
+	u8 keym[key_len];	/* modifiable copy of key */
+	u8 in[strlen ( label ) + 1 + data_len + 1]; /* message to HMAC */
+	u8 *in_blknr;		/* pointer to last byte of in, block number */
+	u8 out[SHA1_SIZE];	/* HMAC-SHA1 result */
+	u8 sha1_ctx[SHA1_CTX_SIZE]; /* SHA1 context */
+	const size_t label_len = strlen ( label );
+
+	/* The HMAC-SHA-1 is calculated using the given key on the
+	   message text `label', followed by a NUL, followed by one
+	   byte indicating the block number (0 for first). */
+
+	memcpy ( keym, key, key_len );
+
+	memcpy ( in, label, strlen ( label ) + 1 );
+	memcpy ( in + label_len + 1, data, data_len );
+	in_blknr = in + label_len + 1 + data_len;
+
+	for ( blk = 0 ;; blk++ ) {
+		*in_blknr = blk;
+
+		hmac_init ( &sha1_algorithm, sha1_ctx, keym, &key_len );
+		hmac_update ( &sha1_algorithm, sha1_ctx, in, sizeof ( in ) );
+		hmac_final ( &sha1_algorithm, sha1_ctx, keym, &key_len, out );
+
+		if ( prf_len <= SHA1_SIZE ) {
+			memcpy ( prf, out, prf_len );
+			break;
+		}
+
+		memcpy ( prf, out, SHA1_SIZE );
+		prf_len -= SHA1_SIZE;
+		prf += SHA1_SIZE;
+	}
+}
+
+/**
+ * PBKDF2 key derivation function inner block operation
+ *
+ * @v passphrase	Passphrase from which to derive key
+ * @v pass_len		Length of passphrase
+ * @v salt		Salt to include in key
+ * @v salt_len		Length of salt
+ * @v iterations	Number of iterations of SHA1 to perform
+ * @v blocknr		Index of this block, starting at 1
+ * @ret block		SHA1_SIZE bytes of PBKDF2 data
+ *
+ * The operation of this function is described in RFC 2898.
+ */
+static void pbkdf2_sha1_f ( const void *passphrase, size_t pass_len,
+			    const void *salt, size_t salt_len,
+			    int iterations, u32 blocknr, u8 *block )
+{
+	u8 pass[pass_len];	/* modifiable passphrase */
+	u8 in[salt_len + 4];	/* input buffer to first round */
+	u8 last[SHA1_SIZE];	/* output of round N, input of N+1 */
+	u8 sha1_ctx[SHA1_CTX_SIZE];
+	u8 *next_in = in;	/* changed to `last' after first round */
+	int next_size = sizeof ( in );
+	int i, j;
+
+	blocknr = htonl ( blocknr );
+
+	memcpy ( pass, passphrase, pass_len );
+	memcpy ( in, salt, salt_len );
+	memcpy ( in + salt_len, &blocknr, 4 );
+	memset ( block, 0, SHA1_SIZE );
+
+	for ( i = 0; i < iterations; i++ ) {
+		hmac_init ( &sha1_algorithm, sha1_ctx, pass, &pass_len );
+		hmac_update ( &sha1_algorithm, sha1_ctx, next_in, next_size );
+		hmac_final ( &sha1_algorithm, sha1_ctx, pass, &pass_len, last );
+
+		for ( j = 0; j < SHA1_SIZE; j++ ) {
+			block[j] ^= last[j];
+		}
+
+		next_in = last;
+		next_size = SHA1_SIZE;
+	}
+}
+
+/**
+ * PBKDF2 key derivation function using SHA1
+ *
+ * @v passphrase	Passphrase from which to derive key
+ * @v pass_len		Length of passphrase
+ * @v salt		Salt to include in key
+ * @v salt_len		Length of salt
+ * @v iterations	Number of iterations of SHA1 to perform
+ * @v key_len		Length of key to generate
+ * @ret key		Generated key bytes
+ *
+ * This is used most notably in 802.11 WPA passphrase hashing, in
+ * which case the salt is the SSID, 4096 iterations are used, and a
+ * 32-byte key is generated that serves as the Pairwise Master Key for
+ * EAPOL authentication.
+ *
+ * The operation of this function is further described in RFC 2898.
+ */
+void pbkdf2_sha1 ( const void *passphrase, size_t pass_len,
+		   const void *salt, size_t salt_len,
+		   int iterations, void *key, size_t key_len )
+{
+	u32 blocks = ( key_len + SHA1_SIZE - 1 ) / SHA1_SIZE;
+	u32 blk;
+	u8 buf[SHA1_SIZE];
+
+	for ( blk = 1; blk <= blocks; blk++ ) {
+		pbkdf2_sha1_f ( passphrase, pass_len, salt, salt_len,
+				iterations, blk, buf );
+		if ( key_len <= SHA1_SIZE ) {
+			memcpy ( key, buf, key_len );
+			break;
+		}
+
+		memcpy ( key, buf, SHA1_SIZE );
+		key_len -= SHA1_SIZE;
+		key += SHA1_SIZE;
+	}
+}
diff --git a/gpxe/src/crypto/x509.c b/gpxe/src/crypto/x509.c
index 35adfa3..31ed412 100644
--- a/gpxe/src/crypto/x509.c
+++ b/gpxe/src/crypto/x509.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
diff --git a/gpxe/src/doc/pxe_extensions b/gpxe/src/doc/pxe_extensions
index 92269cf..8ff14a9 100644
--- a/gpxe/src/doc/pxe_extensions
+++ b/gpxe/src/doc/pxe_extensions
@@ -277,3 +277,36 @@ Provider:	Set to 0x45585067 ("gPXE").  Another implementation of this
 APIMask:	Bitmask of supported API functions (one bit for each function
 		in the range 00e0h to 00ffh).
 Flags:		Set to zero, reserved for future use.
+
+
+
+
+FILE EXIT HOOK
+
+Op-Code:	PXENV_FILE_EXIT_HOOK (00e7h)
+
+Input:		Far pointer to a t_PXENV_FILE_EXIT_HOOK parameter
+		structure that has been initialized by the caller.
+
+Output:		PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be
+		returned in AX.  The Status field in the parameter
+		structure must be set to one of the values represented
+		by the PXENV_STATUS_xxx constants.
+
+Description:	Modify the exit path to jump to the specified code.
+		Only valid for pxeprefix-based builds.
+
+typedef struct s_PXENV_FILE_EXIT_HOOK {
+        PXENV_STATUS_t Status;
+        SEGOFF16_t Hook;
+} t_PXENV_FILE_EXIT_HOOK;
+
+
+Set before calling API service:
+
+Hook:		The SEG16:OFF16 of the code to jump to.
+
+
+Returned from API service:
+
+Status:		See PXENV_STATUS_xxx constants.
diff --git a/gpxe/src/doxygen.cfg b/gpxe/src/doxygen.cfg
index 5c42909..0ee87ad 100644
--- a/gpxe/src/doxygen.cfg
+++ b/gpxe/src/doxygen.cfg
@@ -1,4 +1,4 @@
-# Doxyfile 1.4.4
+# Doxyfile 1.5.7.1
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project
@@ -14,10 +14,18 @@
 # Project related configuration options
 #---------------------------------------------------------------------------
 
+# This tag specifies the encoding used for all characters in the config file 
+# that follow. The default is UTF-8 which is also the encoding used for all 
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
+# iconv built into libc) for the transcoding. See 
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
 # by quotes) that should identify the project.
 
-PROJECT_NAME           = Etherboot
+PROJECT_NAME           = gPXE
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. 
 # This could be handy for archiving the generated documentation or 
@@ -45,24 +53,15 @@ CREATE_SUBDIRS         = NO
 # documentation generated by doxygen is written. Doxygen will use this 
 # information to generate all constant output in the proper language. 
 # The default language is English, other supported languages are: 
-# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
-# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
-# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
-# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
-# Swedish, and Ukrainian.
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, 
+# Spanish, Swedish, and Ukrainian.
 
 OUTPUT_LANGUAGE        = English
 
-# This tag can be used to specify the encoding used in the generated output. 
-# The encoding is not always determined by the language that is chosen, 
-# but also whether or not the output is meant for Windows or non-Windows users. 
-# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
-# forces the Windows encoding (this is the default for the Windows binary), 
-# whereas setting the tag to NO uses a Unix-style encoding (the default for 
-# all platforms other than Windows).
-
-USE_WINDOWS_ENCODING   = NO
-
 # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
 # include brief member descriptions after the members that are listed in 
 # the file and class documentation (similar to JavaDoc). 
@@ -93,7 +92,7 @@ ABBREVIATE_BRIEF       =
 # Doxygen will generate a detailed section even if there is only a brief 
 # description.
 
-ALWAYS_DETAILED_SEC    = NO
+ALWAYS_DETAILED_SEC    = YES
 
 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
 # inherited members of a class in the documentation of that class as if those 
@@ -106,7 +105,7 @@ INLINE_INHERITED_MEMB  = NO
 # path before files name in the file list and in the header files. If set 
 # to NO the shortest path that makes the file name unique will be used.
 
-FULL_PATH_NAMES        = NO
+FULL_PATH_NAMES        = YES
 
 # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
 # can be used to strip a user-defined part of the path. Stripping is 
@@ -135,11 +134,19 @@ SHORT_NAMES            = NO
 # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
 # will interpret the first line (until the first dot) of a JavaDoc-style 
 # comment as the brief description. If set to NO, the JavaDoc 
-# comments will behave just like the Qt-style comments (thus requiring an 
-# explicit @brief command for a brief description.
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
 
 JAVADOC_AUTOBRIEF      = YES
 
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
 # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
 # treat a multi-line C++ special comment block (i.e. a block of //! or /// 
 # comments) as a brief description. This used to be the default behaviour. 
@@ -148,26 +155,12 @@ JAVADOC_AUTOBRIEF      = YES
 
 MULTILINE_CPP_IS_BRIEF = NO
 
-# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
-# will output the detailed description near the top, like JavaDoc.
-# If set to NO, the detailed description appears after the member 
-# documentation.
-
-DETAILS_AT_TOP         = YES
-
 # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
 # member inherits the documentation from any documented member that it 
 # re-implements.
 
 INHERIT_DOCS           = YES
 
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
-# tag is set to YES, then doxygen will reuse the documentation of the first 
-# member in the group (if any) for the other members of the group. By default 
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC   = NO
-
 # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
 # a new page for each member. If set to NO, the documentation of a member will 
 # be part of the file/class/namespace that contains it.
@@ -197,13 +190,61 @@ ALIASES                = v=@param \
 
 OPTIMIZE_OUTPUT_FOR_C  = YES
 
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
-# only. Doxygen will then generate output that is more tailored for Java. 
-# For instance, namespaces will be presented as packages, qualified scopes 
-# will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Java. For instance, namespaces will be presented as packages, qualified 
+# scopes will look different, etc.
 
 OPTIMIZE_OUTPUT_JAVA   = NO
 
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
+# sources. Doxygen will then generate output that is tailored for 
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
+# to include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter 
+# and setter methods for a property. Setting this option to YES (the default) 
+# will make doxygen to replace the get and set methods by a property in the 
+# documentation. This will only work if the methods are indeed getting or 
+# setting a simple type. If this is not the case, or you want to show the 
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = YES
+
 # Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
 # the same type (for instance a group of public functions) to be put as a 
 # subgroup of that type (e.g. under the Public Functions section). Set it to 
@@ -212,6 +253,32 @@ OPTIMIZE_OUTPUT_JAVA   = NO
 
 SUBGROUPING            = YES
 
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
+# is documented as struct, union, or enum with the name of the typedef. So 
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
+# with name TypeT. When disabled the typedef will appear as a member of a file, 
+# namespace, or class. And the struct will be named TypeS. This can typically 
+# be useful for C code in case the coding convention dictates that all compound 
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is 
+# probably good enough. For larger projects a too small cache size can cause 
+# doxygen to be busy swapping symbols to and from disk most of the time 
+# causing a significant performance penality. 
+# If the system has enough physical memory increasing the cache will improve the 
+# performance by keeping more symbols in memory. Note that the value works on 
+# a logarithmic scale so increasing the size by one will rougly double the 
+# memory usage. The cache size is given by this formula: 
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
@@ -221,7 +288,7 @@ SUBGROUPING            = YES
 # Private class members and static file members will be hidden unless 
 # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
 
-EXTRACT_ALL            = NO
+EXTRACT_ALL            = YES
 
 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
 # will be included in the documentation.
@@ -246,6 +313,14 @@ EXTRACT_LOCAL_CLASSES  = YES
 
 EXTRACT_LOCAL_METHODS  = NO
 
+# If this flag is set to YES, the members of anonymous namespaces will be 
+# extracted and appear in the documentation as a namespace called 
+# 'anonymous_namespace{file}', where file will be replaced with the base 
+# name of the file that contains the anonymous namespace. By default 
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
 # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
 # undocumented members of documented classes, files or namespaces. 
 # If set to NO (the default) these members will be included in the 
@@ -300,7 +375,7 @@ HIDE_SCOPE_NAMES       = NO
 # will put a list of the files that are included by a file in the documentation 
 # of that file.
 
-SHOW_INCLUDE_FILES     = NO
+SHOW_INCLUDE_FILES     = YES
 
 # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
 # is inserted in the documentation for inline members.
@@ -321,6 +396,12 @@ SORT_MEMBER_DOCS       = NO
 
 SORT_BRIEF_DOCS        = NO
 
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
+# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
 # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
 # sorted by fully-qualified names, including namespaces. If set to 
 # NO (the default), the class list will be sorted only by class name, 
@@ -378,20 +459,41 @@ SHOW_USED_FILES        = YES
 
 # If the sources in your project are distributed over multiple directories 
 # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
-# in the documentation. The default is YES.
+# in the documentation. The default is NO.
 
 SHOW_DIRECTORIES       = YES
 
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the 
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
 # The FILE_VERSION_FILTER tag can be used to specify a program or script that 
-# doxygen should invoke to get the current version for each file (typically from the 
-# version control system). Doxygen will invoke the program by executing (via 
+# doxygen should invoke to get the current version for each file (typically from 
+# the version control system). Doxygen will invoke the program by executing (via 
 # popen()) the command <command> <input-file>, where <command> is the value of 
 # the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
-# provided by doxygen. Whatever the progam writes to standard output 
+# provided by doxygen. Whatever the program writes to standard output 
 # is used as the file version. See the manual for examples.
 
 FILE_VERSION_FILTER    = 
 
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 
+# doxygen. The layout file controls the global structure of the generated output files 
+# in an output format independent way. The create the layout file that represents 
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a 
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name 
+# of the layout file.
+
+LAYOUT_FILE            = 
+
 #---------------------------------------------------------------------------
 # configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
@@ -426,7 +528,7 @@ WARN_IF_DOC_ERROR      = YES
 # wrong or incomplete parameter documentation, but not about the absence of 
 # documentation.
 
-WARN_NO_PARAMDOC       = NO
+WARN_NO_PARAMDOC       = YES
 
 # The WARN_FORMAT tag determines the format of the warning messages that 
 # doxygen can produce. The string should contain the $file, $line, and $text 
@@ -453,28 +555,34 @@ WARN_LOGFILE           =
 # with spaces.
 
 INPUT                  = @SRCDIRS@ \
-                         include \
-                         include/gpxe \
-                         arch/@ARCH@/include \
+                         @INCDIRS@ \
+                         config \
                          doc
 
+# This tag can be used to specify the character encoding of the source files 
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
+# also the default input encoding. Doxygen uses libiconv (or the iconv built 
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
 # If the value of the INPUT tag contains directories, you can use the 
 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
 # and *.h) to filter out the source-files in the directories. If left 
 # blank the following patterns are tested: 
 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
 
 FILE_PATTERNS          = *.c \
                          *.h \
-                         *.S \
                          *.dox
 
 # The RECURSIVE tag can be used to turn specify whether or not subdirectories 
 # should be searched for input files as well. Possible values are YES and NO. 
 # If left blank NO is used.
 
-RECURSIVE              = NO
+RECURSIVE              = YES
 
 # The EXCLUDE tag can be used to specify files and/or directories that should 
 # excluded from the INPUT source files. This way you can easily exclude a 
@@ -496,6 +604,14 @@ EXCLUDE_SYMLINKS       = NO
 
 EXCLUDE_PATTERNS       = 
 
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the 
+# output. The symbol name can be a fully qualified name, a word, or if the 
+# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
 # The EXAMPLE_PATH tag can be used to specify one or more files or 
 # directories that contain example code fragments that are included (see 
 # the \include command).
@@ -561,7 +677,7 @@ SOURCE_BROWSER         = YES
 # Setting the INLINE_SOURCES tag to YES will include the body 
 # of functions and classes directly in the documentation.
 
-INLINE_SOURCES         = NO
+INLINE_SOURCES         = YES
 
 # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
 # doxygen to hide any special comment blocks from generated source code 
@@ -569,17 +685,24 @@ INLINE_SOURCES         = NO
 
 STRIP_CODE_COMMENTS    = NO
 
-# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# If the REFERENCED_BY_RELATION tag is set to YES 
 # then for each documented function all documented 
 # functions referencing it will be listed.
 
 REFERENCED_BY_RELATION = YES
 
-# If the REFERENCES_RELATION tag is set to YES (the default) 
+# If the REFERENCES_RELATION tag is set to YES 
 # then for each documented function all documented entities 
 # called/used by that function will be listed.
 
-REFERENCES_RELATION    = NO
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
 
 # If the USE_HTAGS tag is set to YES then the references to source code 
 # will point to the HTML generated by the htags(1) tool instead of doxygen 
@@ -666,9 +789,43 @@ HTML_STYLESHEET        =
 
 HTML_ALIGN_MEMBERS     = YES
 
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = YES
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files 
+# will be generated that can be used as input for Apple's Xcode 3 
+# integrated development environment, introduced with OSX 10.5 (Leopard). 
+# To create a documentation set, doxygen will generate a Makefile in the 
+# HTML output directory. Running make will produce the docset in that 
+# directory and running "make install" will install the docset in 
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
+# it at startup. 
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
+# feed. A documentation feed provides an umbrella under which multiple 
+# documentation sets from a single provider (such as a company or product suite) 
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
+# should uniquely identify the documentation set bundle. This should be a 
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
 # If the GENERATE_HTMLHELP tag is set to YES, additional index files 
 # will be generated that can be used as input for tools like the 
-# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
 # of the generated HTML documentation.
 
 GENERATE_HTMLHELP      = NO
@@ -693,6 +850,12 @@ HHC_LOCATION           =
 
 GENERATE_CHI           = NO
 
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     = 
+
 # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
 # controls whether a binary table of contents is generated (YES) or a 
 # normal table of contents (NO) in the .chm file.
@@ -702,7 +865,39 @@ BINARY_TOC             = NO
 # The TOC_EXPAND flag can be set to YES to add extra items for group members 
 # to the contents of the HTML help documentation and to the tree view.
 
-TOC_EXPAND             = NO
+TOC_EXPAND             = YES
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 
+# are set, an additional index file will be generated that can be used as input for 
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
+# be used to specify the file name of the resulting .qch file. 
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               = 
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# <a href="http://doc.trolltech.com/qthelpproject.html#namespace">Qt Help Project / Namespace</a>.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# <a href="http://doc.trolltech.com/qthelpproject.html#virtual-folders">Qt Help Project / Virtual Folders</a>.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
+# be used to specify the location of Qt's qhelpgenerator. 
+# If non-empty doxygen will try to run qhelpgenerator on the generated 
+# .qhp file .
+
+QHG_LOCATION           = 
 
 # The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
 # top of each HTML page. The value NO (the default) enables the index and 
@@ -715,14 +910,22 @@ DISABLE_INDEX          = NO
 
 ENUM_VALUES_PER_LINE   = 4
 
-# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
-# generated containing a tree-like index structure (just like the one that 
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that 
 # is generated for HTML Help). For this to work a browser that supports 
 # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
 # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
-# probably better off using the HTML help feature.
+# probably better off using the HTML help feature. Other possible values 
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
 
-GENERATE_TREEVIEW      = NO
+GENERATE_TREEVIEW      = NONE
 
 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
 # used to set the initial width (in pixels) of the frame in which the tree 
@@ -730,6 +933,14 @@ GENERATE_TREEVIEW      = NO
 
 TREEVIEW_WIDTH         = 250
 
+# Use this tag to change the font size of Latex formulas included 
+# as images in the HTML documentation. The default is 10. Note that 
+# when you change the font size after a successful doxygen run you need 
+# to manually remove any form_*.png images from the HTML output directory 
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
@@ -737,7 +948,7 @@ TREEVIEW_WIDTH         = 250
 # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
 # generate Latex output.
 
-GENERATE_LATEX         = YES
+GENERATE_LATEX         = NO
 
 # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
 # If a relative path is entered the value of OUTPUT_DIRECTORY will be 
@@ -855,7 +1066,7 @@ RTF_EXTENSIONS_FILE    =
 # If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
 # generate man pages
 
-GENERATE_MAN           = YES
+GENERATE_MAN           = NO
 
 # The MAN_OUTPUT tag is used to specify where the man pages will be put. 
 # If a relative path is entered the value of OUTPUT_DIRECTORY will be 
@@ -975,7 +1186,7 @@ MACRO_EXPANSION        = YES
 
 # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
 # then the macro expansion is limited to the macros specified with the 
-# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+# PREDEFINED and EXPAND_AS_DEFINED tags.
 
 EXPAND_ONLY_PREDEF     = YES
 
@@ -988,8 +1199,7 @@ SEARCH_INCLUDES        = YES
 # contain include files that are not input files but should be processed by 
 # the preprocessor.
 
-INCLUDE_PATH           = include \
-                         arch/@ARCH@/include
+INCLUDE_PATH           = @INCDIRS@
 
 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
 # patterns (like *.h and *.hpp) to filter out the header-files in the 
@@ -1084,13 +1294,22 @@ PERL_PATH              =
 # fallback. It is recommended to install and use dot, since it yields more 
 # powerful graphs.
 
-CLASS_DIAGRAMS         = YES
+CLASS_DIAGRAMS         = NO
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see 
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
+# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# default search path.
+
+MSCGEN_PATH            = 
 
 # If set to YES, the inheritance and collaboration graphs will hide 
 # inheritance and usage relations if the target is undocumented 
 # or is not a class.
 
-HIDE_UNDOC_RELATIONS   = YES
+HIDE_UNDOC_RELATIONS   = NO
 
 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
 # available from the path. This tool is part of Graphviz, a graph visualization 
@@ -1099,6 +1318,29 @@ HIDE_UNDOC_RELATIONS   = YES
 
 HAVE_DOT               = NO
 
+# By default doxygen will write a font called FreeSans.ttf to the output 
+# directory and reference it in all dot files that doxygen generates. This 
+# font does not include all possible unicode characters however, so when you need 
+# these (or just want a differently looking font) you can specify the font name 
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
+# which can be done by putting it in a standard location or by setting the 
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the 
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
+# different font using DOT_FONTNAME you can set the path where dot 
+# can find it using this tag.
+
+DOT_FONTPATH           = 
+
 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
 # will generate a graph for each documented class showing the direct and 
 # indirect inheritance relations. Setting this tag to YES will force the 
@@ -1143,14 +1385,22 @@ INCLUDE_GRAPH          = YES
 
 INCLUDED_BY_GRAPH      = YES
 
-# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
-# generate a call dependency graph for every global function or class method. 
-# Note that enabling this option will significantly increase the time of a run. 
-# So in most cases it will be better to enable call graphs for selected 
-# functions only using the \callgraph command.
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
+# doxygen will generate a call dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable call graphs 
+# for selected functions only using the \callgraph command.
 
 CALL_GRAPH             = NO
 
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
+# doxygen will generate a caller dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable caller 
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
 # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
 # will graphical hierarchy of all classes instead of a textual one.
 
@@ -1180,39 +1430,31 @@ DOT_PATH               =
 
 DOTFILE_DIRS           = 
 
-# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
-# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
-# this value, doxygen will try to truncate the graph, so that it fits within 
-# the specified constraint. Beware that most browsers cannot cope with very 
-# large images.
-
-MAX_DOT_GRAPH_WIDTH    = 1024
-
-# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
-# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
-# this value, doxygen will try to truncate the graph, so that it fits within 
-# the specified constraint. Beware that most browsers cannot cope with very 
-# large images.
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the 
+# number of direct children of the root node in a graph is already larger than 
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
 
-MAX_DOT_GRAPH_HEIGHT   = 1024
+DOT_GRAPH_MAX_NODES    = 50
 
 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
 # graphs generated by dot. A depth value of 3 means that only nodes reachable 
 # from the root by following a path via at most 3 edges will be shown. Nodes 
 # that lay further from the root node will be omitted. Note that setting this 
 # option to 1 or 2 may greatly reduce the computation time needed for large 
-# code bases. Also note that a graph may be further truncated if the graph's 
-# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH 
-# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), 
-# the graph is not depth-constrained.
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
 
 MAX_DOT_GRAPH_DEPTH    = 0
 
 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
-# background. This is disabled by default, which results in a white background. 
-# Warning: Depending on the platform used, enabling this option may lead to 
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
-# read).
+# background. This is disabled by default, because dot on Windows does not 
+# seem to support this out of the box. Warning: Depending on the platform used, 
+# enabling this option may lead to badly anti-aliased labels on the edges of 
+# a graph (i.e. they become hard to read).
 
 DOT_TRANSPARENT        = NO
 
diff --git a/gpxe/src/drivers/bitbash/bitbash.c b/gpxe/src/drivers/bitbash/bitbash.c
index c6f9352..3e558d5 100644
--- a/gpxe/src/drivers/bitbash/bitbash.c
+++ b/gpxe/src/drivers/bitbash/bitbash.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/bitbash.h>
 
 /** @file
diff --git a/gpxe/src/drivers/bitbash/i2c_bit.c b/gpxe/src/drivers/bitbash/i2c_bit.c
index b85057a..1319727 100644
--- a/gpxe/src/drivers/bitbash/i2c_bit.c
+++ b/gpxe/src/drivers/bitbash/i2c_bit.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdint.h>
 #include <errno.h>
diff --git a/gpxe/src/drivers/bitbash/spi_bit.c b/gpxe/src/drivers/bitbash/spi_bit.c
index ef87b5a..8e70393 100644
--- a/gpxe/src/drivers/bitbash/spi_bit.c
+++ b/gpxe/src/drivers/bitbash/spi_bit.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdint.h>
 #include <string.h>
@@ -58,8 +60,8 @@ static void spi_bit_set_slave_select ( struct spi_bit_basher *spibit,
 	struct bit_basher *basher = &spibit->basher;
 
 	state ^= ( spibit->bus.mode & SPI_MODE_SSPOL );
-	DBG ( "Setting slave %d select %s\n", slave,
-	      ( state ? "high" : "low" ) );
+	DBGC2 ( spibit, "SPIBIT %p setting slave %d select %s\n",
+		spibit, slave, ( state ? "high" : "low" ) );
 
 	spi_bit_delay();
 	write_bit ( basher, SPI_BIT_SS ( slave ), state );
@@ -94,7 +96,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
 	unsigned int bit;
 	unsigned int step;
 
-	DBG ( "Transferring %d bits in mode %x\n", len, bus->mode );
+	DBGC2 ( spibit, "SPIBIT %p transferring %d bits in mode %#x\n",
+		spibit, len, bus->mode );
 
 	for ( step = 0 ; step < ( len * 2 ) ; step++ ) {
 		/* Calculate byte offset and byte mask */
@@ -111,6 +114,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
 			if ( data_out ) {
 				byte = ( data_out + byte_offset );
 				bit = ( *byte & byte_mask );
+				DBGCP ( spibit, "SPIBIT %p wrote bit %d\n",
+					spibit, ( bit ? 1 : 0 ) );
 			} else {
 				bit = 0;
 			}
@@ -121,6 +126,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
 			/* Shift data in */
 			bit = read_bit ( basher, SPI_BIT_MISO );
 			if ( data_in ) {
+				DBGCP ( spibit, "SPIBIT %p read bit %d\n",
+					spibit, ( bit ? 1 : 0 ) );
 				byte = ( data_in + byte_offset );
 				*byte &= ~byte_mask;
 				*byte |= ( bit & byte_mask );
@@ -129,7 +136,7 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
 
 		/* Toggle clock line */
 		spi_bit_delay();
-		sclk = ~sclk;
+		sclk ^= 1;
 		write_bit ( basher, SPI_BIT_SCLK, sclk );
 	}
 }
@@ -151,7 +158,9 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device,
 			const void *data_out, void *data_in, size_t len ) {
 	struct spi_bit_basher *spibit
 		= container_of ( bus, struct spi_bit_basher, bus );
-	uint32_t tmp;
+	uint32_t tmp_command;
+	uint32_t tmp_address;
+	uint32_t tmp_address_detect;
 
 	/* Set clock line to idle state */
 	write_bit ( &spibit->basher, SPI_BIT_SCLK, 
@@ -161,17 +170,37 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device,
 	spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE );
 
 	/* Transmit command */
-	assert ( device->command_len <= ( 8 * sizeof ( tmp ) ) );
-	tmp = cpu_to_le32 ( command );
-	spi_bit_transfer ( spibit, &tmp, NULL, device->command_len,
+	assert ( device->command_len <= ( 8 * sizeof ( tmp_command ) ) );
+	tmp_command = cpu_to_le32 ( command );
+	spi_bit_transfer ( spibit, &tmp_command, NULL, device->command_len,
 			   SPI_BIT_BIG_ENDIAN );
 
 	/* Transmit address, if present */
 	if ( address >= 0 ) {
-		assert ( device->address_len <= ( 8 * sizeof ( tmp ) ) );
-		tmp = cpu_to_le32 ( address );
-		spi_bit_transfer ( spibit, &tmp, NULL, device->address_len,
-				   SPI_BIT_BIG_ENDIAN );
+		assert ( device->address_len <= ( 8 * sizeof ( tmp_address )));
+		tmp_address = cpu_to_le32 ( address );
+		if ( device->address_len == SPI_AUTODETECT_ADDRESS_LEN ) {
+			/* Autodetect address length.  This relies on
+			 * the device responding with a dummy zero
+			 * data bit before the first real data bit.
+			 */
+			DBGC ( spibit, "SPIBIT %p autodetecting device "
+			       "address length\n", spibit );
+			assert ( address == 0 );
+			device->address_len = 0;
+			do {
+				spi_bit_transfer ( spibit, &tmp_address,
+						   &tmp_address_detect, 1,
+						   SPI_BIT_BIG_ENDIAN );
+				device->address_len++;
+			} while ( le32_to_cpu ( tmp_address_detect ) & 1 );
+			DBGC ( spibit, "SPIBIT %p autodetected device address "
+			       "length %d\n", spibit, device->address_len );
+		} else {
+			spi_bit_transfer ( spibit, &tmp_address, NULL,
+					   device->address_len,
+					   SPI_BIT_BIG_ENDIAN );
+		}
 	}
 
 	/* Transmit/receive data */
diff --git a/gpxe/src/drivers/block/ata.c b/gpxe/src/drivers/block/ata.c
index c21d2f6..dc851d7 100644
--- a/gpxe/src/drivers/block/ata.c
+++ b/gpxe/src/drivers/block/ata.c
@@ -16,11 +16,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <string.h>
 #include <assert.h>
+#include <errno.h>
 #include <byteswap.h>
 #include <gpxe/blockdev.h>
+#include <gpxe/process.h>
 #include <gpxe/ata.h>
 
 /** @file
@@ -43,13 +47,34 @@ block_to_ata ( struct block_device *blockdev ) {
  */
 static inline __attribute__ (( always_inline )) int
 ata_command ( struct ata_device *ata, struct ata_command *command ) {
+	int rc;
+
 	DBG ( "ATA cmd %02x dev %02x LBA%s %llx count %04x\n",
 	      command->cb.cmd_stat, command->cb.device,
 	      ( command->cb.lba48 ? "48" : "" ),
 	      ( unsigned long long ) command->cb.lba.native,
 	      command->cb.count.native );
 
-	return ata->command ( ata, command );	
+	/* Flag command as in-progress */
+	command->rc = -EINPROGRESS;
+
+	/* Issue ATA command */
+	if ( ( rc = ata->command ( ata, command ) ) != 0 ) {
+		/* Something went wrong with the issuing mechanism */
+		DBG ( "ATA could not issue command: %s\n", strerror ( rc ) );
+		return rc;
+	}
+
+	/* Wait for command to complete */
+	while ( command->rc == -EINPROGRESS )
+		step();
+	if ( ( rc = command->rc ) != 0 ) {
+		/* Something went wrong with the command execution */
+		DBG ( "ATA command failed: %s\n", strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
 }
 
 /**
diff --git a/gpxe/src/drivers/block/ramdisk.c b/gpxe/src/drivers/block/ramdisk.c
index 5091199..4e6f1bc 100644
--- a/gpxe/src/drivers/block/ramdisk.c
+++ b/gpxe/src/drivers/block/ramdisk.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/blockdev.h>
 #include <gpxe/ramdisk.h>
 
diff --git a/gpxe/src/drivers/block/scsi.c b/gpxe/src/drivers/block/scsi.c
index b22bd20..a51b3af 100644
--- a/gpxe/src/drivers/block/scsi.c
+++ b/gpxe/src/drivers/block/scsi.c
@@ -16,11 +16,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
+#include <stdlib.h>
 #include <string.h>
 #include <byteswap.h>
 #include <errno.h>
 #include <gpxe/blockdev.h>
+#include <gpxe/process.h>
 #include <gpxe/scsi.h>
 
 /** @file
@@ -42,6 +46,18 @@ block_to_scsi ( struct block_device *blockdev ) {
 }
 
 /**
+ * Handle SCSI command with no backing device
+ *
+ * @v scsi		SCSI device
+ * @v command		SCSI command
+ * @ret rc		Return status code
+ */
+int scsi_detached_command ( struct scsi_device *scsi __unused,
+			    struct scsi_command *command __unused ) {
+	return -ENODEV;
+}
+
+/**
  * Issue SCSI command
  *
  * @v scsi		SCSI device
@@ -52,24 +68,38 @@ static int scsi_command ( struct scsi_device *scsi,
 			  struct scsi_command *command ) {
 	int rc;
 
+	DBGC2 ( scsi, "SCSI %p " SCSI_CDB_FORMAT "\n",
+		scsi, SCSI_CDB_DATA ( command->cdb ) );
+
 	/* Clear sense response code before issuing command */
 	command->sense_response = 0;
 
+	/* Flag command as in-progress */
+	command->rc = -EINPROGRESS;
+
 	/* Issue SCSI command */
 	if ( ( rc = scsi->command ( scsi, command ) ) != 0 ) {
-		/* Something went wrong with the issuing mechanism,
-		 * (rather than with the command itself)
-		 */
-		DBG ( "SCSI %p " SCSI_CDB_FORMAT " err %s\n",
-		      scsi, SCSI_CDB_DATA ( command->cdb ), strerror ( rc ) );
+		/* Something went wrong with the issuing mechanism */
+		DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " err %s\n",
+		       scsi, SCSI_CDB_DATA ( command->cdb ), strerror ( rc ) );
+		return rc;
+	}
+
+	/* Wait for command to complete */
+	while ( command->rc == -EINPROGRESS )
+		step();
+	if ( ( rc = command->rc ) != 0 ) {
+		/* Something went wrong with the command execution */
+		DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " err %s\n",
+		       scsi, SCSI_CDB_DATA ( command->cdb ), strerror ( rc ) );
 		return rc;
 	}
 
 	/* Check for SCSI errors */
 	if ( command->status != 0 ) {
-		DBG ( "SCSI %p " SCSI_CDB_FORMAT " status %02x sense %02x\n",
-		      scsi, SCSI_CDB_DATA ( command->cdb ),
-		      command->status, command->sense_response );
+		DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " status %02x sense "
+		       "%02x\n", scsi, SCSI_CDB_DATA ( command->cdb ),
+		       command->status, command->sense_response );
 		return -EIO;
 	}
 
@@ -271,12 +301,17 @@ int init_scsidev ( struct scsi_device *scsi ) {
 	for ( i = 0 ; i < SCSI_MAX_DUMMY_READ_CAP ; i++ ) {
 		if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) == 0 )
 			break;
+		DBGC ( scsi, "SCSI %p ignoring start-of-day error (#%d)\n",
+		       scsi, ( i + 1 ) );
 	}
 
 	/* Try READ CAPACITY (10), which is a mandatory command, first. */
 	scsi->blockdev.op = &scsi_operations_10;
-	if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) != 0 )
+	if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) != 0 ) {
+		DBGC ( scsi, "SCSI %p could not READ CAPACITY (10): %s\n",
+		       scsi, strerror ( rc ) );
 		return rc;
+	}
 
 	/* If capacity range was exceeded (i.e. capacity.lba was
 	 * 0xffffffff, meaning that blockdev->blocks is now zero), use
@@ -285,8 +320,46 @@ int init_scsidev ( struct scsi_device *scsi ) {
 	 */
 	if ( scsi->blockdev.blocks == 0 ) {
 		scsi->blockdev.op = &scsi_operations_16;
-		if ( ( rc = scsi_read_capacity_16 ( &scsi->blockdev ) ) != 0 )
+		if ( ( rc = scsi_read_capacity_16 ( &scsi->blockdev ) ) != 0 ){
+			DBGC ( scsi, "SCSI %p could not READ CAPACITY (16): "
+			       "%s\n", scsi, strerror ( rc ) );
 			return rc;
+		}
+	}
+
+	DBGC ( scsi, "SCSI %p using READ/WRITE (%d) commands\n", scsi,
+	       ( ( scsi->blockdev.op == &scsi_operations_10 ) ? 10 : 16 ) );
+	DBGC ( scsi, "SCSI %p capacity is %ld MB (%#llx blocks)\n", scsi,
+	       ( ( unsigned long ) ( scsi->blockdev.blocks >> 11 ) ),
+	       scsi->blockdev.blocks );
+
+	return 0;
+}
+
+/**
+ * Parse SCSI LUN
+ *
+ * @v lun_string	LUN string representation
+ * @v lun		LUN to fill in
+ * @ret rc		Return status code
+ */
+int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ) {
+	char *p;
+	int i;
+
+	memset ( lun, 0, sizeof ( *lun ) );
+	if ( lun_string ) {
+		p = ( char * ) lun_string;
+		for ( i = 0 ; i < 4 ; i++ ) {
+			lun->u16[i] = htons ( strtoul ( p, &p, 16 ) );
+			if ( *p == '\0' )
+				break;
+			if ( *p != '-' )
+				return -EINVAL;
+			p++;
+		}
+		if ( *p )
+			return -EINVAL;
 	}
 
 	return 0;
diff --git a/gpxe/src/drivers/block/srp.c b/gpxe/src/drivers/block/srp.c
new file mode 100644
index 0000000..1d0799a
--- /dev/null
+++ b/gpxe/src/drivers/block/srp.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2009 Fen Systems Ltd <mbrown at fensystems.co.uk>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/scsi.h>
+#include <gpxe/xfer.h>
+#include <gpxe/features.h>
+#include <gpxe/ib_srp.h>
+#include <gpxe/srp.h>
+
+/**
+ * @file
+ *
+ * SCSI RDMA Protocol
+ *
+ */
+
+FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 );
+
+/** Tag to be used for next SRP IU */
+static unsigned int srp_tag = 0;
+
+static void srp_login ( struct srp_device *srp );
+static void srp_cmd ( struct srp_device *srp );
+
+/**
+ * Mark SRP SCSI command as complete
+ *
+ * @v srp		SRP device
+ * @v rc		Status code
+ */
+static void srp_scsi_done ( struct srp_device *srp, int rc ) {
+	if ( srp->command )
+		srp->command->rc = rc;
+	srp->command = NULL;
+}
+
+/**
+ * Handle SRP session failure
+ *
+ * @v srp		SRP device
+ * @v rc 		Reason for failure
+ */
+static void srp_fail ( struct srp_device *srp, int rc ) {
+
+	/* Close underlying socket */
+	xfer_close ( &srp->socket, rc );
+
+	/* Clear session state */
+	srp->state = 0;
+
+	/* If we have reached the retry limit, report the failure */
+	if ( srp->retry_count >= SRP_MAX_RETRIES ) {
+		srp_scsi_done ( srp, rc );
+		return;
+	}
+
+	/* Otherwise, increment the retry count and try to reopen the
+	 * connection
+	 */
+	srp->retry_count++;
+	srp_login ( srp );
+}
+
+/**
+ * Initiate SRP login
+ *
+ * @v srp		SRP device
+ */
+static void srp_login ( struct srp_device *srp ) {
+	struct io_buffer *iobuf;
+	struct srp_login_req *login_req;
+	int rc;
+
+	assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) );
+
+	/* Open underlying socket */
+	if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) {
+		DBGC ( srp, "SRP %p could not open socket: %s\n",
+		       srp, strerror ( rc ) );
+		goto err;
+	}
+	srp->state |= SRP_STATE_SOCKET_OPEN;
+
+	/* Allocate I/O buffer */
+	iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) );
+	if ( ! iobuf ) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	/* Construct login request IU */
+	login_req = iob_put ( iobuf, sizeof ( *login_req ) );
+	memset ( login_req, 0, sizeof ( *login_req ) );
+	login_req->type = SRP_LOGIN_REQ;
+	login_req->tag.dwords[1] = htonl ( ++srp_tag );
+	login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN );
+	login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD;
+	memcpy ( &login_req->port_ids, &srp->port_ids,
+		 sizeof ( login_req->port_ids ) );
+
+	DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n",
+		srp, ntohl ( login_req->tag.dwords[0] ),
+		ntohl ( login_req->tag.dwords[1] ) );
+	DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
+
+	/* Send login request IU */
+	if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
+		DBGC ( srp, "SRP %p could not send login request: %s\n",
+		       srp, strerror ( rc ) );
+		goto err;
+	}
+
+	return;
+
+ err:
+	srp_fail ( srp, rc );
+}
+
+/**
+ * Handle SRP login response
+ *
+ * @v srp		SRP device
+ * @v iobuf		I/O buffer
+ * @ret rc		Return status code
+ */
+static int srp_login_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
+	struct srp_login_rsp *login_rsp = iobuf->data;
+	int rc;
+
+	DBGC2 ( srp, "SRP %p RX login response tag %08x%08x\n",
+		srp, ntohl ( login_rsp->tag.dwords[0] ),
+		ntohl ( login_rsp->tag.dwords[1] ) );
+
+	/* Sanity check */
+	if ( iob_len ( iobuf ) < sizeof ( *login_rsp ) ) {
+		DBGC ( srp, "SRP %p RX login response too short (%zd bytes)\n",
+		       srp, iob_len ( iobuf ) );
+		rc = -EINVAL;
+		goto out;
+	}
+
+	DBGC ( srp, "SRP %p logged in\n", srp );
+
+	/* Mark as logged in */
+	srp->state |= SRP_STATE_LOGGED_IN;
+
+	/* Reset error counter */
+	srp->retry_count = 0;
+
+	/* Issue pending command */
+	srp_cmd ( srp );
+
+	rc = 0;
+ out:
+	free_iob ( iobuf );
+	return rc;
+}
+
+/**
+ * Handle SRP login rejection
+ *
+ * @v srp		SRP device
+ * @v iobuf		I/O buffer
+ * @ret rc		Return status code
+ */
+static int srp_login_rej ( struct srp_device *srp, struct io_buffer *iobuf ) {
+	struct srp_login_rej *login_rej = iobuf->data;
+	int rc;
+
+	DBGC2 ( srp, "SRP %p RX login rejection tag %08x%08x\n",
+		srp, ntohl ( login_rej->tag.dwords[0] ),
+		ntohl ( login_rej->tag.dwords[1] ) );
+
+	/* Sanity check */
+	if ( iob_len ( iobuf ) < sizeof ( *login_rej ) ) {
+		DBGC ( srp, "SRP %p RX login rejection too short (%zd "
+		       "bytes)\n", srp, iob_len ( iobuf ) );
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* Login rejection always indicates an error */
+	DBGC ( srp, "SRP %p login rejected (reason %08x)\n",
+	       srp, ntohl ( login_rej->reason ) );
+	rc = -EPERM;
+
+ out:
+	free_iob ( iobuf );
+	return rc;
+}
+
+/**
+ * Transmit SRP SCSI command
+ *
+ * @v srp		SRP device
+ */
+static void srp_cmd ( struct srp_device *srp ) {
+	struct io_buffer *iobuf;
+	struct srp_cmd *cmd;
+	struct srp_memory_descriptor *data_out;
+	struct srp_memory_descriptor *data_in;
+	int rc;
+
+	assert ( srp->state & SRP_STATE_LOGGED_IN );
+
+	/* Allocate I/O buffer */
+	iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN );
+	if ( ! iobuf ) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	/* Construct base portion */
+	cmd = iob_put ( iobuf, sizeof ( *cmd ) );
+	memset ( cmd, 0, sizeof ( *cmd ) );
+	cmd->type = SRP_CMD;
+	cmd->tag.dwords[1] = htonl ( ++srp_tag );
+	cmd->lun = srp->lun;
+	memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) );
+
+	/* Construct data-out descriptor, if present */
+	if ( srp->command->data_out ) {
+		cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT;
+		data_out = iob_put ( iobuf, sizeof ( *data_out ) );
+		data_out->address =
+		    cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) );
+		data_out->handle = ntohl ( srp->memory_handle );
+		data_out->len = ntohl ( srp->command->data_out_len );
+	}
+
+	/* Construct data-in descriptor, if present */
+	if ( srp->command->data_in ) {
+		cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT;
+		data_in = iob_put ( iobuf, sizeof ( *data_in ) );
+		data_in->address =
+		     cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) );
+		data_in->handle = ntohl ( srp->memory_handle );
+		data_in->len = ntohl ( srp->command->data_in_len );
+	}
+
+	DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp,
+		ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) );
+	DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
+
+	/* Send IU */
+	if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
+		DBGC ( srp, "SRP %p could not send command: %s\n",
+		       srp, strerror ( rc ) );
+		goto err;
+	}
+
+	return;
+
+ err:
+	srp_fail ( srp, rc );
+}
+
+/**
+ * Handle SRP SCSI response
+ *
+ * @v srp		SRP device
+ * @v iobuf		I/O buffer
+ * @ret rc		Returns status code
+ */
+static int srp_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
+	struct srp_rsp *rsp = iobuf->data;
+	int rc;
+
+	DBGC2 ( srp, "SRP %p RX SCSI response tag %08x%08x\n", srp,
+		ntohl ( rsp->tag.dwords[0] ), ntohl ( rsp->tag.dwords[1] ) );
+
+	/* Sanity check */
+	if ( iob_len ( iobuf ) < sizeof ( *rsp ) ) {
+		DBGC ( srp, "SRP %p RX SCSI response too short (%zd bytes)\n",
+		       srp, iob_len ( iobuf ) );
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* Report SCSI errors */
+	if ( rsp->status != 0 ) {
+		DBGC ( srp, "SRP %p response status %02x\n",
+		       srp, rsp->status );
+		if ( srp_rsp_sense_data ( rsp ) ) {
+			DBGC ( srp, "SRP %p sense data:\n", srp );
+			DBGC_HDA ( srp, 0, srp_rsp_sense_data ( rsp ),
+				   srp_rsp_sense_data_len ( rsp ) );
+		}
+	}
+	if ( rsp->valid & ( SRP_RSP_VALID_DOUNDER | SRP_RSP_VALID_DOOVER ) ) {
+		DBGC ( srp, "SRP %p response data-out %srun by %#x bytes\n",
+		       srp, ( ( rsp->valid & SRP_RSP_VALID_DOUNDER )
+			      ? "under" : "over" ),
+		       ntohl ( rsp->data_out_residual_count ) );
+	}
+	if ( rsp->valid & ( SRP_RSP_VALID_DIUNDER | SRP_RSP_VALID_DIOVER ) ) {
+		DBGC ( srp, "SRP %p response data-in %srun by %#x bytes\n",
+		       srp, ( ( rsp->valid & SRP_RSP_VALID_DIUNDER )
+			      ? "under" : "over" ),
+		       ntohl ( rsp->data_in_residual_count ) );
+	}
+	srp->command->status = rsp->status;
+
+	/* Mark SCSI command as complete */
+	srp_scsi_done ( srp, 0 );
+
+	rc = 0;
+ out:
+	free_iob ( iobuf );
+	return rc;
+}
+
+/**
+ * Handle SRP unrecognised response
+ *
+ * @v srp		SRP device
+ * @v iobuf		I/O buffer
+ * @ret rc		Returns status code
+ */
+static int srp_unrecognised ( struct srp_device *srp,
+			      struct io_buffer *iobuf ) {
+	struct srp_common *common = iobuf->data;
+
+	DBGC ( srp, "SRP %p RX unrecognised IU tag %08x%08x type %02x\n",
+	       srp, ntohl ( common->tag.dwords[0] ),
+	       ntohl ( common->tag.dwords[1] ), common->type );
+
+	free_iob ( iobuf );
+	return -ENOTSUP;
+}
+
+/**
+ * Receive data from underlying socket
+ *
+ * @v xfer		Data transfer interface
+ * @v iobuf		Datagram I/O buffer
+ * @v meta		Data transfer metadata
+ * @ret rc		Return status code
+ */
+static int srp_xfer_deliver_iob ( struct xfer_interface *xfer,
+				  struct io_buffer *iobuf,
+				  struct xfer_metadata *meta __unused ) {
+	struct srp_device *srp =
+		container_of ( xfer, struct srp_device, socket );
+	struct srp_common *common = iobuf->data;
+	int ( * type ) ( struct srp_device *srp, struct io_buffer *iobuf );
+	int rc;
+
+	/* Determine IU type */
+	switch ( common->type ) {
+	case SRP_LOGIN_RSP:
+		type = srp_login_rsp;
+		break;
+	case SRP_LOGIN_REJ:
+		type = srp_login_rej;
+		break;
+	case SRP_RSP:
+		type = srp_rsp;
+		break;
+	default:
+		type = srp_unrecognised;
+		break;
+	}
+
+	/* Handle IU */
+	if ( ( rc = type ( srp, iobuf ) ) != 0 )
+		goto err;
+
+	return 0;
+
+ err:
+	srp_fail ( srp, rc );
+	return rc;
+}
+
+/**
+ * Underlying socket closed
+ *
+ * @v xfer		Data transfer interface
+ * @v rc		Reason for close
+ */
+static void srp_xfer_close ( struct xfer_interface *xfer, int rc ) {
+	struct srp_device *srp =
+		container_of ( xfer, struct srp_device, socket );
+
+	DBGC ( srp, "SRP %p socket closed: %s\n", srp, strerror ( rc ) );
+
+	srp_fail ( srp, rc );
+}
+
+/** SRP data transfer interface operations */
+static struct xfer_interface_operations srp_xfer_operations = {
+	.close		= srp_xfer_close,
+	.vredirect	= ignore_xfer_vredirect,
+	.window		= unlimited_xfer_window,
+	.alloc_iob	= default_xfer_alloc_iob,
+	.deliver_iob	= srp_xfer_deliver_iob,
+	.deliver_raw	= xfer_deliver_as_iob,
+};
+
+/**
+ * Issue SCSI command via SRP
+ *
+ * @v scsi		SCSI device
+ * @v command		SCSI command
+ * @ret rc		Return status code
+ */
+static int srp_command ( struct scsi_device *scsi,
+			 struct scsi_command *command ) {
+	struct srp_device *srp =
+		container_of ( scsi->backend, struct srp_device, refcnt );
+
+	/* Store SCSI command */
+	if ( srp->command ) {
+		DBGC ( srp, "SRP %p cannot handle concurrent SCSI commands\n",
+		       srp );
+		return -EBUSY;
+	}
+	srp->command = command;
+
+	/* Log in or issue command as appropriate */
+	if ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ) {
+		srp_login ( srp );
+	} else if ( srp->state & SRP_STATE_LOGGED_IN ) {
+		srp_cmd ( srp );
+	} else {
+		/* Still waiting for login; do nothing */
+	}
+
+	return 0;
+}
+
+/**
+ * Attach SRP device
+ *
+ * @v scsi		SCSI device
+ * @v root_path		Root path
+ */
+int srp_attach ( struct scsi_device *scsi, const char *root_path ) {
+	struct srp_transport_type *transport;
+	struct srp_device *srp;
+	int rc;
+
+	/* Hard-code an IB SRP back-end for now */
+	transport = &ib_srp_transport;
+
+	/* Allocate and initialise structure */
+	srp = zalloc ( sizeof ( *srp ) + transport->priv_len );
+	if ( ! srp ) {
+		rc = -ENOMEM;
+		goto err_alloc;
+	}
+	xfer_init ( &srp->socket, &srp_xfer_operations, &srp->refcnt );
+	srp->transport = transport;
+	DBGC ( srp, "SRP %p using %s\n", srp, root_path );
+
+	/* Parse root path */
+	if ( ( rc = transport->parse_root_path ( srp, root_path ) ) != 0 ) {
+		DBGC ( srp, "SRP %p could not parse root path: %s\n",
+		       srp, strerror ( rc ) );
+		goto err_parse_root_path;
+	}
+
+	/* Attach parent interface, mortalise self, and return */
+	scsi->backend = ref_get ( &srp->refcnt );
+	scsi->command = srp_command;
+	ref_put ( &srp->refcnt );
+	return 0;
+
+ err_parse_root_path:
+	ref_put ( &srp->refcnt );
+ err_alloc:
+	return rc;
+}
+
+/**
+ * Detach SRP device
+ *
+ * @v scsi		SCSI device
+ */
+void srp_detach ( struct scsi_device *scsi ) {
+	struct srp_device *srp =
+		container_of ( scsi->backend, struct srp_device, refcnt );
+
+	/* Close socket */
+	xfer_nullify ( &srp->socket );
+	xfer_close ( &srp->socket, 0 );
+	scsi->command = scsi_detached_command;
+	ref_put ( scsi->backend );
+	scsi->backend = NULL;
+}
diff --git a/gpxe/src/drivers/bus/eisa.c b/gpxe/src/drivers/bus/eisa.c
index d9e4235..b533364 100644
--- a/gpxe/src/drivers/bus/eisa.c
+++ b/gpxe/src/drivers/bus/eisa.c
@@ -7,10 +7,7 @@
 #include <unistd.h>
 #include <gpxe/eisa.h>
 
-static struct eisa_driver eisa_drivers[0]
-	__table_start ( struct eisa_driver, eisa_drivers );
-static struct eisa_driver eisa_drivers_end[0]
-	__table_end ( struct eisa_driver, eisa_drivers );
+FILE_LICENCE ( GPL2_OR_LATER );
 
 static void eisabus_remove ( struct root_device *rootdev );
 
@@ -57,7 +54,7 @@ static int eisa_probe ( struct eisa_device *eisa ) {
 	      eisa->slot, eisa->vendor_id, eisa->prod_id,
 	      isa_id_string ( eisa->vendor_id, eisa->prod_id ), eisa->ioaddr );
 
-	for ( driver = eisa_drivers; driver < eisa_drivers_end; driver++ ) {
+	for_each_table_entry ( driver, EISA_DRIVERS ) {
 		for ( i = 0 ; i < driver->id_count ; i++ ) {
 			id = &driver->ids[i];
 			if ( id->vendor_id != eisa->vendor_id )
diff --git a/gpxe/src/drivers/bus/isa.c b/gpxe/src/drivers/bus/isa.c
index fa5def5..f458826 100644
--- a/gpxe/src/drivers/bus/isa.c
+++ b/gpxe/src/drivers/bus/isa.c
@@ -6,6 +6,8 @@
 #include <gpxe/io.h>
 #include <gpxe/isa.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /*
  * isa.c implements a "classical" port-scanning method of ISA device
  * detection.  The driver must provide a list of probe addresses
@@ -48,11 +50,6 @@ static isa_probe_addr_t isa_extra_probe_addrs[] = {
 	  isa_extra_probe_addrs[ (ioidx) + ISA_EXTRA_PROBE_ADDR_COUNT ] : \
 	  (driver)->probe_addrs[(ioidx)] )
 
-static struct isa_driver isa_drivers[0]
-	__table_start ( struct isa_driver, isa_drivers );
-static struct isa_driver isa_drivers_end[0]
-	__table_end ( struct isa_driver, isa_drivers );
-
 static void isabus_remove ( struct root_device *rootdev );
 
 /**
@@ -100,7 +97,7 @@ static int isabus_probe ( struct root_device *rootdev ) {
 	int ioidx;
 	int rc;
 
-	for ( driver = isa_drivers ; driver < isa_drivers_end ; driver++ ) {
+	for_each_table_entry ( driver, ISA_DRIVERS ) {
 		for ( ioidx = ISA_IOIDX_MIN ( driver ) ;
 		      ioidx <= ISA_IOIDX_MAX ( driver ) ; ioidx++ ) {
 			/* Allocate struct isa_device */
diff --git a/gpxe/src/drivers/bus/isapnp.c b/gpxe/src/drivers/bus/isapnp.c
index 8f812df..ccf6209 100644
--- a/gpxe/src/drivers/bus/isapnp.c
+++ b/gpxe/src/drivers/bus/isapnp.c
@@ -55,6 +55,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -72,11 +74,6 @@
  */
 uint16_t isapnp_read_port;
 
-static struct isapnp_driver isapnp_drivers[0]
-	__table_start ( struct isapnp_driver, isapnp_drivers );
-static struct isapnp_driver isapnp_drivers_end[0]
-	__table_end ( struct isapnp_driver, isapnp_drivers );
-
 static void isapnpbus_remove ( struct root_device *rootdev );
 
 /*
@@ -594,7 +591,7 @@ static int isapnp_probe ( struct isapnp_device *isapnp ) {
 	      isa_id_string ( isapnp->vendor_id, isapnp->prod_id ),
 	      isapnp->ioaddr, isapnp->irqno );
 
-	for ( driver = isapnp_drivers; driver < isapnp_drivers_end; driver++ ){
+	for_each_table_entry ( driver, ISAPNP_DRIVERS ) {
 		for ( i = 0 ; i < driver->id_count ; i++ ) {
 			id = &driver->ids[i];
 			if ( id->vendor_id != isapnp->vendor_id )
diff --git a/gpxe/src/drivers/bus/mca.c b/gpxe/src/drivers/bus/mca.c
index e923381..2815603 100644
--- a/gpxe/src/drivers/bus/mca.c
+++ b/gpxe/src/drivers/bus/mca.c
@@ -5,6 +5,8 @@
  *
  */
 
+FILE_LICENCE ( BSD2 );
+
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
@@ -13,11 +15,6 @@
 #include <gpxe/io.h>
 #include <gpxe/mca.h>
 
-static struct mca_driver mca_drivers[0]
-	__table_start ( struct mca_driver, mca_drivers );
-static struct mca_driver mca_drivers_end[0]
-	__table_end ( struct mca_driver, mca_drivers );
-
 static void mcabus_remove ( struct root_device *rootdev );
 
 /**
@@ -41,7 +38,7 @@ static int mca_probe ( struct mca_device *mca ) {
 	      mca->pos[0], mca->pos[1], mca->pos[2], mca->pos[3],
 	      mca->pos[4], mca->pos[5], mca->pos[6], mca->pos[7] );
 
-	for ( driver = mca_drivers; driver < mca_drivers_end; driver++ ){
+	for_each_table_entry ( driver, MCA_DRIVERS ) {
 		for ( i = 0 ; i < driver->id_count ; i++ ) {
 			id = &driver->ids[i];
 			if ( id->id != MCA_ID ( mca ) )
diff --git a/gpxe/src/drivers/bus/pci.c b/gpxe/src/drivers/bus/pci.c
index 2dc9d43..8899e6e 100644
--- a/gpxe/src/drivers/bus/pci.c
+++ b/gpxe/src/drivers/bus/pci.c
@@ -19,6 +19,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -34,11 +36,6 @@
  *
  */
 
-static struct pci_driver pci_drivers[0]
-	__table_start ( struct pci_driver, pci_drivers );
-static struct pci_driver pci_drivers_end[0]
-	__table_end ( struct pci_driver, pci_drivers );
-
 static void pcibus_remove ( struct root_device *rootdev );
 
 /**
@@ -188,7 +185,7 @@ static int pci_probe ( struct pci_device *pci ) {
 	      PCI_FUNC ( pci->devfn ), pci->vendor, pci->device,
 	      pci->membase, pci->ioaddr, pci->irq );
 
-	for ( driver = pci_drivers ; driver < pci_drivers_end ; driver++ ) {
+	for_each_table_entry ( driver, PCI_DRIVERS ) {
 		for ( i = 0 ; i < driver->id_count ; i++ ) {
 			id = &driver->ids[i];
 			if ( ( id->vendor != PCI_ANY_ID ) &&
diff --git a/gpxe/src/drivers/bus/pcibackup.c b/gpxe/src/drivers/bus/pcibackup.c
new file mode 100644
index 0000000..8f9994e
--- /dev/null
+++ b/gpxe/src/drivers/bus/pcibackup.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <gpxe/pci.h>
+#include <gpxe/pcibackup.h>
+
+/** @file
+ *
+ * PCI configuration space backup and restoration
+ *
+ */
+
+/**
+ * Check PCI configuration space offset against exclusion list
+ *
+ * @v pci		PCI device
+ * @v offset		Offset within PCI configuration space
+ * @v exclude		PCI configuration space backup exclusion list, or NULL
+ */
+static int
+pci_backup_excluded ( struct pci_device *pci, unsigned int offset,
+		      const uint8_t *exclude ) {
+
+	if ( ! exclude )
+		return 0;
+	for ( ; *exclude != PCI_CONFIG_BACKUP_EXCLUDE_END ; exclude++ ) {
+		if ( offset == *exclude ) {
+			DBGC ( pci, "PCI %p skipping configuration offset "
+			       "%02x\n", pci, offset );
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/**
+ * Back up PCI configuration space
+ *
+ * @v pci		PCI device
+ * @v backup		PCI configuration space backup
+ * @v exclude		PCI configuration space backup exclusion list, or NULL
+ */
+void pci_backup ( struct pci_device *pci, struct pci_config_backup *backup,
+		  const uint8_t *exclude ) {
+	unsigned int offset;
+	uint32_t *dword;
+
+	for ( offset = 0, dword = backup->dwords ; offset < 0x100 ;
+	      offset += sizeof ( *dword ) , dword++ ) {
+		if ( ! pci_backup_excluded ( pci, offset, exclude ) )
+			pci_read_config_dword ( pci, offset, dword );
+	}
+}
+
+/**
+ * Restore PCI configuration space
+ *
+ * @v pci		PCI device
+ * @v backup		PCI configuration space backup
+ * @v exclude		PCI configuration space backup exclusion list, or NULL
+ */
+void pci_restore ( struct pci_device *pci, struct pci_config_backup *backup,
+		   const uint8_t *exclude ) {
+	unsigned int offset;
+	uint32_t *dword;
+
+	for ( offset = 0, dword = backup->dwords ; offset < 0x100 ;
+	      offset += sizeof ( *dword ) , dword++ ) {
+		if ( ! pci_backup_excluded ( pci, offset, exclude ) )
+			pci_write_config_dword ( pci, offset, *dword );
+	}
+}
diff --git a/gpxe/src/drivers/bus/pciextra.c b/gpxe/src/drivers/bus/pciextra.c
index 4603bcb..74c4099 100644
--- a/gpxe/src/drivers/bus/pciextra.c
+++ b/gpxe/src/drivers/bus/pciextra.c
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/pci.h>
 
@@ -58,8 +60,11 @@ int pci_find_capability ( struct pci_device *pci, int cap ) {
  * function.
  */
 unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) {
+	uint16_t cmd;
 	uint32_t start, size;
 
+	/* Save the original command register */
+	pci_read_config_word ( pci, PCI_COMMAND, &cmd );
 	/* Save the original bar */
 	pci_read_config_dword ( pci, reg, &start );
 	/* Compute which bits can be set */
@@ -68,6 +73,8 @@ unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) {
 	/* Restore the original size */
 	pci_write_config_dword ( pci, reg, start );
 	/* Find the significant bits */
+	/* Restore the original command register. This reenables decoding. */
+	pci_write_config_word ( pci, PCI_COMMAND, cmd );
 	if ( start & PCI_BASE_ADDRESS_SPACE_IO ) {
 		size &= PCI_BASE_ADDRESS_IO_MASK;
 	} else {
diff --git a/gpxe/src/drivers/infiniband/MT25218_PRM.h b/gpxe/src/drivers/infiniband/MT25218_PRM.h
index 19ca92c..f1b7c1f 100644
--- a/gpxe/src/drivers/infiniband/MT25218_PRM.h
+++ b/gpxe/src/drivers/infiniband/MT25218_PRM.h
@@ -19,6 +19,8 @@
   Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
 */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /***
  *** This file was generated at "Tue Nov 22 15:21:23 2005"
  *** by:
diff --git a/gpxe/src/drivers/infiniband/MT25408_PRM.h b/gpxe/src/drivers/infiniband/MT25408_PRM.h
index 17882ed..419e25a 100644
--- a/gpxe/src/drivers/infiniband/MT25408_PRM.h
+++ b/gpxe/src/drivers/infiniband/MT25408_PRM.h
@@ -19,6 +19,8 @@
   Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
 */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /***
  *** This file was generated at "Mon Apr 16 23:22:02 2007"
  *** by:
@@ -2069,7 +2071,11 @@ struct hermonprm_query_dev_cap_st {	/* Little Endian */
     pseudo_bit_t	pkv[0x00001];          /* PKey Violation Counter Supported */
     pseudo_bit_t	qkv[0x00001];          /* QKey Violation Coutner Supported */
     pseudo_bit_t	vmm[0x00001];          /* Hermon New */
-    pseudo_bit_t	reserved27[0x00005];
+    pseudo_bit_t	fcoe[0x00001];
+    pseudo_bit_t	dpdp[0x00001];	       /* Dual Port Different Protocols */
+    pseudo_bit_t	raw_ethertype[0x00001];
+    pseudo_bit_t	raw_ipv6[0x00001];
+    pseudo_bit_t	blh[0x00001];
     pseudo_bit_t	mw[0x00001];           /* Memory windows supported */
     pseudo_bit_t	apm[0x00001];          /* Automatic Path Migration Supported */
     pseudo_bit_t	atm[0x00001];          /* Atomic operations supported (atomicity is guaranteed between QPs on this HCA) */
diff --git a/gpxe/src/drivers/infiniband/arbel.c b/gpxe/src/drivers/infiniband/arbel.c
index 1756a6e..5bf3574 100644
--- a/gpxe/src/drivers/infiniband/arbel.c
+++ b/gpxe/src/drivers/infiniband/arbel.c
@@ -19,6 +19,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -853,7 +855,6 @@ static int arbel_create_qp ( struct ib_device *ibdev,
 		     ( virt_to_bus ( arbel_qp->recv.wqe ) >> 6 ) );
 	MLX_FILL_1 ( &qpctx, 43, qpc_eec_data.rcv_db_record_index,
 		     arbel_qp->recv.doorbell_idx );
-	MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
 	if ( ( rc = arbel_cmd_rst2init_qpee ( arbel, qp->qpn, &qpctx )) != 0 ){
 		DBGC ( arbel, "Arbel %p RST2INIT_QPEE failed: %s\n",
 		       arbel, strerror ( rc ) );
@@ -906,24 +907,17 @@ static int arbel_create_qp ( struct ib_device *ibdev,
  *
  * @v ibdev		Infiniband device
  * @v qp		Queue pair
- * @v mod_list		Modification list
  * @ret rc		Return status code
  */
 static int arbel_modify_qp ( struct ib_device *ibdev,
-			     struct ib_queue_pair *qp,
-			     unsigned long mod_list ) {
+			     struct ib_queue_pair *qp ) {
 	struct arbel *arbel = ib_get_drvdata ( ibdev );
 	struct arbelprm_qp_ee_state_transitions qpctx;
-	unsigned long optparammask = 0;
 	int rc;
 
-	/* Construct optparammask */
-	if ( mod_list & IB_MODIFY_QKEY )
-		optparammask |= ARBEL_QPEE_OPT_PARAM_QKEY;
-
 	/* Issue RTS2RTS_QP */
 	memset ( &qpctx, 0, sizeof ( qpctx ) );
-	MLX_FILL_1 ( &qpctx, 0, opt_param_mask, optparammask );
+	MLX_FILL_1 ( &qpctx, 0, opt_param_mask, ARBEL_QPEE_OPT_PARAM_QKEY );
 	MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
 	if ( ( rc = arbel_cmd_rts2rts_qp ( arbel, qp->qpn, &qpctx ) ) != 0 ){
 		DBGC ( arbel, "Arbel %p RTS2RTS_QP failed: %s\n",
@@ -2241,8 +2235,8 @@ static void arbel_remove ( struct pci_device *pci ) {
 }
 
 static struct pci_device_id arbel_nics[] = {
-	PCI_ROM ( 0x15b3, 0x6282, "mt25218", "MT25218 HCA driver" ),
-	PCI_ROM ( 0x15b3, 0x6274, "mt25204", "MT25204 HCA driver" ),
+	PCI_ROM ( 0x15b3, 0x6282, "mt25218", "MT25218 HCA driver", 0 ),
+	PCI_ROM ( 0x15b3, 0x6274, "mt25204", "MT25204 HCA driver", 0 ),
 };
 
 struct pci_driver arbel_driver __pci_driver = {
diff --git a/gpxe/src/drivers/infiniband/arbel.h b/gpxe/src/drivers/infiniband/arbel.h
index 7d97b15..87f5933 100644
--- a/gpxe/src/drivers/infiniband/arbel.h
+++ b/gpxe/src/drivers/infiniband/arbel.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/uaccess.h>
 #include "mlx_bitops.h"
diff --git a/gpxe/src/drivers/infiniband/hermon.c b/gpxe/src/drivers/infiniband/hermon.c
index 40add28..b9c97f9 100644
--- a/gpxe/src/drivers/infiniband/hermon.c
+++ b/gpxe/src/drivers/infiniband/hermon.c
@@ -17,6 +17,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -27,6 +29,7 @@
 #include <byteswap.h>
 #include <gpxe/io.h>
 #include <gpxe/pci.h>
+#include <gpxe/pcibackup.h>
 #include <gpxe/malloc.h>
 #include <gpxe/umalloc.h>
 #include <gpxe/iobuf.h>
@@ -199,9 +202,10 @@ static int hermon_cmd ( struct hermon *hermon, unsigned long command,
 		     opcode_modifier, op_mod,
 		     go, 1,
 		     t, hermon->toggle );
-	DBGC ( hermon, "Hermon %p issuing command:\n", hermon );
-	DBGC_HDA ( hermon, virt_to_phys ( hermon->config + HERMON_HCR_BASE ),
-		   &hcr, sizeof ( hcr ) );
+	DBGC ( hermon, "Hermon %p issuing command %04x\n",
+	       hermon, opcode );
+	DBGC2_HDA ( hermon, virt_to_phys ( hermon->config + HERMON_HCR_BASE ),
+		    &hcr, sizeof ( hcr ) );
 	if ( in_len && ( command & HERMON_HCR_IN_MBOX ) ) {
 		DBGC2 ( hermon, "Input mailbox:\n" );
 		DBGC2_HDA ( hermon, virt_to_phys ( in_buffer ), in_buffer,
@@ -416,6 +420,23 @@ hermon_cmd_2rst_qp ( struct hermon *hermon, unsigned long qpn ) {
 }
 
 static inline int
+hermon_cmd_query_qp ( struct hermon *hermon, unsigned long qpn,
+		      struct hermonprm_qp_ee_state_transitions *ctx ) {
+	return hermon_cmd ( hermon,
+			    HERMON_HCR_OUT_CMD ( HERMON_HCR_QUERY_QP,
+						 1, sizeof ( *ctx ) ),
+			    0, NULL, qpn, ctx );
+}
+
+static inline int
+hermon_cmd_conf_special_qp ( struct hermon *hermon, unsigned int internal_qps,
+			     unsigned long base_qpn ) {
+	return hermon_cmd ( hermon,
+			    HERMON_HCR_VOID_CMD ( HERMON_HCR_CONF_SPECIAL_QP ),
+			    internal_qps, NULL, base_qpn, NULL );
+}
+
+static inline int
 hermon_cmd_mad_ifc ( struct hermon *hermon, unsigned int port,
 		     union hermonprm_mad *mad ) {
 	return hermon_cmd ( hermon,
@@ -521,6 +542,16 @@ hermon_cmd_map_fa ( struct hermon *hermon,
 			    0, map, 1, NULL );
 }
 
+static inline int
+hermon_cmd_sense_port ( struct hermon *hermon, unsigned int port,
+			struct hermonprm_sense_port *port_type ) {
+	return hermon_cmd ( hermon,
+                            HERMON_HCR_OUT_CMD ( HERMON_HCR_SENSE_PORT,
+                                                 1, sizeof ( *port_type ) ),
+                            0, NULL, port, port_type );
+}
+
+
 /***************************************************************************
  *
  * Memory translation table operations
@@ -795,6 +826,124 @@ static void hermon_destroy_cq ( struct ib_device *ibdev,
  */
 
 /**
+ * Assign queue pair number
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @ret rc		Return status code
+ */
+static int hermon_alloc_qpn ( struct ib_device *ibdev,
+			      struct ib_queue_pair *qp ) {
+	struct hermon *hermon = ib_get_drvdata ( ibdev );
+	unsigned int port_offset;
+	int qpn_offset;
+
+	/* Calculate queue pair number */
+	port_offset = ( ibdev->port - HERMON_PORT_BASE );
+
+	switch ( qp->type ) {
+	case IB_QPT_SMI:
+		qp->qpn = ( hermon->special_qpn_base + port_offset );
+		return 0;
+	case IB_QPT_GSI:
+		qp->qpn = ( hermon->special_qpn_base + 2 + port_offset );
+		return 0;
+	case IB_QPT_UD:
+	case IB_QPT_RC:
+		/* Find a free queue pair number */
+		qpn_offset = hermon_bitmask_alloc ( hermon->qp_inuse,
+						    HERMON_MAX_QPS, 1 );
+		if ( qpn_offset < 0 ) {
+			DBGC ( hermon, "Hermon %p out of queue pairs\n",
+			       hermon );
+			return qpn_offset;
+		}
+		qp->qpn = ( ( random() & HERMON_QPN_RANDOM_MASK ) |
+			    ( hermon->qpn_base + qpn_offset ) );
+		return 0;
+	default:
+		DBGC ( hermon, "Hermon %p unsupported QP type %d\n",
+		       hermon, qp->type );
+		return -ENOTSUP;
+	}
+}
+
+/**
+ * Free queue pair number
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ */
+static void hermon_free_qpn ( struct ib_device *ibdev,
+			      struct ib_queue_pair *qp ) {
+	struct hermon *hermon = ib_get_drvdata ( ibdev );
+	int qpn_offset;
+
+	qpn_offset = ( ( qp->qpn & ~HERMON_QPN_RANDOM_MASK )
+		       - hermon->qpn_base );
+	if ( qpn_offset >= 0 )
+		hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 );
+}
+
+/**
+ * Calculate transmission rate
+ *
+ * @v av		Address vector
+ * @ret hermon_rate	Hermon rate
+ */
+static unsigned int hermon_rate ( struct ib_address_vector *av ) {
+	return ( ( ( av->rate >= IB_RATE_2_5 ) && ( av->rate <= IB_RATE_120 ) )
+		 ? ( av->rate + 5 ) : 0 );
+}
+
+/**
+ * Calculate schedule queue
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @ret sched_queue	Schedule queue
+ */
+static unsigned int hermon_sched_queue ( struct ib_device *ibdev,
+					 struct ib_queue_pair *qp ) {
+	return ( ( ( qp->type == IB_QPT_SMI ) ?
+		   HERMON_SCHED_QP0 : HERMON_SCHED_DEFAULT ) |
+		 ( ( ibdev->port - 1 ) << 6 ) );
+}
+
+/** Queue pair transport service type map */
+static uint8_t hermon_qp_st[] = {
+	[IB_QPT_SMI] = HERMON_ST_MLX,
+	[IB_QPT_GSI] = HERMON_ST_MLX,
+	[IB_QPT_UD] = HERMON_ST_UD,
+	[IB_QPT_RC] = HERMON_ST_RC,
+};
+
+/**
+ * Dump queue pair context (for debugging only)
+ *
+ * @v hermon		Hermon device
+ * @v qp		Queue pair
+ * @ret rc		Return status code
+ */
+static inline int hermon_dump_qpctx ( struct hermon *hermon,
+				      struct ib_queue_pair *qp ) {
+	struct hermonprm_qp_ee_state_transitions qpctx;
+	int rc;
+
+	memset ( &qpctx, 0, sizeof ( qpctx ) );
+	if ( ( rc = hermon_cmd_query_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ) {
+		DBGC ( hermon, "Hermon %p QUERY_QP failed: %s\n",
+		       hermon, strerror ( rc ) );
+		return rc;
+	}
+	DBGC ( hermon, "Hermon %p QPN %lx context:\n", hermon, qp->qpn );
+	DBGC_HDA ( hermon, 0, &qpctx.u.dwords[2],
+		   ( sizeof ( qpctx ) - 8 ) );
+
+	return 0;
+}
+
+/**
  * Create queue pair
  *
  * @v ibdev		Infiniband device
@@ -806,19 +955,11 @@ static int hermon_create_qp ( struct ib_device *ibdev,
 	struct hermon *hermon = ib_get_drvdata ( ibdev );
 	struct hermon_queue_pair *hermon_qp;
 	struct hermonprm_qp_ee_state_transitions qpctx;
-	int qpn_offset;
 	int rc;
 
-	/* Find a free queue pair number */
-	qpn_offset = hermon_bitmask_alloc ( hermon->qp_inuse,
-					    HERMON_MAX_QPS, 1 );
-	if ( qpn_offset < 0 ) {
-		DBGC ( hermon, "Hermon %p out of queue pairs\n", hermon );
-		rc = qpn_offset;
-		goto err_qpn_offset;
-	}
-	qp->qpn = ( HERMON_QPN_BASE + hermon->cap.reserved_qps +
-		    qpn_offset );
+	/* Calculate queue pair number */
+	if ( ( rc = hermon_alloc_qpn ( ibdev, qp ) ) != 0 )
+		goto err_alloc_qpn;
 
 	/* Allocate control structures */
 	hermon_qp = zalloc ( sizeof ( *hermon_qp ) );
@@ -864,8 +1005,8 @@ static int hermon_create_qp ( struct ib_device *ibdev,
 	/* Transition queue to INIT state */
 	memset ( &qpctx, 0, sizeof ( qpctx ) );
 	MLX_FILL_2 ( &qpctx, 2,
-		     qpc_eec_data.pm_state, 0x03 /* Always 0x03 for UD */,
-		     qpc_eec_data.st, HERMON_ST_UD );
+		     qpc_eec_data.pm_state, HERMON_PM_STATE_MIGRATED,
+		     qpc_eec_data.st, hermon_qp_st[qp->type] );
 	MLX_FILL_1 ( &qpctx, 3, qpc_eec_data.pd, HERMON_GLOBAL_PD );
 	MLX_FILL_4 ( &qpctx, 4,
 		     qpc_eec_data.log_rq_size, fls ( qp->recv.num_wqes - 1 ),
@@ -878,12 +1019,15 @@ static int hermon_create_qp ( struct ib_device *ibdev,
 	MLX_FILL_1 ( &qpctx, 5,
 		     qpc_eec_data.usr_page, HERMON_UAR_NON_EQ_PAGE );
 	MLX_FILL_1 ( &qpctx, 33, qpc_eec_data.cqn_snd, qp->send.cq->cqn );
-	MLX_FILL_1 ( &qpctx, 38, qpc_eec_data.page_offset,
+	MLX_FILL_4 ( &qpctx, 38,
+		     qpc_eec_data.rre, 1,
+		     qpc_eec_data.rwe, 1,
+		     qpc_eec_data.rae, 1,
+		     qpc_eec_data.page_offset,
 		     ( hermon_qp->mtt.page_offset >> 6 ) );
 	MLX_FILL_1 ( &qpctx, 41, qpc_eec_data.cqn_rcv, qp->recv.cq->cqn );
 	MLX_FILL_1 ( &qpctx, 43, qpc_eec_data.db_record_addr_l,
 		     ( virt_to_phys ( &hermon_qp->recv.doorbell ) >> 2 ) );
-	MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
 	MLX_FILL_1 ( &qpctx, 53, qpc_eec_data.mtt_base_addr_l,
 		     ( hermon_qp->mtt.mtt_base_addr >> 3 ) );
 	if ( ( rc = hermon_cmd_rst2init_qp ( hermon, qp->qpn,
@@ -892,28 +1036,7 @@ static int hermon_create_qp ( struct ib_device *ibdev,
 		       hermon, strerror ( rc ) );
 		goto err_rst2init_qp;
 	}
-
-	/* Transition queue to RTR state */
-	memset ( &qpctx, 0, sizeof ( qpctx ) );
-	MLX_FILL_2 ( &qpctx, 4,
-		     qpc_eec_data.mtu, HERMON_MTU_2048,
-		     qpc_eec_data.msg_max, 11 /* 2^11 = 2048 */ );
-	MLX_FILL_1 ( &qpctx, 16,
-		     qpc_eec_data.primary_address_path.sched_queue,
-		     ( 0x83 /* default policy */ |
-		       ( ( ibdev->port - 1 ) << 6 ) ) );
-	if ( ( rc = hermon_cmd_init2rtr_qp ( hermon, qp->qpn,
-					     &qpctx ) ) != 0 ) {
-		DBGC ( hermon, "Hermon %p INIT2RTR_QP failed: %s\n",
-		       hermon, strerror ( rc ) );
-		goto err_init2rtr_qp;
-	}
-	memset ( &qpctx, 0, sizeof ( qpctx ) );
-	if ( ( rc = hermon_cmd_rtr2rts_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ){
-		DBGC ( hermon, "Hermon %p RTR2RTS_QP failed: %s\n",
-		       hermon, strerror ( rc ) );
-		goto err_rtr2rts_qp;
-	}
+	hermon_qp->state = HERMON_QP_ST_INIT;
 
 	DBGC ( hermon, "Hermon %p QPN %#lx send ring at [%p,%p)\n",
 	       hermon, qp->qpn, hermon_qp->send.wqe,
@@ -924,8 +1047,6 @@ static int hermon_create_qp ( struct ib_device *ibdev,
 	ib_qp_set_drvdata ( qp, hermon_qp );
 	return 0;
 
- err_rtr2rts_qp:
- err_init2rtr_qp:
 	hermon_cmd_2rst_qp ( hermon, qp->qpn );
  err_rst2init_qp:
 	hermon_free_mtt ( hermon, &hermon_qp->mtt );
@@ -934,8 +1055,8 @@ static int hermon_create_qp ( struct ib_device *ibdev,
  err_alloc_wqe:
 	free ( hermon_qp );
  err_hermon_qp:
-	hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 );
- err_qpn_offset:
+	hermon_free_qpn ( ibdev, qp );
+ err_alloc_qpn:
 	return rc;
 }
 
@@ -944,24 +1065,68 @@ static int hermon_create_qp ( struct ib_device *ibdev,
  *
  * @v ibdev		Infiniband device
  * @v qp		Queue pair
- * @v mod_list		Modification list
  * @ret rc		Return status code
  */
 static int hermon_modify_qp ( struct ib_device *ibdev,
-			      struct ib_queue_pair *qp,
-			      unsigned long mod_list ) {
+			      struct ib_queue_pair *qp ) {
 	struct hermon *hermon = ib_get_drvdata ( ibdev );
+	struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
 	struct hermonprm_qp_ee_state_transitions qpctx;
-	unsigned long optparammask = 0;
 	int rc;
 
-	/* Construct optparammask */
-	if ( mod_list & IB_MODIFY_QKEY )
-		optparammask |= HERMON_QP_OPT_PARAM_QKEY;
+	/* Transition queue to RTR state, if applicable */
+	if ( hermon_qp->state < HERMON_QP_ST_RTR ) {
+		memset ( &qpctx, 0, sizeof ( qpctx ) );
+		MLX_FILL_2 ( &qpctx, 4,
+			     qpc_eec_data.mtu, HERMON_MTU_2048,
+			     qpc_eec_data.msg_max, 31 );
+		MLX_FILL_1 ( &qpctx, 7,
+			     qpc_eec_data.remote_qpn_een, qp->av.qpn );
+		MLX_FILL_1 ( &qpctx, 9,
+			     qpc_eec_data.primary_address_path.rlid,
+			     qp->av.lid );
+		MLX_FILL_1 ( &qpctx, 10,
+			     qpc_eec_data.primary_address_path.max_stat_rate,
+			     hermon_rate ( &qp->av ) );
+		memcpy ( &qpctx.u.dwords[12], &qp->av.gid,
+			 sizeof ( qp->av.gid ) );
+		MLX_FILL_1 ( &qpctx, 16,
+			     qpc_eec_data.primary_address_path.sched_queue,
+			     hermon_sched_queue ( ibdev, qp ) );
+		MLX_FILL_1 ( &qpctx, 39,
+			     qpc_eec_data.next_rcv_psn, qp->recv.psn );
+		if ( ( rc = hermon_cmd_init2rtr_qp ( hermon, qp->qpn,
+						     &qpctx ) ) != 0 ) {
+			DBGC ( hermon, "Hermon %p INIT2RTR_QP failed: %s\n",
+			       hermon, strerror ( rc ) );
+			return rc;
+		}
+		hermon_qp->state = HERMON_QP_ST_RTR;
+	}
+
+	/* Transition queue to RTS state */
+	if ( hermon_qp->state < HERMON_QP_ST_RTS ) {
+		memset ( &qpctx, 0, sizeof ( qpctx ) );
+		MLX_FILL_1 ( &qpctx, 10,
+			     qpc_eec_data.primary_address_path.ack_timeout,
+			     14 /* 4.096us * 2^(14) = 67ms */ );
+		MLX_FILL_2 ( &qpctx, 30,
+			     qpc_eec_data.retry_count, HERMON_RETRY_MAX,
+			     qpc_eec_data.rnr_retry, HERMON_RETRY_MAX );
+		MLX_FILL_1 ( &qpctx, 32,
+			     qpc_eec_data.next_send_psn, qp->send.psn );
+		if ( ( rc = hermon_cmd_rtr2rts_qp ( hermon, qp->qpn,
+						    &qpctx ) ) != 0 ) {
+			DBGC ( hermon, "Hermon %p RTR2RTS_QP failed: %s\n",
+			       hermon, strerror ( rc ) );
+			return rc;
+		}
+		hermon_qp->state = HERMON_QP_ST_RTS;
+	}
 
-	/* Issue RTS2RTS_QP */
+	/* Update parameters in RTS state */
 	memset ( &qpctx, 0, sizeof ( qpctx ) );
-	MLX_FILL_1 ( &qpctx, 0, opt_param_mask, optparammask );
+	MLX_FILL_1 ( &qpctx, 0, opt_param_mask, HERMON_QP_OPT_PARAM_QKEY );
 	MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
 	if ( ( rc = hermon_cmd_rts2rts_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ){
 		DBGC ( hermon, "Hermon %p RTS2RTS_QP failed: %s\n",
@@ -982,7 +1147,6 @@ static void hermon_destroy_qp ( struct ib_device *ibdev,
 				struct ib_queue_pair *qp ) {
 	struct hermon *hermon = ib_get_drvdata ( ibdev );
 	struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
-	int qpn_offset;
 	int rc;
 
 	/* Take ownership back from hardware */
@@ -1001,9 +1165,7 @@ static void hermon_destroy_qp ( struct ib_device *ibdev,
 	free ( hermon_qp );
 
 	/* Mark queue number as free */
-	qpn_offset = ( qp->qpn - HERMON_QPN_BASE -
-		       hermon->cap.reserved_qps );
-	hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 );
+	hermon_free_qpn ( ibdev, qp );
 
 	ib_qp_set_drvdata ( qp, NULL );
 }
@@ -1015,9 +1177,133 @@ static void hermon_destroy_qp ( struct ib_device *ibdev,
  ***************************************************************************
  */
 
-/** GID used for GID-less send work queue entries */
-static const struct ib_gid hermon_no_gid = {
-	{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+/**
+ * Construct UD send work queue entry
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v av		Address vector
+ * @v iobuf		I/O buffer
+ * @v wqe		Send work queue entry
+ * @ret opcode		Control opcode
+ */
+static unsigned int
+hermon_fill_ud_send_wqe ( struct ib_device *ibdev,
+			  struct ib_queue_pair *qp __unused,
+			  struct ib_address_vector *av,
+			  struct io_buffer *iobuf,
+			  union hermon_send_wqe *wqe ) {
+	struct hermon *hermon = ib_get_drvdata ( ibdev );
+
+	MLX_FILL_1 ( &wqe->ud.ctrl, 1, ds,
+		     ( ( offsetof ( typeof ( wqe->ud ), data[1] ) / 16 ) ) );
+	MLX_FILL_1 ( &wqe->ud.ctrl, 2, c, 0x03 /* generate completion */ );
+	MLX_FILL_2 ( &wqe->ud.ud, 0,
+		     ud_address_vector.pd, HERMON_GLOBAL_PD,
+		     ud_address_vector.port_number, ibdev->port );
+	MLX_FILL_2 ( &wqe->ud.ud, 1,
+		     ud_address_vector.rlid, av->lid,
+		     ud_address_vector.g, av->gid_present );
+	MLX_FILL_1 ( &wqe->ud.ud, 2,
+		     ud_address_vector.max_stat_rate, hermon_rate ( av ) );
+	MLX_FILL_1 ( &wqe->ud.ud, 3, ud_address_vector.sl, av->sl );
+	memcpy ( &wqe->ud.ud.u.dwords[4], &av->gid, sizeof ( av->gid ) );
+	MLX_FILL_1 ( &wqe->ud.ud, 8, destination_qp, av->qpn );
+	MLX_FILL_1 ( &wqe->ud.ud, 9, q_key, av->qkey );
+	MLX_FILL_1 ( &wqe->ud.data[0], 0, byte_count, iob_len ( iobuf ) );
+	MLX_FILL_1 ( &wqe->ud.data[0], 1, l_key, hermon->lkey );
+	MLX_FILL_1 ( &wqe->ud.data[0], 3,
+		     local_address_l, virt_to_bus ( iobuf->data ) );
+	return HERMON_OPCODE_SEND;
+}
+
+/**
+ * Construct MLX send work queue entry
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v av		Address vector
+ * @v iobuf		I/O buffer
+ * @v wqe		Send work queue entry
+ * @ret opcode		Control opcode
+ */
+static unsigned int
+hermon_fill_mlx_send_wqe ( struct ib_device *ibdev,
+			   struct ib_queue_pair *qp,
+			   struct ib_address_vector *av,
+			   struct io_buffer *iobuf,
+			   union hermon_send_wqe *wqe ) {
+	struct hermon *hermon = ib_get_drvdata ( ibdev );
+	struct io_buffer headers;
+
+	/* Construct IB headers */
+	iob_populate ( &headers, &wqe->mlx.headers, 0,
+		       sizeof ( wqe->mlx.headers ) );
+	iob_reserve ( &headers, sizeof ( wqe->mlx.headers ) );
+	ib_push ( ibdev, &headers, qp, iob_len ( iobuf ), av );
+
+	/* Fill work queue entry */
+	MLX_FILL_1 ( &wqe->mlx.ctrl, 1, ds,
+		     ( ( offsetof ( typeof ( wqe->mlx ), data[2] ) / 16 ) ) );
+	MLX_FILL_5 ( &wqe->mlx.ctrl, 2,
+		     c, 0x03 /* generate completion */,
+		     icrc, 0 /* generate ICRC */,
+		     max_statrate, hermon_rate ( av ),
+		     slr, 0,
+		     v15, ( ( qp->ext_qpn == IB_QPN_SMI ) ? 1 : 0 ) );
+	MLX_FILL_1 ( &wqe->mlx.ctrl, 3, rlid, av->lid );
+	MLX_FILL_1 ( &wqe->mlx.data[0], 0,
+		     byte_count, iob_len ( &headers ) );
+	MLX_FILL_1 ( &wqe->mlx.data[0], 1, l_key, hermon->lkey );
+	MLX_FILL_1 ( &wqe->mlx.data[0], 3,
+		     local_address_l, virt_to_bus ( headers.data ) );
+	MLX_FILL_1 ( &wqe->mlx.data[1], 0,
+		     byte_count, ( iob_len ( iobuf ) + 4 /* ICRC */ ) );
+	MLX_FILL_1 ( &wqe->mlx.data[1], 1, l_key, hermon->lkey );
+	MLX_FILL_1 ( &wqe->mlx.data[1], 3,
+		     local_address_l, virt_to_bus ( iobuf->data ) );
+	return HERMON_OPCODE_SEND;
+}
+
+/**
+ * Construct RC send work queue entry
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v av		Address vector
+ * @v iobuf		I/O buffer
+ * @v wqe		Send work queue entry
+ * @ret opcode		Control opcode
+ */
+static unsigned int
+hermon_fill_rc_send_wqe ( struct ib_device *ibdev,
+			  struct ib_queue_pair *qp __unused,
+			  struct ib_address_vector *av __unused,
+			  struct io_buffer *iobuf,
+			  union hermon_send_wqe *wqe ) {
+	struct hermon *hermon = ib_get_drvdata ( ibdev );
+
+	MLX_FILL_1 ( &wqe->rc.ctrl, 1, ds,
+		     ( ( offsetof ( typeof ( wqe->rc ), data[1] ) / 16 ) ) );
+	MLX_FILL_1 ( &wqe->rc.ctrl, 2, c, 0x03 /* generate completion */ );
+	MLX_FILL_1 ( &wqe->rc.data[0], 0, byte_count, iob_len ( iobuf ) );
+	MLX_FILL_1 ( &wqe->rc.data[0], 1, l_key, hermon->lkey );
+	MLX_FILL_1 ( &wqe->rc.data[0], 3,
+		     local_address_l, virt_to_bus ( iobuf->data ) );
+	return HERMON_OPCODE_SEND;
+}
+
+/** Work queue entry constructors */
+static unsigned int
+( * hermon_fill_send_wqe[] ) ( struct ib_device *ibdev,
+			       struct ib_queue_pair *qp,
+			       struct ib_address_vector *av,
+			       struct io_buffer *iobuf,
+			       union hermon_send_wqe *wqe ) = {
+	[IB_QPT_SMI] = hermon_fill_mlx_send_wqe,
+	[IB_QPT_GSI] = hermon_fill_mlx_send_wqe,
+	[IB_QPT_UD] = hermon_fill_ud_send_wqe,
+	[IB_QPT_RC] = hermon_fill_rc_send_wqe,
 };
 
 /**
@@ -1037,10 +1323,10 @@ static int hermon_post_send ( struct ib_device *ibdev,
 	struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
 	struct ib_work_queue *wq = &qp->send;
 	struct hermon_send_work_queue *hermon_send_wq = &hermon_qp->send;
-	struct hermonprm_ud_send_wqe *wqe;
-	const struct ib_gid *gid;
+	union hermon_send_wqe *wqe;
 	union hermonprm_doorbell_register db_reg;
 	unsigned int wqe_idx_mask;
+	unsigned int opcode;
 
 	/* Allocate work queue entry */
 	wqe_idx_mask = ( wq->num_wqes - 1 );
@@ -1050,35 +1336,18 @@ static int hermon_post_send ( struct ib_device *ibdev,
 	}
 	wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf;
 	wqe = &hermon_send_wq->wqe[ wq->next_idx &
-				    ( hermon_send_wq->num_wqes - 1 ) ].ud;
+				    ( hermon_send_wq->num_wqes - 1 ) ];
 
 	/* Construct work queue entry */
 	memset ( ( ( ( void * ) wqe ) + 4 /* avoid ctrl.owner */ ), 0,
 		   ( sizeof ( *wqe ) - 4 ) );
-	MLX_FILL_1 ( &wqe->ctrl, 1, ds, ( sizeof ( *wqe ) / 16 ) );
-	MLX_FILL_1 ( &wqe->ctrl, 2, c, 0x03 /* generate completion */ );
-	MLX_FILL_2 ( &wqe->ud, 0,
-		     ud_address_vector.pd, HERMON_GLOBAL_PD,
-		     ud_address_vector.port_number, ibdev->port );
-	MLX_FILL_2 ( &wqe->ud, 1,
-		     ud_address_vector.rlid, av->lid,
-		     ud_address_vector.g, av->gid_present );
-	MLX_FILL_1 ( &wqe->ud, 2,
-		     ud_address_vector.max_stat_rate,
-		     ( ( ( av->rate < 2 ) || ( av->rate > 10 ) ) ?
-		       8 : ( av->rate + 5 ) ) );
-	MLX_FILL_1 ( &wqe->ud, 3, ud_address_vector.sl, av->sl );
-	gid = ( av->gid_present ? &av->gid : &hermon_no_gid );
-	memcpy ( &wqe->ud.u.dwords[4], gid, sizeof ( *gid ) );
-	MLX_FILL_1 ( &wqe->ud, 8, destination_qp, av->qpn );
-	MLX_FILL_1 ( &wqe->ud, 9, q_key, av->qkey );
-	MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_len ( iobuf ) );
-	MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->reserved_lkey );
-	MLX_FILL_1 ( &wqe->data[0], 3,
-		     local_address_l, virt_to_bus ( iobuf->data ) );
+	assert ( qp->type < ( sizeof ( hermon_fill_send_wqe ) /
+			      sizeof ( hermon_fill_send_wqe[0] ) ) );
+	assert ( hermon_fill_send_wqe[qp->type] != NULL );
+	opcode = hermon_fill_send_wqe[qp->type] ( ibdev, qp, av, iobuf, wqe );
 	barrier();
 	MLX_FILL_2 ( &wqe->ctrl, 0,
-		     opcode, HERMON_OPCODE_SEND,
+		     opcode, opcode,
 		     owner,
 		     ( ( wq->next_idx & hermon_send_wq->num_wqes ) ? 1 : 0 ) );
 	DBGCP ( hermon, "Hermon %p posting send WQE:\n", hermon );
@@ -1126,7 +1395,7 @@ static int hermon_post_recv ( struct ib_device *ibdev,
 
 	/* Construct work queue entry */
 	MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) );
-	MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->reserved_lkey );
+	MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->lkey );
 	MLX_FILL_1 ( &wqe->data[0], 3,
 		     local_address_l, virt_to_bus ( iobuf->data ) );
 
@@ -1157,8 +1426,9 @@ static int hermon_complete ( struct ib_device *ibdev,
 	struct ib_queue_pair *qp;
 	struct hermon_queue_pair *hermon_qp;
 	struct io_buffer *iobuf;
-	struct ib_address_vector av;
+	struct ib_address_vector recv_av;
 	struct ib_global_route_header *grh;
+	struct ib_address_vector *av;
 	unsigned int opcode;
 	unsigned long qpn;
 	int is_send;
@@ -1196,7 +1466,7 @@ static int hermon_complete ( struct ib_device *ibdev,
 	iobuf = wq->iobufs[wqe_idx];
 	if ( ! iobuf ) {
 		DBGC ( hermon, "Hermon %p CQN %lx QPN %lx empty WQE %x\n",
-		       hermon, cq->cqn, qpn, wqe_idx );
+		       hermon, cq->cqn, qp->qpn, wqe_idx );
 		return -EIO;
 	}
 	wq->iobufs[wqe_idx] = NULL;
@@ -1209,18 +1479,31 @@ static int hermon_complete ( struct ib_device *ibdev,
 		len = MLX_GET ( &cqe->normal, byte_cnt );
 		assert ( len <= iob_tailroom ( iobuf ) );
 		iob_put ( iobuf, len );
-		assert ( iob_len ( iobuf ) >= sizeof ( *grh ) );
-		grh = iobuf->data;
-		iob_pull ( iobuf, sizeof ( *grh ) );
-		/* Construct address vector */
-		memset ( &av, 0, sizeof ( av ) );
-		av.qpn = MLX_GET ( &cqe->normal, srq_rqpn );
-		av.lid = MLX_GET ( &cqe->normal, slid_smac47_32 );
-		av.sl = MLX_GET ( &cqe->normal, sl );
-		av.gid_present = MLX_GET ( &cqe->normal, g );
-		memcpy ( &av.gid, &grh->sgid, sizeof ( av.gid ) );
+		switch ( qp->type ) {
+		case IB_QPT_SMI:
+		case IB_QPT_GSI:
+		case IB_QPT_UD:
+			assert ( iob_len ( iobuf ) >= sizeof ( *grh ) );
+			grh = iobuf->data;
+			iob_pull ( iobuf, sizeof ( *grh ) );
+			/* Construct address vector */
+			av = &recv_av;
+			memset ( av, 0, sizeof ( *av ) );
+			av->qpn = MLX_GET ( &cqe->normal, srq_rqpn );
+			av->lid = MLX_GET ( &cqe->normal, slid_smac47_32 );
+			av->sl = MLX_GET ( &cqe->normal, sl );
+			av->gid_present = MLX_GET ( &cqe->normal, g );
+			memcpy ( &av->gid, &grh->sgid, sizeof ( av->gid ) );
+			break;
+		case IB_QPT_RC:
+			av = &qp->av;
+			break;
+		default:
+			assert ( 0 );
+			return -EINVAL;
+		}
 		/* Hand off to completion handler */
-		ib_complete_recv ( ibdev, qp, &av, iobuf, rc );
+		ib_complete_recv ( ibdev, qp, av, iobuf, rc );
 	}
 
 	return rc;
@@ -1417,7 +1700,7 @@ static void hermon_event_port_state_change ( struct hermon *hermon,
 	       ( link_up ? "up" : "down" ) );
 
 	/* Sanity check */
-	if ( port >= HERMON_NUM_PORTS ) {
+	if ( port >= hermon->cap.num_ports ) {
 		DBGC ( hermon, "Hermon %p port %d does not exist!\n",
 		       hermon, ( port + 1 ) );
 		return;
@@ -1489,6 +1772,36 @@ static void hermon_poll_eq ( struct ib_device *ibdev ) {
  */
 
 /**
+ * Sense port type
+ *
+ * @v ibdev		Infiniband device
+ * @ret port_type	Port type, or negative error
+ */
+static int hermon_sense_port_type ( struct ib_device *ibdev ) {
+	struct hermon *hermon = ib_get_drvdata ( ibdev );
+	struct hermonprm_sense_port sense_port;
+	int port_type;
+	int rc;
+
+	/* If DPDP is not supported, always assume Infiniband */
+	if ( ! hermon->cap.dpdp )
+		return HERMON_PORT_TYPE_IB;
+
+	/* Sense the port type */
+	if ( ( rc = hermon_cmd_sense_port ( hermon, ibdev->port,
+					    &sense_port ) ) != 0 ) {
+		DBGC ( hermon, "Hermon %p port %d sense failed: %s\n",
+		       hermon, ibdev->port, strerror ( rc ) );
+		return rc;
+	}
+	port_type = MLX_GET ( &sense_port, port_type );
+
+	DBGC ( hermon, "Hermon %p port %d type %d\n",
+	       hermon, ibdev->port, port_type );
+	return port_type;
+}
+
+/**
  * Initialise Infiniband link
  *
  * @v ibdev		Infiniband device
@@ -1497,8 +1810,19 @@ static void hermon_poll_eq ( struct ib_device *ibdev ) {
 static int hermon_open ( struct ib_device *ibdev ) {
 	struct hermon *hermon = ib_get_drvdata ( ibdev );
 	struct hermonprm_init_port init_port;
+	int port_type;
 	int rc;
 
+	/* Check we are connected to an Infiniband network */
+	if ( ( rc = port_type = hermon_sense_port_type ( ibdev ) ) < 0 )
+		return rc;
+	if ( port_type != HERMON_PORT_TYPE_IB ) {
+		DBGC ( hermon, "Hermon %p port %d not connected to an "
+		       "Infiniband network", hermon, ibdev->port );
+		return -ENOTCONN;
+        }
+
+	/* Init Port */
 	memset ( &init_port, 0, sizeof ( init_port ) );
 	MLX_FILL_2 ( &init_port, 0,
 		     port_width_cap, 3,
@@ -1536,6 +1860,27 @@ static void hermon_close ( struct ib_device *ibdev ) {
 	}
 }
 
+/**
+ * Inform embedded subnet management agent of a received MAD
+ *
+ * @v ibdev		Infiniband device
+ * @v mad		MAD
+ * @ret rc		Return status code
+ */
+static int hermon_inform_sma ( struct ib_device *ibdev,
+			       union ib_mad *mad ) {
+	int rc;
+
+	/* Send the MAD to the embedded SMA */
+	if ( ( rc = hermon_mad ( ibdev, mad ) ) != 0 )
+		return rc;
+
+	/* Update parameters held in software */
+	ib_smc_update ( ibdev, hermon_mad );
+
+	return 0;
+}
+
 /***************************************************************************
  *
  * Multicast group operations
@@ -1646,6 +1991,8 @@ static struct ib_device_operations hermon_ib_operations = {
 	.close		= hermon_close,
 	.mcast_attach	= hermon_mcast_attach,
 	.mcast_detach	= hermon_mcast_detach,
+	.set_port_info	= hermon_inform_sma,
+	.set_pkey_table	= hermon_inform_sma,
 };
 
 /***************************************************************************
@@ -1676,6 +2023,12 @@ static int hermon_map_vpm ( struct hermon *hermon,
 	assert ( ( pa & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
 	assert ( ( len & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
 
+	/* These mappings tend to generate huge volumes of
+	 * uninteresting debug data, which basically makes it
+	 * impossible to use debugging otherwise.
+	 */
+	DBG_DISABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
+
 	while ( len ) {
 		memset ( &mapping, 0, sizeof ( mapping ) );
 		MLX_FILL_1 ( &mapping, 0, va_h, ( va >> 32 ) );
@@ -1684,6 +2037,7 @@ static int hermon_map_vpm ( struct hermon *hermon,
 			     log2size, 0,
 			     pa_l, ( pa >> 12 ) );
 		if ( ( rc = map ( hermon, &mapping ) ) != 0 ) {
+			DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
 			DBGC ( hermon, "Hermon %p could not map %llx => %lx: "
 			       "%s\n", hermon, va, pa, strerror ( rc ) );
 			return rc;
@@ -1693,6 +2047,7 @@ static int hermon_map_vpm ( struct hermon *hermon,
 		len -= HERMON_PAGE_SIZE;
 	}
 
+	DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
 	return 0;
 }
 
@@ -1821,6 +2176,15 @@ static int hermon_get_cap ( struct hermon *hermon ) {
 		( 1 << MLX_GET ( &dev_cap, log2_rsvd_mrws ) );
 	hermon->cap.dmpt_entry_size = MLX_GET ( &dev_cap, d_mpt_entry_sz );
 	hermon->cap.reserved_uars = MLX_GET ( &dev_cap, num_rsvd_uars );
+	hermon->cap.num_ports = MLX_GET ( &dev_cap, num_ports );
+	hermon->cap.dpdp = MLX_GET ( &dev_cap, dpdp );
+
+	/* Sanity check */
+	if ( hermon->cap.num_ports > HERMON_MAX_PORTS ) {
+		DBGC ( hermon, "Hermon %p has %d ports (only %d supported)\n",
+		       hermon, hermon->cap.num_ports, HERMON_MAX_PORTS );
+		hermon->cap.num_ports = HERMON_MAX_PORTS;
+	}
 
 	return 0;
 }
@@ -1868,7 +2232,8 @@ static int hermon_alloc_icm ( struct hermon *hermon,
 	 */
 
 	/* Calculate number of each object type within ICM */
-	log_num_qps = fls ( hermon->cap.reserved_qps + HERMON_MAX_QPS - 1 );
+	log_num_qps = fls ( hermon->cap.reserved_qps +
+			    HERMON_RSVD_SPECIAL_QPS + HERMON_MAX_QPS - 1 );
 	log_num_srqs = fls ( hermon->cap.reserved_srqs - 1 );
 	log_num_cqs = fls ( hermon->cap.reserved_cqs + HERMON_MAX_CQS - 1 );
 	log_num_eqs = fls ( hermon->cap.reserved_eqs + HERMON_MAX_EQS - 1 );
@@ -2130,17 +2495,21 @@ static int hermon_setup_mpt ( struct hermon *hermon ) {
 
 	/* Derive key */
 	key = ( hermon->cap.reserved_mrws | HERMON_MKEY_PREFIX );
-	hermon->reserved_lkey = ( ( key << 8 ) | ( key >> 24 ) );
+	hermon->lkey = ( ( key << 8 ) | ( key >> 24 ) );
 
 	/* Initialise memory protection table */
 	memset ( &mpt, 0, sizeof ( mpt ) );
-	MLX_FILL_4 ( &mpt, 0,
-		     r_w, 1,
-		     pa, 1,
+	MLX_FILL_7 ( &mpt, 0,
+		     atomic, 1,
+		     rw, 1,
+		     rr, 1,
+		     lw, 1,
 		     lr, 1,
-		     lw, 1 );
+		     pa, 1,
+		     r_w, 1 );
 	MLX_FILL_1 ( &mpt, 2, mem_key, key );
-	MLX_FILL_1 ( &mpt, 3, pd, HERMON_GLOBAL_PD );
+	MLX_FILL_1 ( &mpt, 3,
+		     pd, HERMON_GLOBAL_PD );
 	MLX_FILL_1 ( &mpt, 10, len64, 1 );
 	if ( ( rc = hermon_cmd_sw2hw_mpt ( hermon,
 					   hermon->cap.reserved_mrws,
@@ -2154,6 +2523,54 @@ static int hermon_setup_mpt ( struct hermon *hermon ) {
 }
 
 /**
+ * Configure special queue pairs
+ *
+ * @v hermon		Hermon device
+ * @ret rc		Return status code
+ */
+static int hermon_configure_special_qps ( struct hermon *hermon ) {
+	int rc;
+
+	/* Special QP block must be aligned on its own size */
+	hermon->special_qpn_base = ( ( hermon->cap.reserved_qps +
+				       HERMON_NUM_SPECIAL_QPS - 1 )
+				     & ~( HERMON_NUM_SPECIAL_QPS - 1 ) );
+	hermon->qpn_base = ( hermon->special_qpn_base +
+			     HERMON_NUM_SPECIAL_QPS );
+	DBGC ( hermon, "Hermon %p special QPs at [%lx,%lx]\n", hermon,
+	       hermon->special_qpn_base, ( hermon->qpn_base - 1 ) );
+
+	/* Issue command to configure special QPs */
+	if ( ( rc = hermon_cmd_conf_special_qp ( hermon, 0x00,
+					  hermon->special_qpn_base ) ) != 0 ) {
+		DBGC ( hermon, "Hermon %p could not configure special QPs: "
+		       "%s\n", hermon, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * Reset device
+ *
+ * @v hermon		Hermon device
+ * @v pci		PCI device
+ */
+static void hermon_reset ( struct hermon *hermon,
+			   struct pci_device *pci ) {
+	struct pci_config_backup backup;
+	static const uint8_t backup_exclude[] =
+		PCI_CONFIG_BACKUP_EXCLUDE ( 0x58, 0x5c );
+
+	pci_backup ( pci, &backup, backup_exclude );
+	writel ( HERMON_RESET_MAGIC,
+		 ( hermon->config + HERMON_RESET_OFFSET ) );
+	mdelay ( HERMON_RESET_WAIT_TIME_MS );
+	pci_restore ( pci, &backup, backup_exclude );
+}
+
+/**
  * Probe PCI device
  *
  * @v pci		PCI device
@@ -2165,7 +2582,7 @@ static int hermon_probe ( struct pci_device *pci,
 	struct hermon *hermon;
 	struct ib_device *ibdev;
 	struct hermonprm_init_hca init_hca;
-	int i;
+	unsigned int i;
 	int rc;
 
 	/* Allocate Hermon device */
@@ -2176,20 +2593,6 @@ static int hermon_probe ( struct pci_device *pci,
 	}
 	pci_set_drvdata ( pci, hermon );
 
-	/* Allocate Infiniband devices */
-	for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) {
-	        ibdev = alloc_ibdev ( 0 );
-		if ( ! ibdev ) {
-			rc = -ENOMEM;
-			goto err_alloc_ibdev;
-		}
-		hermon->ibdev[i] = ibdev;
-		ibdev->op = &hermon_ib_operations;
-		ibdev->dev = &pci->dev;
-		ibdev->port = ( HERMON_PORT_BASE + i );
-		ib_set_drvdata ( ibdev, hermon );
-	}
-
 	/* Fix up PCI device */
 	adjust_pci_device ( pci );
 
@@ -2199,6 +2602,9 @@ static int hermon_probe ( struct pci_device *pci,
 	hermon->uar = ioremap ( pci_bar_start ( pci, HERMON_PCI_UAR_BAR ),
 				HERMON_UAR_NON_EQ_PAGE * HERMON_PAGE_SIZE );
 
+	/* Reset device */
+	hermon_reset ( hermon, pci );
+
 	/* Allocate space for mailboxes */
 	hermon->mailbox_in = malloc_dma ( HERMON_MBOX_SIZE,
 					  HERMON_MBOX_ALIGN );
@@ -2221,6 +2627,20 @@ static int hermon_probe ( struct pci_device *pci,
 	if ( ( rc = hermon_get_cap ( hermon ) ) != 0 )
 		goto err_get_cap;
 
+	/* Allocate Infiniband devices */
+	for ( i = 0 ; i < hermon->cap.num_ports ; i++ ) {
+	        ibdev = alloc_ibdev ( 0 );
+		if ( ! ibdev ) {
+			rc = -ENOMEM;
+			goto err_alloc_ibdev;
+		}
+		hermon->ibdev[i] = ibdev;
+		ibdev->op = &hermon_ib_operations;
+		ibdev->dev = &pci->dev;
+		ibdev->port = ( HERMON_PORT_BASE + i );
+		ib_set_drvdata ( ibdev, hermon );
+	}
+
 	/* Allocate ICM */
 	memset ( &init_hca, 0, sizeof ( init_hca ) );
 	if ( ( rc = hermon_alloc_icm ( hermon, &init_hca ) ) != 0 )
@@ -2239,17 +2659,24 @@ static int hermon_probe ( struct pci_device *pci,
 	/* Set up memory protection */
 	if ( ( rc = hermon_setup_mpt ( hermon ) ) != 0 )
 		goto err_setup_mpt;
+	for ( i = 0 ; i < hermon->cap.num_ports ; i++ )
+		hermon->ibdev[i]->rdma_key = hermon->lkey;
 
 	/* Set up event queue */
 	if ( ( rc = hermon_create_eq ( hermon ) ) != 0 )
 		goto err_create_eq;
 
-	/* Update MAD parameters */
-	for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ )
+	/* Configure special QPs */
+	if ( ( rc = hermon_configure_special_qps ( hermon ) ) != 0 )
+		goto err_conf_special_qps;
+
+	/* Update IPoIB MAC address */
+	for ( i = 0 ; i < hermon->cap.num_ports ; i++ ) {
 		ib_smc_update ( hermon->ibdev[i], hermon_mad );
+	}
 
 	/* Register Infiniband devices */
-	for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) {
+	for ( i = 0 ; i < hermon->cap.num_ports ; i++ ) {
 		if ( ( rc = register_ibdev ( hermon->ibdev[i] ) ) != 0 ) {
 			DBGC ( hermon, "Hermon %p could not register IB "
 			       "device: %s\n", hermon, strerror ( rc ) );
@@ -2259,10 +2686,11 @@ static int hermon_probe ( struct pci_device *pci,
 
 	return 0;
 
-	i = HERMON_NUM_PORTS;
+	i = hermon->cap.num_ports;
  err_register_ibdev:
-	for ( i-- ; i >= 0 ; i-- )
+	for ( i-- ; ( signed int ) i >= 0 ; i-- )
 		unregister_ibdev ( hermon->ibdev[i] );
+ err_conf_special_qps:
 	hermon_destroy_eq ( hermon );
  err_create_eq:
  err_setup_mpt:
@@ -2270,6 +2698,10 @@ static int hermon_probe ( struct pci_device *pci,
  err_init_hca:
 	hermon_free_icm ( hermon );
  err_alloc_icm:
+	i = hermon->cap.num_ports;
+ err_alloc_ibdev:
+	for ( i-- ; ( signed int ) i >= 0 ; i-- )
+		ibdev_put ( hermon->ibdev[i] );
  err_get_cap:
 	hermon_stop_firmware ( hermon );
  err_start_firmware:
@@ -2277,10 +2709,6 @@ static int hermon_probe ( struct pci_device *pci,
  err_mailbox_out:
 	free_dma ( hermon->mailbox_in, HERMON_MBOX_SIZE );
  err_mailbox_in:
-	i = HERMON_NUM_PORTS;
- err_alloc_ibdev:
-	for ( i-- ; i >= 0 ; i-- )
-		ibdev_put ( hermon->ibdev[i] );
 	free ( hermon );
  err_alloc_hermon:
 	return rc;
@@ -2295,7 +2723,7 @@ static void hermon_remove ( struct pci_device *pci ) {
 	struct hermon *hermon = pci_get_drvdata ( pci );
 	int i;
 
-	for ( i = ( HERMON_NUM_PORTS - 1 ) ; i >= 0 ; i-- )
+	for ( i = ( hermon->cap.num_ports - 1 ) ; i >= 0 ; i-- )
 		unregister_ibdev ( hermon->ibdev[i] );
 	hermon_destroy_eq ( hermon );
 	hermon_cmd_close_hca ( hermon );
@@ -2304,16 +2732,16 @@ static void hermon_remove ( struct pci_device *pci ) {
 	hermon_stop_firmware ( hermon );
 	free_dma ( hermon->mailbox_out, HERMON_MBOX_SIZE );
 	free_dma ( hermon->mailbox_in, HERMON_MBOX_SIZE );
-	for ( i = ( HERMON_NUM_PORTS - 1 ) ; i >= 0 ; i-- )
+	for ( i = ( hermon->cap.num_ports - 1 ) ; i >= 0 ; i-- )
 		ibdev_put ( hermon->ibdev[i] );
 	free ( hermon );
 }
 
 static struct pci_device_id hermon_nics[] = {
-	PCI_ROM ( 0x15b3, 0x6340, "mt25408", "MT25408 HCA driver" ),
-	PCI_ROM ( 0x15b3, 0x634a, "mt25418", "MT25418 HCA driver" ),
-	PCI_ROM ( 0x15b3, 0x6732, "mt26418", "MT26418 HCA driver" ),
-	PCI_ROM ( 0x15b3, 0x673c, "mt26428", "MT26428 HCA driver" ),
+	PCI_ROM ( 0x15b3, 0x6340, "mt25408", "MT25408 HCA driver", 0 ),
+	PCI_ROM ( 0x15b3, 0x634a, "mt25418", "MT25418 HCA driver", 0 ),
+	PCI_ROM ( 0x15b3, 0x6732, "mt26418", "MT26418 HCA driver", 0 ),
+	PCI_ROM ( 0x15b3, 0x673c, "mt26428", "MT26428 HCA driver", 0 ),
 };
 
 struct pci_driver hermon_driver __pci_driver = {
diff --git a/gpxe/src/drivers/infiniband/hermon.h b/gpxe/src/drivers/infiniband/hermon.h
index ed39da6..c53f3da 100644
--- a/gpxe/src/drivers/infiniband/hermon.h
+++ b/gpxe/src/drivers/infiniband/hermon.h
@@ -7,8 +7,11 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/uaccess.h>
+#include <gpxe/ib_packet.h>
 #include "mlx_bitops.h"
 #include "MT25408_PRM.h"
 
@@ -18,7 +21,7 @@
  */
 
 /* Ports in existence */
-#define HERMON_NUM_PORTS		2
+#define HERMON_MAX_PORTS		2
 #define HERMON_PORT_BASE		1
 
 /* PCI BARs */
@@ -26,7 +29,13 @@
 #define HERMON_PCI_CONFIG_BAR_SIZE	0x100000
 #define HERMON_PCI_UAR_BAR		PCI_BASE_ADDRESS_2
 
+/* Device reset */
+#define HERMON_RESET_OFFSET		0x0f0010
+#define HERMON_RESET_MAGIC		0x01000000UL
+#define HERMON_RESET_WAIT_TIME_MS	1000
+
 /* Work queue entry and completion queue entry opcodes */
+#define HERMON_OPCODE_NOP		0x00
 #define HERMON_OPCODE_SEND		0x0a
 #define HERMON_OPCODE_RECV_ERROR	0xfe
 #define HERMON_OPCODE_SEND_ERROR	0xff
@@ -51,10 +60,13 @@
 #define HERMON_HCR_RTR2RTS_QP		0x001b
 #define HERMON_HCR_RTS2RTS_QP		0x001c
 #define HERMON_HCR_2RST_QP		0x0021
+#define HERMON_HCR_QUERY_QP		0x0022
+#define HERMON_HCR_CONF_SPECIAL_QP	0x0023
 #define HERMON_HCR_MAD_IFC		0x0024
 #define HERMON_HCR_READ_MCG		0x0025
 #define HERMON_HCR_WRITE_MCG		0x0026
 #define HERMON_HCR_MGID_HASH		0x0027
+#define HERMON_HCR_SENSE_PORT		0x004d
 #define HERMON_HCR_RUN_FW		0x0ff6
 #define HERMON_HCR_DISABLE_LAM		0x0ff7
 #define HERMON_HCR_ENABLE_LAM		0x0ff8
@@ -67,7 +79,9 @@
 #define HERMON_HCR_MAP_FA		0x0fff
 
 /* Service types */
+#define HERMON_ST_RC			0x00
 #define HERMON_ST_UD			0x03
+#define HERMON_ST_MLX			0x07
 
 /* MTUs */
 #define HERMON_MTU_2048			0x04
@@ -80,13 +94,24 @@
 #define HERMON_DB_EQ_OFFSET(_eqn)	\
 	( 0x800 + HERMON_PAGE_SIZE * ( (_eqn) / 4 ) + 0x08 * ( (_eqn) % 4 ) )
 
+#define HERMON_QP_OPT_PARAM_PM_STATE	0x00000400UL
 #define HERMON_QP_OPT_PARAM_QKEY	0x00000020UL
+#define HERMON_QP_OPT_PARAM_ALT_PATH	0x00000001UL
 
 #define HERMON_MAP_EQ			( 0UL << 31 )
 #define HERMON_UNMAP_EQ			( 1UL << 31 )
 
 #define HERMON_EV_PORT_STATE_CHANGE	0x09
 
+#define HERMON_SCHED_QP0		0x3f
+#define HERMON_SCHED_DEFAULT		0x83
+
+#define HERMON_PM_STATE_ARMED		0x00
+#define HERMON_PM_STATE_REARM		0x01
+#define HERMON_PM_STATE_MIGRATED	0x03
+
+#define HERMON_RETRY_MAX		0x07
+
 /*
  * Datatypes that seem to be missing from the autogenerated documentation
  *
@@ -145,6 +170,14 @@ struct hermonprm_port_state_change_event_st {
 	struct hermonprm_port_state_change_st data;
 } __attribute__ (( packed ));
 
+/** Hermon sense port */
+struct hermonprm_sense_port_st {
+	pseudo_bit_t port_type[0x00020];
+/* -------------- */
+	pseudo_bit_t reserved[0x00020];
+};
+#define HERMON_PORT_TYPE_IB		1
+
 /*
  * Wrapper structures for hardware datatypes
  *
@@ -173,9 +206,11 @@ struct MLX_DECLARE_STRUCT ( hermonprm_query_dev_cap );
 struct MLX_DECLARE_STRUCT ( hermonprm_query_fw );
 struct MLX_DECLARE_STRUCT ( hermonprm_queue_pair_ee_context_entry );
 struct MLX_DECLARE_STRUCT ( hermonprm_scalar_parameter );
+struct MLX_DECLARE_STRUCT ( hermonprm_sense_port );
 struct MLX_DECLARE_STRUCT ( hermonprm_send_db_register );
 struct MLX_DECLARE_STRUCT ( hermonprm_ud_address_vector );
 struct MLX_DECLARE_STRUCT ( hermonprm_virtual_physical_mapping );
+struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ctrl_mlx );
 struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ctrl_send );
 struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_data_ptr );
 struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ud );
@@ -191,7 +226,7 @@ struct hermonprm_write_mtt {
 	struct hermonprm_mtt mtt;
 } __attribute__ (( packed ));
 
-#define HERMON_MAX_GATHER 1
+#define HERMON_MAX_GATHER 2
 
 struct hermonprm_ud_send_wqe {
 	struct hermonprm_wqe_segment_ctrl_send ctrl;
@@ -199,6 +234,17 @@ struct hermonprm_ud_send_wqe {
 	struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER];
 } __attribute__ (( packed ));
 
+struct hermonprm_mlx_send_wqe {
+	struct hermonprm_wqe_segment_ctrl_mlx ctrl;
+	struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER];
+	uint8_t headers[IB_MAX_HEADER_SIZE];
+} __attribute__ (( packed ));
+
+struct hermonprm_rc_send_wqe {
+	struct hermonprm_wqe_segment_ctrl_send ctrl;
+	struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER];
+} __attribute__ (( packed ));
+
 #define HERMON_MAX_SCATTER 1
 
 struct hermonprm_recv_wqe {
@@ -265,6 +311,10 @@ struct hermon_dev_cap {
 	size_t dmpt_entry_size;
 	/** Number of reserved UARs */
 	unsigned int reserved_uars;
+	/** Number of ports */
+	unsigned int num_ports;
+	/** Dual-port different protocol */
+	int dpdp;
 };
 
 /** Number of cMPT entries of each type */
@@ -318,7 +368,10 @@ struct hermon_mtt {
 
 /** A Hermon send work queue entry */
 union hermon_send_wqe {
+	struct hermonprm_wqe_segment_ctrl_send ctrl;
 	struct hermonprm_ud_send_wqe ud;
+	struct hermonprm_mlx_send_wqe mlx;
+	struct hermonprm_rc_send_wqe rc;
 	uint8_t force_align[HERMON_SEND_WQE_ALIGN];
 } __attribute__ (( packed ));
 
@@ -358,14 +411,32 @@ struct hermon_recv_work_queue {
 	struct hermonprm_qp_db_record doorbell __attribute__ (( aligned (4) ));
 };
 
+/** Number of special queue pairs */
+#define HERMON_NUM_SPECIAL_QPS 8
+
+/** Number of queue pairs reserved for the "special QP" block
+ *
+ * The special QPs must be within a contiguous block aligned on its
+ * own size.
+ */
+#define HERMON_RSVD_SPECIAL_QPS	( ( HERMON_NUM_SPECIAL_QPS << 1 ) - 1 )
+
 /** Maximum number of allocatable queue pairs
  *
  * This is a policy decision, not a device limit.
  */
 #define HERMON_MAX_QPS		8
 
-/** Base queue pair number */
-#define HERMON_QPN_BASE 0x550000
+/** Queue pair number randomisation mask */
+#define HERMON_QPN_RANDOM_MASK 0xfff000
+
+/** Hermon queue pair state */
+enum hermon_queue_pair_state {
+	HERMON_QP_ST_RST = 0,
+	HERMON_QP_ST_INIT,
+	HERMON_QP_ST_RTR,
+	HERMON_QP_ST_RTS,
+};
 
 /** A Hermon queue pair */
 struct hermon_queue_pair {
@@ -379,6 +450,8 @@ struct hermon_queue_pair {
 	struct hermon_send_work_queue send;
 	/** Receive work queue */
 	struct hermon_recv_work_queue recv;
+	/** Queue state */
+	enum hermon_queue_pair_state state;
 };
 
 /** Maximum number of allocatable completion queues
@@ -458,11 +531,11 @@ struct hermon {
 
 	/** Event queue */
 	struct hermon_event_queue eq;
-	/** Reserved LKey
+	/** Unrestricted LKey
 	 *
 	 * Used to get unrestricted memory access.
 	 */
-	unsigned long reserved_lkey;
+	unsigned long lkey;
 
 	/** Completion queue in-use bitmask */
 	hermon_bitmask_t cq_inuse[ HERMON_BITMASK_SIZE ( HERMON_MAX_CQS ) ];
@@ -473,9 +546,13 @@ struct hermon {
 
 	/** Device capabilities */
 	struct hermon_dev_cap cap;
+	/** Special QPN base */
+	unsigned long special_qpn_base;
+	/** QPN base */
+	unsigned long qpn_base;
 
 	/** Infiniband devices */
-	struct ib_device *ibdev[HERMON_NUM_PORTS];
+	struct ib_device *ibdev[HERMON_MAX_PORTS];
 };
 
 /** Global protection domain */
diff --git a/gpxe/src/drivers/infiniband/ib_packet.c b/gpxe/src/drivers/infiniband/ib_packet.c
deleted file mode 100644
index 0f21617..0000000
--- a/gpxe/src/drivers/infiniband/ib_packet.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2008 Michael Brown <mbrown at fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_packet.h>
-
-/**
- * @file
- *
- * Infiniband Packet Formats
- *
- */
-
-/**
- * Add IB headers
- *
- * @v ibdev		Infiniband device
- * @v iobuf		I/O buffer to contain headers
- * @v qp		Queue pair
- * @v payload_len	Payload length
- * @v av		Address vector
- */
-int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf,
-	      struct ib_queue_pair *qp, size_t payload_len,
-	      const struct ib_address_vector *av ) {
-	struct ib_local_route_header *lrh;
-	struct ib_global_route_header *grh;
-	struct ib_base_transport_header *bth;
-	struct ib_datagram_extended_transport_header *deth;
-	size_t orig_iob_len = iob_len ( iobuf );
-	size_t pad_len;
-	size_t lrh_len;
-	size_t grh_len;
-	unsigned int vl;
-	unsigned int lnh;
-
-	DBGC2 ( ibdev, "IBDEV %p TX %04x:%08lx => %04x:%08lx (key %08lx)\n",
-		ibdev, ibdev->lid, qp->qpn, av->lid, av->qpn, av->qkey );
-
-	/* Calculate packet length */
-	pad_len = ( (-payload_len) & 0x3 );
-	payload_len += pad_len;
-	payload_len += 4; /* ICRC */
-
-	/* Reserve space for headers */
-	orig_iob_len = iob_len ( iobuf );
-	deth = iob_push ( iobuf, sizeof ( *deth ) );
-	bth = iob_push ( iobuf, sizeof ( *bth ) );
-	grh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len );
-	grh = ( av->gid_present ?
-		iob_push ( iobuf, sizeof ( *grh ) ) : NULL );
-	lrh = iob_push ( iobuf, sizeof ( *lrh ) );
-	lrh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len );
-
-	/* Construct LRH */
-	vl = ( ( av->qpn == IB_QPN_SMP ) ? IB_VL_SMP : IB_VL_DEFAULT );
-	lrh->vl__lver = ( vl << 4 );
-	lnh = ( grh ? IB_LNH_GRH : IB_LNH_BTH );
-	lrh->sl__lnh = ( ( av->sl << 4 ) | lnh );
-	lrh->dlid = htons ( av->lid );
-	lrh->length = htons ( lrh_len >> 2 );
-	lrh->slid = htons ( ibdev->lid );
-
-	/* Construct GRH, if required */
-	if ( grh ) {
-		grh->ipver__tclass__flowlabel =
-			htonl ( IB_GRH_IPVER_IPv6 << 28 );
-		grh->paylen = htons ( grh_len );
-		grh->nxthdr = IB_GRH_NXTHDR_IBA;
-		grh->hoplmt = 0;
-		memcpy ( &grh->sgid, &ibdev->gid, sizeof ( grh->sgid ) );
-		memcpy ( &grh->dgid, &av->gid, sizeof ( grh->dgid ) );
-	}
-
-	/* Construct BTH */
-	bth->opcode = BTH_OPCODE_UD_SEND;
-	bth->se__m__padcnt__tver = ( pad_len << 4 );
-	bth->pkey = htons ( ibdev->pkey );
-	bth->dest_qp = htonl ( av->qpn );
-	bth->ack__psn = htonl ( ( ibdev->psn++ ) & 0xffffffUL );
-
-	/* Construct DETH */
-	deth->qkey = htonl ( av->qkey );
-	deth->src_qp = htonl ( qp->qpn );
-
-	DBGCP_HDA ( ibdev, 0, iobuf->data,
-		    ( iob_len ( iobuf ) - orig_iob_len ) );
-
-	return 0;
-}
-
-/**
- * Remove IB headers
- *
- * @v ibdev		Infiniband device
- * @v iobuf		I/O buffer containing headers
- * @v qp		Queue pair to fill in, or NULL
- * @v payload_len	Payload length to fill in, or NULL
- * @v av		Address vector to fill in
- */
-int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf,
-	      struct ib_queue_pair **qp, size_t *payload_len,
-	      struct ib_address_vector *av ) {
-	struct ib_local_route_header *lrh;
-	struct ib_global_route_header *grh;
-	struct ib_base_transport_header *bth;
-	struct ib_datagram_extended_transport_header *deth;
-	size_t orig_iob_len = iob_len ( iobuf );
-	unsigned int lnh;
-	size_t pad_len;
-	unsigned long qpn;
-	unsigned int lid;
-
-	/* Clear return values */
-	if ( qp )
-		*qp = NULL;
-	if ( payload_len )
-		*payload_len = 0;
-	memset ( av, 0, sizeof ( *av ) );
-
-	/* Extract LRH */
-	if ( iob_len ( iobuf ) < sizeof ( *lrh ) ) {
-		DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for LRH\n",
-		       ibdev, iob_len ( iobuf ) );
-		return -EINVAL;
-	}
-	lrh = iobuf->data;
-	iob_pull ( iobuf, sizeof ( *lrh ) );
-	av->lid = ntohs ( lrh->slid );
-	av->sl = ( lrh->sl__lnh >> 4 );
-	lnh = ( lrh->sl__lnh & 0x3 );
-	lid = ntohs ( lrh->dlid );
-
-	/* Reject unsupported packets */
-	if ( ! ( ( lnh == IB_LNH_BTH ) || ( lnh == IB_LNH_GRH ) ) ) {
-		DBGC ( ibdev, "IBDEV %p RX unsupported LNH %x\n",
-		       ibdev, lnh );
-		return -ENOTSUP;
-	}
-
-	/* Extract GRH, if present */
-	if ( lnh == IB_LNH_GRH ) {
-		if ( iob_len ( iobuf ) < sizeof ( *grh ) ) {
-			DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) "
-			       "for GRH\n", ibdev, iob_len ( iobuf ) );
-			return -EINVAL;
-		}
-		grh = iobuf->data;
-		iob_pull ( iobuf, sizeof ( *grh ) );
-		av->gid_present = 1;
-		memcpy ( &av->gid, &grh->sgid, sizeof ( av->gid ) );
-	} else {
-		grh = NULL;
-	}
-
-	/* Extract BTH */
-	if ( iob_len ( iobuf ) < sizeof ( *bth ) ) {
-		DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for BTH\n",
-		       ibdev, iob_len ( iobuf ) );
-		return -EINVAL;
-	}
-	bth = iobuf->data;
-	iob_pull ( iobuf, sizeof ( *bth ) );
-	if ( bth->opcode != BTH_OPCODE_UD_SEND ) {
-		DBGC ( ibdev, "IBDEV %p unsupported BTH opcode %x\n",
-		       ibdev, bth->opcode );
-		return -ENOTSUP;
-	}
-	qpn = ntohl ( bth->dest_qp );
-
-	/* Extract DETH */
-	if ( iob_len ( iobuf ) < sizeof ( *deth ) ) {
-		DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for DETH\n",
-		       ibdev, iob_len ( iobuf ) );
-		return -EINVAL;
-	}
-	deth = iobuf->data;
-	iob_pull ( iobuf, sizeof ( *deth ) );
-	av->qpn = ntohl ( deth->src_qp );
-	av->qkey = ntohl ( deth->qkey );
-
-	/* Calculate payload length, if applicable */
-	if ( payload_len ) {
-		pad_len = ( ( bth->se__m__padcnt__tver >> 4 ) & 0x3 );
-		*payload_len = ( ( ntohs ( lrh->length ) << 2 )
-				 - ( orig_iob_len - iob_len ( iobuf ) )
-				 - pad_len - 4 /* ICRC */ );
-	}
-
-	/* Determine destination QP, if applicable */
-	if ( qp ) {
-		if ( IB_LID_MULTICAST ( lid ) && grh ) {
-			*qp = ib_find_qp_mgid ( ibdev, &grh->dgid );
-		} else {
-			*qp = ib_find_qp_qpn ( ibdev, qpn );
-		}
-		if ( ! *qp ) {
-			DBGC ( ibdev, "IBDEV %p RX for nonexistent QP\n",
-			       ibdev );
-			return -ENODEV;
-		}
-	}
-
-	DBGC2 ( ibdev, "IBDEV %p RX %04x:%08lx <= %04x:%08lx (key %08x)\n",
-		ibdev, lid,
-		( IB_LID_MULTICAST( lid ) ? ( qp ? (*qp)->qpn : -1UL ) : qpn ),
-		av->lid, av->qpn, ntohl ( deth->qkey ) );
-	DBGCP_HDA ( ibdev, 0,
-		    ( iobuf->data - ( orig_iob_len - iob_len ( iobuf ) ) ),
-		    ( orig_iob_len - iob_len ( iobuf ) ) );
-
-	return 0;
-}
diff --git a/gpxe/src/drivers/infiniband/ib_sma.c b/gpxe/src/drivers/infiniband/ib_sma.c
deleted file mode 100644
index 2bd3a9e..0000000
--- a/gpxe/src/drivers/infiniband/ib_sma.c
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * Copyright (C) 2008 Michael Brown <mbrown at fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <byteswap.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/process.h>
-#include <gpxe/ib_sma.h>
-
-/**
- * @file
- *
- * Infiniband Subnet Management Agent
- *
- */
-
-/**
- * Get node information
- *
- * @v sma		Subnet management agent
- * @v get		Attribute to get
- */
-static void ib_sma_get_node_info ( struct ib_sma *sma,
-				   union ib_smp_data *get ) {
-	struct ib_device *ibdev = sma->ibdev;
-	struct ib_node_info *node_info = &get->node_info;
-	struct ib_device *tmp;
-
-	memset ( node_info, 0, sizeof ( *node_info ) );
-	node_info->base_version = IB_MGMT_BASE_VERSION;
-	node_info->class_version = IB_SMP_CLASS_VERSION;
-	node_info->node_type = IB_NODE_TYPE_HCA;
-	/* Search for IB devices with the same physical device to
-	 * identify port count and a suitable Node GUID.
-	 */
-	for_each_ibdev ( tmp ) {
-		if ( tmp->dev != ibdev->dev )
-			continue;
-		if ( node_info->num_ports == 0 ) {
-			memcpy ( node_info->sys_guid, &tmp->gid.u.half[1],
-				 sizeof ( node_info->sys_guid ) );
-			memcpy ( node_info->node_guid, &tmp->gid.u.half[1],
-				 sizeof ( node_info->node_guid ) );
-		}
-		node_info->num_ports++;
-	}
-	memcpy ( node_info->port_guid, &ibdev->gid.u.half[1],
-		 sizeof ( node_info->port_guid ) );
-	node_info->partition_cap = htons ( 1 );
-	node_info->local_port_num = ibdev->port;
-}
-
-/**
- * Get node description
- *
- * @v sma		Subnet management agent
- * @v get		Attribute to get
- */
-static void ib_sma_get_node_desc ( struct ib_sma *sma,
-				   union ib_smp_data *get ) {
-	struct ib_device *ibdev = sma->ibdev;
-	struct ib_node_desc *node_desc = &get->node_desc;
-	struct ib_gid_half *guid = &ibdev->gid.u.half[1];
-
-	memset ( node_desc, 0, sizeof ( *node_desc ) );
-	snprintf ( node_desc->node_string, sizeof ( node_desc->node_string ),
-		   "gPXE %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)",
-		   guid->bytes[0], guid->bytes[1], guid->bytes[2],
-		   guid->bytes[3], guid->bytes[4], guid->bytes[5],
-		   guid->bytes[6], guid->bytes[7], ibdev->dev->name );
-}
-
-/**
- * Get GUID information
- *
- * @v sma		Subnet management agent
- * @v get		Attribute to get
- */
-static void ib_sma_get_guid_info ( struct ib_sma *sma,
-				   union ib_smp_data *get ) {
-	struct ib_device *ibdev = sma->ibdev;
-	struct ib_guid_info *guid_info = &get->guid_info;
-
-	memset ( guid_info, 0, sizeof ( *guid_info ) );
-	memcpy ( guid_info->guid[0], &ibdev->gid.u.half[1],
-		 sizeof ( guid_info->guid[0] ) );
-}
-
-/**
- * Get port information
- *
- * @v sma		Subnet management agent
- * @v get		Attribute to get
- */
-static void ib_sma_get_port_info ( struct ib_sma *sma,
-				   union ib_smp_data *get ) {
-	struct ib_device *ibdev = sma->ibdev;
-	struct ib_port_info *port_info = &get->port_info;
-
-	memset ( port_info, 0, sizeof ( *port_info ) );
-	memcpy ( port_info->gid_prefix, &ibdev->gid.u.half[0],
-		 sizeof ( port_info->gid_prefix ) );
-	port_info->lid = ntohs ( ibdev->lid );
-	port_info->mastersm_lid = ntohs ( ibdev->sm_lid );
-	port_info->local_port_num = ibdev->port;
-	port_info->link_width_enabled = ibdev->link_width;
-	port_info->link_width_supported = ibdev->link_width;
-	port_info->link_width_active = ibdev->link_width;
-	port_info->link_speed_supported__port_state =
-		( ( ibdev->link_speed << 4 ) | ibdev->port_state );
-	port_info->port_phys_state__link_down_def_state =
-		( ( IB_PORT_PHYS_STATE_POLLING << 4 ) |
-		  IB_PORT_PHYS_STATE_POLLING );
-	port_info->link_speed_active__link_speed_enabled =
-		( ( ibdev->link_speed << 4 ) | ibdev->link_speed );
-	port_info->neighbour_mtu__mastersm_sl =
-		( ( IB_MTU_2048 << 4 ) | ibdev->sm_sl );
-	port_info->vl_cap__init_type = ( IB_VL_0 << 4 );
-	port_info->init_type_reply__mtu_cap = IB_MTU_2048;
-	port_info->operational_vls__enforcement = ( IB_VL_0 << 4 );
-	port_info->guid_cap = 1;
-}
-
-/**
- * Set port information
- *
- * @v sma		Subnet management agent
- * @v set		Attribute to set
- * @ret rc		Return status code
- */
-static int ib_sma_set_port_info ( struct ib_sma *sma,
-				  const union ib_smp_data *set ) {
-	struct ib_device *ibdev = sma->ibdev;
-	const struct ib_port_info *port_info = &set->port_info;
-
-	memcpy ( &ibdev->gid.u.half[0], port_info->gid_prefix,
-		 sizeof ( ibdev->gid.u.half[0] ) );
-	ibdev->lid = ntohs ( port_info->lid );
-	ibdev->sm_lid = ntohs ( port_info->mastersm_lid );
-	ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf );
-
-	if ( ! sma->op->set_port_info ) {
-		/* Not an error; we just ignore all other settings */
-		return 0;
-	}
-
-	return sma->op->set_port_info ( ibdev, port_info );
-}
-
-/**
- * Get partition key table
- *
- * @v sma		Subnet management agent
- * @v get		Attribute to get
- */
-static void ib_sma_get_pkey_table ( struct ib_sma *sma,
-				    union ib_smp_data *get ) {
-	struct ib_device *ibdev = sma->ibdev;
-	struct ib_pkey_table *pkey_table = &get->pkey_table;
-
-	memset ( pkey_table, 0, sizeof ( *pkey_table ) );
-	pkey_table->pkey[0] = htons ( ibdev->pkey );
-}
-
-/**
- * Set partition key table
- *
- * @v sma		Subnet management agent
- * @v set		Attribute to set
- */
-static int ib_sma_set_pkey_table ( struct ib_sma *sma,
-				   const union ib_smp_data *get ) {
-	struct ib_device *ibdev = sma->ibdev;
-	const struct ib_pkey_table *pkey_table = &get->pkey_table;
-
-	ibdev->pkey = ntohs ( pkey_table->pkey[0] );
-	return 0;
-}
-
-/** An attribute handler */
-struct ib_sma_handler {
-	/** Attribute (in network byte order) */
-	uint16_t attr_id;
-	/** Get attribute
-	 *
-	 * @v sma	Subnet management agent
-	 * @v get	Attribute to get
-	 * @ret rc	Return status code
-	 */
-	void ( * get ) ( struct ib_sma *sma, union ib_smp_data *get );
-	/** Set attribute
-	 *
-	 * @v sma	Subnet management agent
-	 * @v set	Attribute to set
-	 * @ret rc	Return status code
-	 */
-	int ( * set ) ( struct ib_sma *sma, const union ib_smp_data *set );
-};
-
-/** List of attribute handlers */
-static struct ib_sma_handler ib_sma_handlers[] = {
-	{ htons ( IB_SMP_ATTR_NODE_DESC ),
-	  ib_sma_get_node_desc, NULL },
-	{ htons ( IB_SMP_ATTR_NODE_INFO ),
-	  ib_sma_get_node_info, NULL },
-	{ htons ( IB_SMP_ATTR_GUID_INFO ),
-	  ib_sma_get_guid_info, NULL },
-	{ htons ( IB_SMP_ATTR_PORT_INFO ),
-	  ib_sma_get_port_info, ib_sma_set_port_info },
-	{ htons ( IB_SMP_ATTR_PKEY_TABLE ),
-	  ib_sma_get_pkey_table, ib_sma_set_pkey_table },
-};
-
-/**
- * Identify attribute handler
- *
- * @v attr_id		Attribute ID (in network byte order)
- * @ret handler		Attribute handler (or NULL)
- */
-static struct ib_sma_handler * ib_sma_handler ( uint16_t attr_id ) {
-	struct ib_sma_handler *handler;
-	unsigned int i;
-
-	for ( i = 0 ; i < ( sizeof ( ib_sma_handlers ) /
-			    sizeof ( ib_sma_handlers[0] ) ) ; i++ ) {
-		handler = &ib_sma_handlers[i];
-		if ( handler->attr_id == attr_id )
-			return handler;
-	}
-
-	return NULL;
-}
-
-/**
- * Respond to management datagram
- *
- * @v sma		Subnet management agent
- * @v mad		Management datagram
- * @ret rc		Return status code
- */
-static int ib_sma_mad ( struct ib_sma *sma, union ib_mad *mad ) {
-	struct ib_device *ibdev = sma->ibdev;
-	struct ib_mad_hdr *hdr = &mad->hdr;
-	struct ib_mad_smp *smp = &mad->smp;
-	struct ib_sma_handler *handler = NULL;
-	unsigned int hop_pointer;
-	unsigned int hop_count;
-	int rc;
-
-	DBGC ( sma, "SMA %p received SMP with bv=%02x mc=%02x cv=%02x "
-	       "meth=%02x attr=%04x mod=%08x\n", sma, hdr->base_version,
-	       hdr->mgmt_class, hdr->class_version, hdr->method,
-	       ntohs ( hdr->attr_id ), ntohl ( hdr->attr_mod ) );
-	DBGC2_HDA ( sma, 0, mad, sizeof ( *mad ) );
-
-	/* Sanity checks */
-	if ( hdr->base_version != IB_MGMT_BASE_VERSION ) {
-		DBGC ( sma, "SMA %p unsupported base version %x\n",
-		       sma, hdr->base_version );
-		return -ENOTSUP;
-	}
-	if ( ( hdr->mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) &&
-	     ( hdr->mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED ) ) {
-		DBGC ( sma, "SMA %p unsupported management class %x\n",
-		       sma, hdr->mgmt_class );
-		return -ENOTSUP;
-	}
-	if ( hdr->class_version != IB_SMP_CLASS_VERSION ) {
-		DBGC ( sma, "SMA %p unsupported class version %x\n",
-		       sma, hdr->class_version );
-		return -ENOTSUP;
-	}
-	if ( ( hdr->method != IB_MGMT_METHOD_GET ) &&
-	     ( hdr->method != IB_MGMT_METHOD_SET ) ) {
-		DBGC ( sma, "SMA %p unsupported method %x\n",
-		       sma, hdr->method );
-		return -ENOTSUP;
-	}
-
-	/* Identify handler */
-	if ( ! ( handler = ib_sma_handler ( hdr->attr_id ) ) ) {
-		DBGC ( sma, "SMA %p unsupported attribute %x\n",
-		       sma, ntohs ( hdr->attr_id ) );
-		hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR );
-		goto respond_without_data;
-	}
-
-	/* Set attribute (if applicable) */
-	if ( hdr->method != IB_MGMT_METHOD_SET ) {
-		hdr->status = htons ( IB_MGMT_STATUS_OK );
-		goto respond;
-	}
-	if ( ! handler->set ) {
-		DBGC ( sma, "SMA %p attribute %x is unsettable\n",
-		       sma, ntohs ( hdr->attr_id ) );
-		hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR );
-		goto respond;
-	}
-	if ( ( rc = handler->set ( sma, &smp->smp_data ) ) != 0 ) {
-		DBGC ( sma, "SMA %p could not set attribute %x: %s\n",
-		       sma, ntohs ( hdr->attr_id ), strerror ( rc ) );
-		hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR );
-		goto respond;
-	}
-
-	hdr->status = htons ( IB_MGMT_STATUS_OK );
-
- respond:
-	/* Get attribute */
-	handler->get ( sma, &smp->smp_data );
-
- respond_without_data:
-
-	/* Set method to "Get Response" */
-	hdr->method = IB_MGMT_METHOD_GET_RESP;
-
-	/* Set response fields for directed route SMPs */
-	if ( hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) {
-		hdr->status |= htons ( IB_SMP_STATUS_D_INBOUND );
-		hop_pointer = smp->mad_hdr.class_specific.smp.hop_pointer;
-		hop_count = smp->mad_hdr.class_specific.smp.hop_count;
-		assert ( hop_count == hop_pointer );
-		if ( hop_pointer < ( sizeof ( smp->return_path.hops ) /
-				     sizeof ( smp->return_path.hops[0] ) ) ) {
-			smp->return_path.hops[hop_pointer] = ibdev->port;
-		} else {
-			DBGC ( sma, "SMA %p invalid hop pointer %d\n",
-			       sma, hop_pointer );
-			return -EINVAL;
-		}
-	}
-
-	DBGC ( sma, "SMA %p responding with status=%04x\n",
-	       sma, ntohs ( hdr->status ) );
-	DBGC2_HDA ( sma, 0, mad, sizeof ( *mad ) );
-
-	return 0;
-}
-
-/**
- * Refill SMA receive ring
- *
- * @v sma		Subnet management agent
- */
-static void ib_sma_refill_recv ( struct ib_sma *sma ) {
-	struct ib_device *ibdev = sma->ibdev;
-	struct io_buffer *iobuf;
-	int rc;
-
-	while ( sma->qp->recv.fill < IB_SMA_NUM_RECV_WQES ) {
-
-		/* Allocate I/O buffer */
-		iobuf = alloc_iob ( IB_SMA_PAYLOAD_LEN );
-		if ( ! iobuf ) {
-			/* Non-fatal; we will refill on next attempt */
-			return;
-		}
-
-		/* Post I/O buffer */
-		if ( ( rc = ib_post_recv ( ibdev, sma->qp, iobuf ) ) != 0 ) {
-			DBGC ( sma, "SMA %p could not refill: %s\n",
-			       sma, strerror ( rc ) );
-			free_iob ( iobuf );
-			/* Give up */
-			return;
-		}
-	}
-}
-
-/**
- * Complete SMA send
- *
- *
- * @v ibdev		Infiniband device
- * @v qp		Queue pair
- * @v iobuf		I/O buffer
- * @v rc		Completion status code
- */
-static void ib_sma_complete_send ( struct ib_device *ibdev __unused,
-				   struct ib_queue_pair *qp,
-				   struct io_buffer *iobuf, int rc ) {
-	struct ib_sma *sma = ib_qp_get_ownerdata ( qp );
-
-	if ( rc != 0 ) {
-		DBGC ( sma, "SMA %p send completion error: %s\n",
-		       sma, strerror ( rc ) );
-	}
-	free_iob ( iobuf );
-}
-
-/**
- * Complete SMA receive
- *
- *
- * @v ibdev		Infiniband device
- * @v qp		Queue pair
- * @v av		Address vector
- * @v iobuf		I/O buffer
- * @v rc		Completion status code
- */
-static void ib_sma_complete_recv ( struct ib_device *ibdev,
-				   struct ib_queue_pair *qp,
-				   struct ib_address_vector *av,
-				   struct io_buffer *iobuf, int rc ) {
-	struct ib_sma *sma = ib_qp_get_ownerdata ( qp );
-	union ib_mad *mad;
-
-	/* Ignore errors */
-	if ( rc != 0 ) {
-		DBGC ( sma, "SMA %p RX error: %s\n", sma, strerror ( rc ) );
-		goto err;
-	}
-
-	/* Sanity check */
-	if ( iob_len ( iobuf ) != sizeof ( *mad ) ) {
-		DBGC ( sma, "SMA %p RX bad size (%zd bytes)\n",
-		       sma, iob_len ( iobuf ) );
-		goto err;
-	}
-	mad = iobuf->data;
-
-	/* Construct MAD response */
-	if ( ( rc = ib_sma_mad ( sma, mad ) ) != 0 ) {
-		DBGC ( sma, "SMA %p could not construct MAD response: %s\n",
-		       sma, strerror ( rc ) );
-		goto err;
-	}
-
-	/* Send MAD response */
-	if ( ( rc = ib_post_send ( ibdev, qp, av, iobuf ) ) != 0 ) {
-		DBGC ( sma, "SMA %p could not send MAD response: %s\n",
-		       sma, strerror ( rc ) );
-		goto err;
-	}
-
-	return;
-
- err:
-	free_iob ( iobuf );
-}
-
-/** SMA completion operations */
-static struct ib_completion_queue_operations ib_sma_completion_ops = {
-	.complete_send = ib_sma_complete_send,
-	.complete_recv = ib_sma_complete_recv,
-};
-
-/**
- * Poll SMA
- *
- * @v process		Process
- */
-static void ib_sma_step ( struct process *process ) {
-	struct ib_sma *sma =
-		container_of ( process, struct ib_sma, poll );
-	struct ib_device *ibdev = sma->ibdev;
-
-	/* Poll the kernel completion queue */
-	ib_poll_cq ( ibdev, sma->cq );
-
-	/* Refill the receive ring */
-	ib_sma_refill_recv ( sma );
-}
-
-/**
- * Create SMA
- *
- * @v sma		Subnet management agent
- * @v ibdev		Infiniband device
- * @v op		Subnet management operations
- * @ret rc		Return status code
- */
-int ib_create_sma ( struct ib_sma *sma, struct ib_device *ibdev,
-		    struct ib_sma_operations *op ) {
-	int rc;
-
-	/* Initialise fields */
-	memset ( sma, 0, sizeof ( *sma ) );
-	sma->ibdev = ibdev;
-	sma->op = op;
-	process_init ( &sma->poll, ib_sma_step, &ibdev->refcnt );
-
-	/* Create completion queue */
-	sma->cq = ib_create_cq ( ibdev, IB_SMA_NUM_CQES,
-				 &ib_sma_completion_ops );
-	if ( ! sma->cq ) {
-		rc = -ENOMEM;
-		goto err_create_cq;
-	}
-
-	/* Create queue pair */
-	sma->qp = ib_create_qp ( ibdev, IB_SMA_NUM_SEND_WQES, sma->cq,
-				 IB_SMA_NUM_RECV_WQES, sma->cq, 0 );
-	if ( ! sma->qp ) {
-		rc = -ENOMEM;
-		goto err_create_qp;
-	}
-	ib_qp_set_ownerdata ( sma->qp, sma );
-
-	/* If we don't get QP0, we can't function */
-	if ( sma->qp->qpn != IB_QPN_SMP ) {
-		DBGC ( sma, "SMA %p on QPN %lx, needs to be on QPN 0\n",
-		       sma, sma->qp->qpn );
-		rc = -ENOTSUP;
-		goto err_not_qp0;
-	}
-
-	/* Fill receive ring */
-	ib_sma_refill_recv ( sma );
-	return 0;
-
- err_not_qp0:
-	ib_destroy_qp ( ibdev, sma->qp );
- err_create_qp:
-	ib_destroy_cq ( ibdev, sma->cq );
- err_create_cq:
-	process_del ( &sma->poll );
-	return rc;
-}
-
-/**
- * Destroy SMA
- *
- * @v sma		Subnet management agent
- */
-void ib_destroy_sma ( struct ib_sma *sma ) {
-	struct ib_device *ibdev = sma->ibdev;
-
-	ib_destroy_qp ( ibdev, sma->qp );
-	ib_destroy_cq ( ibdev, sma->cq );
-	process_del ( &sma->poll );
-}
diff --git a/gpxe/src/drivers/infiniband/ib_smc.c b/gpxe/src/drivers/infiniband/ib_smc.c
deleted file mode 100644
index af0c4ab..0000000
--- a/gpxe/src/drivers/infiniband/ib_smc.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2008 Michael Brown <mbrown at fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <byteswap.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_smc.h>
-
-/**
- * @file
- *
- * Infiniband Subnet Management Client
- *
- */
-
-/**
- * Get port information
- *
- * @v ibdev		Infiniband device
- * @v local_mad		Method for issuing local MADs
- * @v mad		Management datagram to fill in
- * @ret rc		Return status code
- */
-static int ib_smc_get_port_info ( struct ib_device *ibdev,
-				  ib_local_mad_t local_mad,
-				  union ib_mad *mad ) {
-	int rc;
-
-	/* Construct MAD */
-	memset ( mad, 0, sizeof ( *mad ) );
-	mad->hdr.base_version = IB_MGMT_BASE_VERSION;
-	mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
-	mad->hdr.class_version = 1;
-	mad->hdr.method = IB_MGMT_METHOD_GET;
-	mad->hdr.attr_id = htons ( IB_SMP_ATTR_PORT_INFO );
-	mad->hdr.attr_mod = htonl ( ibdev->port );
-
-	if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
-		DBGC ( ibdev, "IBDEV %p could not get port info: %s\n",
-		       ibdev, strerror ( rc ) );
-		return rc;
-	}
-	return 0;
-}
-
-/**
- * Get GUID information
- *
- * @v ibdev		Infiniband device
- * @v local_mad		Method for issuing local MADs
- * @v mad		Management datagram to fill in
- * @ret rc		Return status code
- */
-static int ib_smc_get_guid_info ( struct ib_device *ibdev,
-				  ib_local_mad_t local_mad,
-				  union ib_mad *mad ) {
-	int rc;
-
-	/* Construct MAD */
-	memset ( mad, 0, sizeof ( *mad ) );
-	mad->hdr.base_version = IB_MGMT_BASE_VERSION;
-	mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
-	mad->hdr.class_version = 1;
-	mad->hdr.method = IB_MGMT_METHOD_GET;
-	mad->hdr.attr_id = htons ( IB_SMP_ATTR_GUID_INFO );
-
-	if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
-		DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n",
-		       ibdev, strerror ( rc ) );
-		return rc;
-	}
-	return 0;
-}
-
-/**
- * Get partition key table
- *
- * @v ibdev		Infiniband device
- * @v local_mad		Method for issuing local MADs
- * @v mad		Management datagram to fill in
- * @ret rc		Return status code
- */
-static int ib_smc_get_pkey_table ( struct ib_device *ibdev,
-				   ib_local_mad_t local_mad,
-				   union ib_mad *mad ) {
-	int rc;
-
-	/* Construct MAD */
-	memset ( mad, 0, sizeof ( *mad ) );
-	mad->hdr.base_version = IB_MGMT_BASE_VERSION;
-	mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
-	mad->hdr.class_version = 1;
-	mad->hdr.method = IB_MGMT_METHOD_GET;
-	mad->hdr.attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE );
-
-	if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
-		DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n",
-		       ibdev, strerror ( rc ) );
-		return rc;
-	}
-	return 0;
-}
-
-/**
- * Get MAD parameters
- *
- * @v ibdev		Infiniband device
- * @v local_mad		Method for issuing local MADs
- * @ret rc		Return status code
- */
-int ib_smc_update ( struct ib_device *ibdev, ib_local_mad_t local_mad ) {
-	union ib_mad mad;
-	union ib_smp_data *smp = &mad.smp.smp_data;
-	int rc;
-
-	/* Port info gives us the link state, the first half of the
-	 * port GID and the SM LID.
-	 */
-	if ( ( rc = ib_smc_get_port_info ( ibdev, local_mad, &mad ) ) != 0 )
-		return rc;
-	ibdev->port_state =
-		( smp->port_info.link_speed_supported__port_state & 0x0f );
-	memcpy ( &ibdev->gid.u.half[0], smp->port_info.gid_prefix,
-		 sizeof ( ibdev->gid.u.half[0] ) );
-	ibdev->lid = ntohs ( smp->port_info.lid );
-	ibdev->sm_lid = ntohs ( smp->port_info.mastersm_lid );
-	ibdev->sm_sl = ( smp->port_info.neighbour_mtu__mastersm_sl & 0xf );
-
-	/* GUID info gives us the second half of the port GID */
-	if ( ( rc = ib_smc_get_guid_info ( ibdev, local_mad, &mad ) ) != 0 )
-		return rc;
-	memcpy ( &ibdev->gid.u.half[1], smp->guid_info.guid[0],
-		 sizeof ( ibdev->gid.u.half[1] ) );
-
-	/* Get partition key */
-	if ( ( rc = ib_smc_get_pkey_table ( ibdev, local_mad, &mad ) ) != 0 )
-		return rc;
-	ibdev->pkey = ntohs ( smp->pkey_table.pkey[0] );
-
-	DBGC ( ibdev, "IBDEV %p port GID is %08x:%08x:%08x:%08x\n", ibdev,
-	       htonl ( ibdev->gid.u.dwords[0] ),
-	       htonl ( ibdev->gid.u.dwords[1] ),
-	       htonl ( ibdev->gid.u.dwords[2] ),
-	       htonl ( ibdev->gid.u.dwords[3] ) );
-
-	return 0;
-}
diff --git a/gpxe/src/drivers/infiniband/linda.c b/gpxe/src/drivers/infiniband/linda.c
index c5d1317..b9a7ba5 100644
--- a/gpxe/src/drivers/infiniband/linda.c
+++ b/gpxe/src/drivers/infiniband/linda.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -28,7 +30,6 @@
 #include <gpxe/bitbash.h>
 #include <gpxe/malloc.h>
 #include <gpxe/iobuf.h>
-#include <gpxe/ib_sma.h>
 #include "linda.h"
 
 /**
@@ -95,9 +96,6 @@ struct linda {
 	struct i2c_bit_basher i2c;
 	/** I2C serial EEPROM */
 	struct i2c_device eeprom;
-
-	/** Subnet management agent */
-	struct ib_sma sma;
 };
 
 /***************************************************************************
@@ -233,22 +231,48 @@ static void linda_link_state_changed ( struct ib_device *ibdev ) {
 
 	/* Notify Infiniband core of link state change */
 	ibdev->port_state = ( link_state + 1 );
-	ibdev->link_width =
+	ibdev->link_width_active =
 		( link_width ? IB_LINK_WIDTH_4X : IB_LINK_WIDTH_1X );
-	ibdev->link_speed =
+	ibdev->link_speed_active =
 		( link_speed ? IB_LINK_SPEED_DDR : IB_LINK_SPEED_SDR );
 	ib_link_state_changed ( ibdev );
 }
 
 /**
+ * Wait for link state change to take effect
+ *
+ * @v linda		Linda device
+ * @v new_link_state	Expected link state
+ * @ret rc		Return status code
+ */
+static int linda_link_state_check ( struct linda *linda,
+				    unsigned int new_link_state ) {
+	struct QIB_7220_IBCStatus ibcstatus;
+	unsigned int link_state;
+	unsigned int i;
+
+	for ( i = 0 ; i < LINDA_LINK_STATE_MAX_WAIT_US ; i++ ) {
+		linda_readq ( linda, &ibcstatus, QIB_7220_IBCStatus_offset );
+		link_state = BIT_GET ( &ibcstatus, LinkState );
+		if ( link_state == new_link_state )
+			return 0;
+		udelay ( 1 );
+	}
+
+	DBGC ( linda, "Linda %p timed out waiting for link state %s\n",
+	       linda, linda_link_state_text ( link_state ) );
+	return -ETIMEDOUT;
+}
+
+/**
  * Set port information
  *
  * @v ibdev		Infiniband device
- * @v port_info		New port information
+ * @v mad		Set port information MAD
  */
-static int linda_set_port_info ( struct ib_device *ibdev,
-				 const struct ib_port_info *port_info ) {
+static int linda_set_port_info ( struct ib_device *ibdev, union ib_mad *mad ) {
 	struct linda *linda = ib_get_drvdata ( ibdev );
+	struct ib_port_info *port_info = &mad->smp.smp_data.port_info;
 	struct QIB_7220_IBCCtrl ibcctrl;
 	unsigned int port_state;
 	unsigned int link_state;
@@ -262,6 +286,12 @@ static int linda_set_port_info ( struct ib_device *ibdev,
 		linda_readq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
 		BIT_SET ( &ibcctrl, LinkCmd, link_state );
 		linda_writeq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
+
+		/* Wait for link state change to take effect.  Ignore
+		 * errors; the current link state will be returned via
+		 * the GetResponse MAD.
+		 */
+		linda_link_state_check ( linda, link_state );
 	}
 
 	/* Detect and report link state change */
@@ -270,10 +300,17 @@ static int linda_set_port_info ( struct ib_device *ibdev,
 	return 0;
 }
 
-/** Linda subnet management operations */
-static struct ib_sma_operations linda_sma_operations = {
-	.set_port_info	= linda_set_port_info,
-};
+/**
+ * Set partition key table
+ *
+ * @v ibdev		Infiniband device
+ * @v mad		Set partition key table MAD
+ */
+static int linda_set_pkey_table ( struct ib_device *ibdev __unused,
+				  union ib_mad *mad __unused ) {
+	/* Nothing to do */
+	return 0;
+}
 
 /***************************************************************************
  *
@@ -859,12 +896,10 @@ static int linda_create_qp ( struct ib_device *ibdev,
  *
  * @v ibdev		Infiniband device
  * @v qp		Queue pair
- * @v mod_list		Modification list
  * @ret rc		Return status code
  */
 static int linda_modify_qp ( struct ib_device *ibdev,
-			     struct ib_queue_pair *qp,
-			     unsigned long mod_list __unused ) {
+			     struct ib_queue_pair *qp ) {
 	struct linda *linda = ib_get_drvdata ( ibdev );
 
 	/* Nothing to do; the hardware doesn't have a notion of queue
@@ -1462,6 +1497,8 @@ static struct ib_device_operations linda_ib_operations = {
 	.close		= linda_close,
 	.mcast_attach	= linda_mcast_attach,
 	.mcast_detach	= linda_mcast_detach,
+	.set_port_info	= linda_set_port_info,
+	.set_pkey_table	= linda_set_pkey_table,
 };
 
 /***************************************************************************
@@ -1601,15 +1638,15 @@ static int linda_read_eeprom ( struct linda *linda,
 
 	/* Read GUID */
 	if ( ( rc = i2c->read ( i2c, &linda->eeprom, LINDA_EEPROM_GUID_OFFSET,
-				guid->bytes, sizeof ( *guid ) ) ) != 0 ) {
+				guid->u.bytes, sizeof ( *guid ) ) ) != 0 ) {
 		DBGC ( linda, "Linda %p could not read GUID: %s\n",
 		       linda, strerror ( rc ) );
 		return rc;
 	}
 	DBGC2 ( linda, "Linda %p has GUID %02x:%02x:%02x:%02x:%02x:%02x:"
-		"%02x:%02x\n", linda, guid->bytes[0], guid->bytes[1],
-		guid->bytes[2], guid->bytes[3], guid->bytes[4],
-		guid->bytes[5], guid->bytes[6], guid->bytes[7] );
+		"%02x:%02x\n", linda, guid->u.bytes[0], guid->u.bytes[1],
+		guid->u.bytes[2], guid->u.bytes[3], guid->u.bytes[4],
+		guid->u.bytes[5], guid->u.bytes[6], guid->u.bytes[7] );
 
 	/* Read serial number (debug only) */
 	if ( DBG_LOG ) {
@@ -2219,7 +2256,7 @@ static int linda_init_ib_serdes ( struct linda *linda ) {
 	linda_writeq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
 
 	/* Force SDR only to avoid needing all the DDR tuning,
-	 * Mellanox compatibiltiy hacks etc.  SDR is plenty for
+	 * Mellanox compatibility hacks etc.  SDR is plenty for
 	 * boot-time operation.
 	 */
 	linda_readq ( linda, &ibcddrctrl, QIB_7220_IBCDDRCtrl_offset );
@@ -2317,6 +2354,14 @@ static int linda_probe ( struct pci_device *pci,
 		BIT_GET ( &revision, R_ChipRevMajor ),
 		BIT_GET ( &revision, R_ChipRevMinor ) );
 
+	/* Record link capabilities.  Note that we force SDR only to
+	 * avoid having to carry extra code for DDR tuning etc.
+	 */
+	ibdev->link_width_enabled = ibdev->link_width_supported =
+		( IB_LINK_WIDTH_4X | IB_LINK_WIDTH_1X );
+	ibdev->link_speed_enabled = ibdev->link_speed_supported =
+		IB_LINK_SPEED_SDR;
+
 	/* Initialise I2C subsystem */
 	if ( ( rc = linda_init_i2c ( linda ) ) != 0 )
 		goto err_init_i2c;
@@ -2337,13 +2382,6 @@ static int linda_probe ( struct pci_device *pci,
 	if ( ( rc = linda_init_ib_serdes ( linda ) ) != 0 )
 		goto err_init_ib_serdes;
 
-	/* Create the SMA */
-	if ( ( rc = ib_create_sma ( &linda->sma, ibdev,
-				    &linda_sma_operations ) ) != 0 )
-		goto err_create_sma;
-	/* If the SMA doesn't get context 0, we're screwed */
-	assert ( linda_qpn_to_ctx ( linda->sma.qp->qpn ) == 0 );
-
 	/* Register Infiniband device */
 	if ( ( rc = register_ibdev ( ibdev ) ) != 0 ) {
 		DBGC ( linda, "Linda %p could not register IB "
@@ -2355,8 +2393,6 @@ static int linda_probe ( struct pci_device *pci,
 
 	unregister_ibdev ( ibdev );
  err_register_ibdev:
-	ib_destroy_sma ( &linda->sma );
- err_create_sma:
 	linda_fini_recv ( linda );
  err_init_recv:
 	linda_fini_send ( linda );
@@ -2379,14 +2415,13 @@ static void linda_remove ( struct pci_device *pci ) {
 	struct linda *linda = ib_get_drvdata ( ibdev );
 
 	unregister_ibdev ( ibdev );
-	ib_destroy_sma ( &linda->sma );
 	linda_fini_recv ( linda );
 	linda_fini_send ( linda );
 	ibdev_put ( ibdev );
 }
 
 static struct pci_device_id linda_nics[] = {
-	PCI_ROM ( 0x1077, 0x7220, "iba7220", "QLE7240/7280 HCA driver" ),
+	PCI_ROM ( 0x1077, 0x7220, "iba7220", "QLE7240/7280 HCA driver", 0 ),
 };
 
 struct pci_driver linda_driver __pci_driver = {
diff --git a/gpxe/src/drivers/infiniband/linda.h b/gpxe/src/drivers/infiniband/linda.h
index dd1737a..3068421 100644
--- a/gpxe/src/drivers/infiniband/linda.h
+++ b/gpxe/src/drivers/infiniband/linda.h
@@ -19,6 +19,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -268,4 +270,7 @@ enum linda_link_state {
 	LINDA_LINK_STATE_ACT_DEFER = 4,
 };
 
+/** Maximum time to wait for link state changes, in us */
+#define LINDA_LINK_STATE_MAX_WAIT_US 20
+
 #endif /* _LINDA_H */
diff --git a/gpxe/src/drivers/infiniband/linda_fw.c b/gpxe/src/drivers/infiniband/linda_fw.c
index fc5ea07..968a5f8 100644
--- a/gpxe/src/drivers/infiniband/linda_fw.c
+++ b/gpxe/src/drivers/infiniband/linda_fw.c
@@ -30,6 +30,8 @@
  * SOFTWARE.
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /*
  * This file contains the memory image from the vendor, to be copied into
  * the IB SERDES of the IBA7220 during initialization.
diff --git a/gpxe/src/drivers/infiniband/mlx_bitops.h b/gpxe/src/drivers/infiniband/mlx_bitops.h
index ec57d7b..71a9bf1 100644
--- a/gpxe/src/drivers/infiniband/mlx_bitops.h
+++ b/gpxe/src/drivers/infiniband/mlx_bitops.h
@@ -19,6 +19,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -104,6 +106,10 @@ typedef unsigned char pseudo_bit_t;
 	( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) |	     \
 	  MLX_ASSEMBLE_5 ( _structure_st, _index, __VA_ARGS__ ) )
 
+#define MLX_ASSEMBLE_7( _structure_st, _index, _field, _value, ... )	     \
+	( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) |	     \
+	  MLX_ASSEMBLE_6 ( _structure_st, _index, __VA_ARGS__ ) )
+
 /*
  * Build native-endian (positive) dword bitmasks from named fields
  *
@@ -133,6 +139,10 @@ typedef unsigned char pseudo_bit_t;
 	( MLX_MASK_1 ( _structure_st, _index, _field ) |		     \
 	  MLX_MASK_5 ( _structure_st, _index, __VA_ARGS__ ) )
 
+#define MLX_MASK_7( _structure_st, _index, _field, ... )		     \
+	( MLX_MASK_1 ( _structure_st, _index, _field ) |		     \
+	  MLX_MASK_6 ( _structure_st, _index, __VA_ARGS__ ) )
+
 /*
  * Populate big-endian dwords from named fields and values
  *
@@ -169,6 +179,10 @@ typedef unsigned char pseudo_bit_t;
 	MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_6 ( MLX_PSEUDO_STRUCT ( _ptr ),\
 						  _index, __VA_ARGS__ ) )
 
+#define MLX_FILL_7( _ptr, _index, ... )					     \
+	MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_7 ( MLX_PSEUDO_STRUCT ( _ptr ),\
+						  _index, __VA_ARGS__ ) )
+
 /*
  * Modify big-endian dword using named field and value
  *
diff --git a/gpxe/src/drivers/infiniband/qib_7220_regs.h b/gpxe/src/drivers/infiniband/qib_7220_regs.h
index 0dd3c53..0637ec8 100644
--- a/gpxe/src/drivers/infiniband/qib_7220_regs.h
+++ b/gpxe/src/drivers/infiniband/qib_7220_regs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 QLogic Corporation. All rights reserved.
+ * Copyright (c) 2008, 2009 QLogic Corporation. All rights reserved.
  *
  *
  * This software is available to you under a choice of one of two
@@ -30,12 +30,12 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  *
- * This file is mechanically generated. Any hand-edits will be lost.
- * If not now, soon.
  */
+/* This file is mechanically generated from RTL. Any hand-edits will be lost! */
 
 /* This file has been further processed by ./drivers/infiniband/qib_genbits.pl */
 
+FILE_LICENCE ( GPL2_ONLY );
 
 #define QIB_7220_Revision_offset 0x00000000UL
 struct QIB_7220_Revision_pb {
@@ -44,8 +44,8 @@ struct QIB_7220_Revision_pb {
 	pseudo_bit_t R_Arch[8];
 	pseudo_bit_t R_SW[8];
 	pseudo_bit_t BoardID[8];
-	pseudo_bit_t R_Palldium_Revcode[22];
-	pseudo_bit_t R_Palladium[1];
+	pseudo_bit_t R_Emulation_Revcode[22];
+	pseudo_bit_t R_Emulation[1];
 	pseudo_bit_t R_Simulator[1];
 };
 struct QIB_7220_Revision {
@@ -1250,7 +1250,6 @@ struct QIB_7220_SendDmaReqTagUsed_pb {
 	pseudo_bit_t ReqTagUsed_7_0[8];
 	pseudo_bit_t _unused_0[8];
 	pseudo_bit_t Reserved[48];
-	pseudo_bit_t _unused_1[8];
 };
 struct QIB_7220_SendDmaReqTagUsed {
 	PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaReqTagUsed_pb );
diff --git a/gpxe/src/drivers/infiniband/qib_genbits.pl b/gpxe/src/drivers/infiniband/qib_genbits.pl
old mode 100644
new mode 100755
index 9eba4da..1d5eede
--- a/gpxe/src/drivers/infiniband/qib_genbits.pl
+++ b/gpxe/src/drivers/infiniband/qib_genbits.pl
@@ -20,6 +20,7 @@ use strict;
 use warnings;
 
 my $offsets = {};
+my $defaults = {};
 my $structures = {};
 my $structure = "";
 
@@ -28,8 +29,12 @@ while ( <> ) {
   if ( /^\#define (\S+)_OFFS (\S+)$/ ) {
     $structure = $1;
     $offsets->{$structure} = $2;
+  } elsif ( /^\#define ${structure}_DEF (\S+)$/ ) {
+    $defaults->{$structure} = $1;
   } elsif ( /^\#define ${structure}_(\S+)_LSB (\S+)$/ ) {
     $structures->{$structure}->{$1}->{LSB} = $2;
+  } elsif ( /^\#define ${structure}_(\S+)_MSB (\S+)$/ ) {
+    $structures->{$structure}->{$1}->{MSB} = $2;
   } elsif ( /^\#define ${structure}_(\S+)_RMASK (\S+)$/ ) {
     $structures->{$structure}->{$1}->{RMASK} = $2;
   } elsif ( /^\s*$/ ) {
@@ -39,7 +44,8 @@ while ( <> ) {
   }
 }
 
-my $data = [ map { { name => $_, offset => $offsets->{$_} }; }
+my $data = [ map { { name => $_, offset => $offsets->{$_},
+		     default => $defaults->{$_} }; }
 	     sort { hex ( $offsets->{$a} ) <=> hex ( $offsets->{$b} ) }
 	     keys %$offsets ];
 
@@ -47,6 +53,7 @@ foreach my $datum ( @$data ) {
   next unless exists $structures->{$datum->{name}};
   $structure = $structures->{$datum->{name}};
   my $fields = [ map { { name => $_, lsb => $structure->{$_}->{LSB},
+			 msb => $structure->{$_}->{MSB},
 			 rmask => $structure->{$_}->{RMASK} }; }
 		 sort { hex ( $structure->{$a}->{LSB} ) <=>
 			    hex ( $structure->{$b}->{LSB} ) }
@@ -54,7 +61,8 @@ foreach my $datum ( @$data ) {
   $datum->{fields} = $fields;
 }
 
-print "\n/* This file has been further processed by $0 */\n\n\n";
+print "\n/* This file has been further processed by $0 */\n\n";
+print "FILE_LICENCE ( GPL2_ONLY );\n\n";
 
 foreach my $datum ( @$data ) {
   printf "#define %s_offset 0x%08xUL\n",
@@ -65,11 +73,15 @@ foreach my $datum ( @$data ) {
     printf "struct %s_pb {\n", $datum->{name};
     foreach my $field ( @{$datum->{fields}} ) {
       my $pad_width = ( hex ( $field->{lsb} ) - $lsb );
-      die "Inconsistent LSB/RMASK in $datum->{name}\n" if $pad_width < 0;
+      die "Inconsistent LSB/RMASK in $datum->{name} before $field->{name}\n"
+	  if $pad_width < 0;
       printf "\tpseudo_bit_t _unused_%u[%u];\n", $reserved_idx++, $pad_width
 	  if $pad_width;
+      $lsb += $pad_width;
       # Damn Perl can't cope with 64-bit hex constants
       my $width = 0;
+      die "Missing RMASK in $datum->{name}.$field->{name}\n"
+	  unless defined $field->{rmask};
       my $rmask = $field->{rmask};
       while ( $rmask =~ /^(0x.+)f$/i ) {
 	$width += 4;
@@ -80,16 +92,25 @@ foreach my $datum ( @$data ) {
 	$width++;
 	$rmask >>= 1;
       }
+      if ( defined $field->{msb} ) {
+	my $msb_width = ( hex ( $field->{msb} ) - $lsb + 1 );
+	$width ||= $msb_width;
+	die "Inconsistent LSB/MSB/RMASK in $datum->{name}.$field->{name}\n"
+	    unless $width == $msb_width;
+      }
       printf "\tpseudo_bit_t %s[%u];\n", $field->{name}, $width;
       $lsb += $width;
     }
     my $pad_width = ( 64 - $lsb );
-    die "Inconsistent LSB/RMASK in $datum->{name}\n" if $pad_width < 0;
+    die "Inconsistent LSB/RMASK in $datum->{name} final field\n"
+	if $pad_width < 0;
     printf "\tpseudo_bit_t _unused_%u[%u];\n", $reserved_idx++, $pad_width
 	if $pad_width;
     printf "};\n";
     printf "struct %s {\n\tPSEUDO_BIT_STRUCT ( struct %s_pb );\n};\n",
 	$datum->{name}, $datum->{name};
   }
+  printf "/* Default value: %s */\n", $datum->{default}
+      if defined $datum->{default};
   print "\n";
 }
diff --git a/gpxe/src/drivers/net/3c509.c b/gpxe/src/drivers/net/3c509.c
index ecfdec5..1c58f77 100644
--- a/gpxe/src/drivers/net/3c509.c
+++ b/gpxe/src/drivers/net/3c509.c
@@ -4,6 +4,8 @@
  *
  */
 
+FILE_LICENCE ( BSD2 );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/gpxe/src/drivers/net/3c509.h b/gpxe/src/drivers/net/3c509.h
index a06d91e..f030d4b 100644
--- a/gpxe/src/drivers/net/3c509.h
+++ b/gpxe/src/drivers/net/3c509.h
@@ -31,6 +31,8 @@
 
  */
 
+FILE_LICENCE ( BSD3 );
+
 #include "nic.h"
 
 /*
diff --git a/gpxe/src/drivers/net/3c515.c b/gpxe/src/drivers/net/3c515.c
index dcfe66b..eb9569f 100644
--- a/gpxe/src/drivers/net/3c515.c
+++ b/gpxe/src/drivers/net/3c515.c
@@ -43,6 +43,7 @@
 * Indent Options: indent -kr -i8
 * *********************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
 
 /* to get some global routines like printf */
 #include "etherboot.h"
diff --git a/gpxe/src/drivers/net/3c529.c b/gpxe/src/drivers/net/3c529.c
index 3193104..4282464 100644
--- a/gpxe/src/drivers/net/3c529.c
+++ b/gpxe/src/drivers/net/3c529.c
@@ -3,6 +3,8 @@
  *
  */
 
+FILE_LICENCE ( BSD2 );
+
 #include "etherboot.h"
 #include <gpxe/mca.h>
 #include <gpxe/isa.h> /* for ISA_ROM */
diff --git a/gpxe/src/drivers/net/3c595.c b/gpxe/src/drivers/net/3c595.c
index 198e12e..07c85d0 100644
--- a/gpxe/src/drivers/net/3c595.c
+++ b/gpxe/src/drivers/net/3c595.c
@@ -23,6 +23,8 @@
 * timlegge	08-24-2003	Add Multicast Support
 */
 
+FILE_LICENCE ( BSD2 );
+
 /* #define EDEBUG */
 
 #include "etherboot.h"
@@ -521,20 +523,20 @@ static struct nic_operations t595_operations = {
 };
 
 static struct pci_device_id t595_nics[] = {
-PCI_ROM(0x10b7, 0x5900, "3c590",           "3Com590"),		/* Vortex 10Mbps */
-PCI_ROM(0x10b7, 0x5950, "3c595",           "3Com595"),		/* Vortex 100baseTx */
-PCI_ROM(0x10b7, 0x5951, "3c595-1",         "3Com595"),		/* Vortex 100baseT4 */
-PCI_ROM(0x10b7, 0x5952, "3c595-2",         "3Com595"),		/* Vortex 100base-MII */
-PCI_ROM(0x10b7, 0x9000, "3c900-tpo",       "3Com900-TPO"),	/* 10 Base TPO */
-PCI_ROM(0x10b7, 0x9001, "3c900-t4",        "3Com900-Combo"),	/* 10/100 T4 */
-PCI_ROM(0x10b7, 0x9004, "3c900b-tpo",      "3Com900B-TPO"),	/* 10 Base TPO */
-PCI_ROM(0x10b7, 0x9005, "3c900b-combo",    "3Com900B-Combo"),	/* 10 Base Combo */
-PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2",     "3Com900B-2/T"),	/* 10 Base TP and Base2 */
-PCI_ROM(0x10b7, 0x900a, "3c900b-fl",       "3Com900B-FL"),	/* 10 Base F */
-PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone"),	/* Cyclone */
-PCI_ROM(0x10b7, 0x9805, "3c9805-1",        "3Com9805"),		/* Dual Port Server Cyclone */
-PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1",  "3CSOHO100-TX"),	/* Hurricane */
-PCI_ROM(0x10b7, 0x4500, "3c450-1",         "3Com450 HomePNA Tornado"),
+PCI_ROM(0x10b7, 0x5900, "3c590",           "3Com590", 0),		/* Vortex 10Mbps */
+PCI_ROM(0x10b7, 0x5950, "3c595",           "3Com595", 0),		/* Vortex 100baseTx */
+PCI_ROM(0x10b7, 0x5951, "3c595-1",         "3Com595", 0),		/* Vortex 100baseT4 */
+PCI_ROM(0x10b7, 0x5952, "3c595-2",         "3Com595", 0),		/* Vortex 100base-MII */
+PCI_ROM(0x10b7, 0x9000, "3c900-tpo",       "3Com900-TPO", 0),	/* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9001, "3c900-t4",        "3Com900-Combo", 0),	/* 10/100 T4 */
+PCI_ROM(0x10b7, 0x9004, "3c900b-tpo",      "3Com900B-TPO", 0),	/* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9005, "3c900b-combo",    "3Com900B-Combo", 0),	/* 10 Base Combo */
+PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2",     "3Com900B-2/T", 0),	/* 10 Base TP and Base2 */
+PCI_ROM(0x10b7, 0x900a, "3c900b-fl",       "3Com900B-FL", 0),	/* 10 Base F */
+PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone", 0),	/* Cyclone */
+PCI_ROM(0x10b7, 0x9805, "3c9805-1",        "3Com9805", 0),		/* Dual Port Server Cyclone */
+PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1",  "3CSOHO100-TX", 0),	/* Hurricane */
+PCI_ROM(0x10b7, 0x4500, "3c450-1",         "3Com450 HomePNA Tornado", 0),
 };
 
 PCI_DRIVER ( t595_driver, t595_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/3c595.h b/gpxe/src/drivers/net/3c595.h
index 49d8d9b..e27d204 100644
--- a/gpxe/src/drivers/net/3c595.h
+++ b/gpxe/src/drivers/net/3c595.h
@@ -29,6 +29,8 @@
 
  */
 
+FILE_LICENCE ( BSD3 );
+
 /*
  * Created from if_epreg.h by Fred Gray (fgray at rice.edu) to support the
  * 3c590 family.
diff --git a/gpxe/src/drivers/net/3c5x9.c b/gpxe/src/drivers/net/3c5x9.c
index 565044a..87c9f29 100644
--- a/gpxe/src/drivers/net/3c5x9.c
+++ b/gpxe/src/drivers/net/3c5x9.c
@@ -22,6 +22,8 @@ $Id$
 
 ***************************************************************************/
 
+FILE_LICENCE ( BSD2 );
+
 /* #define EDEBUG */
 
 #include <gpxe/ethernet.h>
diff --git a/gpxe/src/drivers/net/3c90x.c b/gpxe/src/drivers/net/3c90x.c
index a98e662..9c1879b 100644
--- a/gpxe/src/drivers/net/3c90x.c
+++ b/gpxe/src/drivers/net/3c90x.c
@@ -1,10 +1,19 @@
 /*
- * 3c90x.c -- This file implements the 3c90x driver for etherboot.  Written
- * by Greg Beeley, Greg.Beeley at LightSys.org.  Modified by Steve Smith,
- * Steve.Smith at Juno.Com. Alignment bug fix Neil Newell (nn at icenoir.net).
+ * 3c90x.c -- This file implements a gPXE API 3c90x driver
  *
- * This program Copyright (C) 1999 LightSys Technology Services, Inc.
- * Portions Copyright (C) 1999 Steve Smith
+ * Originally written for etherboot by:
+ *   Greg Beeley, Greg.Beeley at LightSys.org
+ * Modified by Steve Smith,
+ *   Steve.Smith at Juno.Com. Alignment bug fix Neil Newell (nn at icenoir.net).
+ * Almost totally Rewritten to use gPXE API, implementation of tx/rx ring support
+ *   by Thomas Miletich, thomas.miletich at gmail.com
+ *   Thanks to Marty Connor and Stefan Hajnoczi for their help and feedback,
+ *   and to Daniel Verkamp for his help with testing.
+ *
+ * Copyright (c) 2009 Thomas Miletich
+ *
+ * Copyright (c) 1999 LightSys Technology Services, Inc.
+ * Portions Copyright (c) 1999 Steve Smith
  *
  * This program may be re-distributed in source or binary form, modified,
  * sold, or copied for any purpose, provided that the above copyright message
@@ -15,1008 +24,966 @@
  * PURPOSE or MERCHANTABILITY.  Please read the associated documentation
  * "3c90x.txt" before compiling and using this driver.
  *
- * --------
+ * [ --mdc 20090313 The 3c90x.txt file is now at:
+ *   http://etherboot.org/wiki/appnotes/3c90x_issues ]
  *
- * Program written with the assistance of the 3com documentation for
+ * This program was written with the assistance of the 3com documentation for
  * the 3c905B-TX card, as well as with some assistance from the 3c59x
  * driver Donald Becker wrote for the Linux kernel, and with some assistance
  * from the remainder of the Etherboot distribution.
  *
- * REVISION HISTORY:
- *
- * v0.10	1-26-1998	GRB	Initial implementation.
- * v0.90	1-27-1998	GRB	System works.
- * v1.00pre1	2-11-1998	GRB	Got prom boot issue fixed.
- * v2.0		9-24-1999	SCS	Modified for 3c905 (from 3c905b code)
- *					Re-wrote poll and transmit for
- *					better error recovery and heavy
- *					network traffic operation
- * v2.01    5-26-2003 NN Fixed driver alignment issue which
- *                  caused system lockups if driver structures
- *                  not 8-byte aligned.
- * v2.02   11-28-2007 GSt Got polling working again by replacing
- * 			"for(i=0;i<40000;i++);" with "mdelay(1);"
- *
+ * Indented with unix 'indent' command: 
+ *   $ indent -kr -i8 3c90x.c
  */
 
-#include "etherboot.h"
-#include "nic.h"
-#include <gpxe/pci.h>
+FILE_LICENCE ( BSD2 );
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <errno.h>
 #include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/io.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/pci.h>
+#include <gpxe/timer.h>
+#include <gpxe/nvs.h>
 
-static struct nic_operations a3c90x_operations;
-
-#define	XCVR_MAGIC	(0x5A00)
-/** any single transmission fails after 16 collisions or other errors
- ** this is the number of times to retry the transmission -- this should
- ** be plenty
- **/
-#define	XMIT_RETRIES	250
-
-/*** Register definitions for the 3c905 ***/
-enum Registers
-    {
-    regPowerMgmtCtrl_w = 0x7c,        /** 905B Revision Only                 **/
-    regUpMaxBurst_w = 0x7a,           /** 905B Revision Only                 **/
-    regDnMaxBurst_w = 0x78,           /** 905B Revision Only                 **/
-    regDebugControl_w = 0x74,         /** 905B Revision Only                 **/
-    regDebugData_l = 0x70,            /** 905B Revision Only                 **/
-    regRealTimeCnt_l = 0x40,          /** Universal                          **/
-    regUpBurstThresh_b = 0x3e,        /** 905B Revision Only                 **/
-    regUpPoll_b = 0x3d,               /** 905B Revision Only                 **/
-    regUpPriorityThresh_b = 0x3c,     /** 905B Revision Only                 **/
-    regUpListPtr_l = 0x38,            /** Universal                          **/
-    regCountdown_w = 0x36,            /** Universal                          **/
-    regFreeTimer_w = 0x34,            /** Universal                          **/
-    regUpPktStatus_l = 0x30,          /** Universal with Exception, pg 130   **/
-    regTxFreeThresh_b = 0x2f,         /** 90X Revision Only                  **/
-    regDnPoll_b = 0x2d,               /** 905B Revision Only                 **/
-    regDnPriorityThresh_b = 0x2c,     /** 905B Revision Only                 **/
-    regDnBurstThresh_b = 0x2a,        /** 905B Revision Only                 **/
-    regDnListPtr_l = 0x24,            /** Universal with Exception, pg 107   **/
-    regDmaCtrl_l = 0x20,              /** Universal with Exception, pg 106   **/
-                                      /**                                    **/
-    regIntStatusAuto_w = 0x1e,        /** 905B Revision Only                 **/
-    regTxStatus_b = 0x1b,             /** Universal with Exception, pg 113   **/
-    regTimer_b = 0x1a,                /** Universal                          **/
-    regTxPktId_b = 0x18,              /** 905B Revision Only                 **/
-    regCommandIntStatus_w = 0x0e,     /** Universal (Command Variations)     **/
-    };
-
-/** following are windowed registers **/
-enum Registers7
-    {
-    regPowerMgmtEvent_7_w = 0x0c,     /** 905B Revision Only                 **/
-    regVlanEtherType_7_w = 0x04,      /** 905B Revision Only                 **/
-    regVlanMask_7_w = 0x00,           /** 905B Revision Only                 **/
-    };
-
-enum Registers6
-    {
-    regBytesXmittedOk_6_w = 0x0c,     /** Universal                          **/
-    regBytesRcvdOk_6_w = 0x0a,        /** Universal                          **/
-    regUpperFramesOk_6_b = 0x09,      /** Universal                          **/
-    regFramesDeferred_6_b = 0x08,     /** Universal                          **/
-    regFramesRecdOk_6_b = 0x07,       /** Universal with Exceptions, pg 142  **/
-    regFramesXmittedOk_6_b = 0x06,    /** Universal                          **/
-    regRxOverruns_6_b = 0x05,         /** Universal                          **/
-    regLateCollisions_6_b = 0x04,     /** Universal                          **/
-    regSingleCollisions_6_b = 0x03,   /** Universal                          **/
-    regMultipleCollisions_6_b = 0x02, /** Universal                          **/
-    regSqeErrors_6_b = 0x01,          /** Universal                          **/
-    regCarrierLost_6_b = 0x00,        /** Universal                          **/
-    };
-
-enum Registers5
-    {
-    regIndicationEnable_5_w = 0x0c,   /** Universal                          **/
-    regInterruptEnable_5_w = 0x0a,    /** Universal                          **/
-    regTxReclaimThresh_5_b = 0x09,    /** 905B Revision Only                 **/
-    regRxFilter_5_b = 0x08,           /** Universal                          **/
-    regRxEarlyThresh_5_w = 0x06,      /** Universal                          **/
-    regTxStartThresh_5_w = 0x00,      /** Universal                          **/
-    };
-
-enum Registers4
-    {
-    regUpperBytesOk_4_b = 0x0d,       /** Universal                          **/
-    regBadSSD_4_b = 0x0c,             /** Universal                          **/
-    regMediaStatus_4_w = 0x0a,        /** Universal with Exceptions, pg 201  **/
-    regPhysicalMgmt_4_w = 0x08,       /** Universal                          **/
-    regNetworkDiagnostic_4_w = 0x06,  /** Universal with Exceptions, pg 203  **/
-    regFifoDiagnostic_4_w = 0x04,     /** Universal with Exceptions, pg 196  **/
-    regVcoDiagnostic_4_w = 0x02,      /** Undocumented?                      **/
-    };
-
-enum Registers3
-    {
-    regTxFree_3_w = 0x0c,             /** Universal                          **/
-    regRxFree_3_w = 0x0a,             /** Universal with Exceptions, pg 125  **/
-    regResetMediaOptions_3_w = 0x08,  /** Media Options on B Revision,       **/
-                                      /** Reset Options on Non-B Revision    **/
-    regMacControl_3_w = 0x06,         /** Universal with Exceptions, pg 199  **/
-    regMaxPktSize_3_w = 0x04,         /** 905B Revision Only                 **/
-    regInternalConfig_3_l = 0x00,     /** Universal, different bit           **/
-                                      /** definitions, pg 59                 **/
-    };
-
-enum Registers2
-    {
-    regResetOptions_2_w = 0x0c,       /** 905B Revision Only                 **/
-    regStationMask_2_3w = 0x06,       /** Universal with Exceptions, pg 127  **/
-    regStationAddress_2_3w = 0x00,    /** Universal with Exceptions, pg 127  **/
-    };
-
-enum Registers1
-    {
-    regRxStatus_1_w = 0x0a,           /** 90X Revision Only, Pg 126          **/
-    };
-
-enum Registers0
-    {
-    regEepromData_0_w = 0x0c,         /** Universal                          **/
-    regEepromCommand_0_w = 0x0a,      /** Universal                          **/
-    regBiosRomData_0_b = 0x08,        /** 905B Revision Only                 **/
-    regBiosRomAddr_0_l = 0x04,        /** 905B Revision Only                 **/
-    };
-
-
-/*** The names for the eight register windows ***/
-enum Windows
-    {
-    winPowerVlan7 = 0x07,
-    winStatistics6 = 0x06,
-    winTxRxControl5 = 0x05,
-    winDiagnostics4 = 0x04,
-    winTxRxOptions3 = 0x03,
-    winAddressing2 = 0x02,
-    winUnused1 = 0x01,
-    winEepromBios0 = 0x00,
-    };
-
-
-/*** Command definitions for the 3c90X ***/
-enum Commands
-    {
-    cmdGlobalReset = 0x00,             /** Universal with Exceptions, pg 151 **/
-    cmdSelectRegisterWindow = 0x01,    /** Universal                         **/
-    cmdEnableDcConverter = 0x02,       /**                                   **/
-    cmdRxDisable = 0x03,               /**                                   **/
-    cmdRxEnable = 0x04,                /** Universal                         **/
-    cmdRxReset = 0x05,                 /** Universal                         **/
-    cmdStallCtl = 0x06,                /** Universal                         **/
-    cmdTxEnable = 0x09,                /** Universal                         **/
-    cmdTxDisable = 0x0A,               /**                                   **/
-    cmdTxReset = 0x0B,                 /** Universal                         **/
-    cmdRequestInterrupt = 0x0C,        /**                                   **/
-    cmdAcknowledgeInterrupt = 0x0D,    /** Universal                         **/
-    cmdSetInterruptEnable = 0x0E,      /** Universal                         **/
-    cmdSetIndicationEnable = 0x0F,     /** Universal                         **/
-    cmdSetRxFilter = 0x10,             /** Universal                         **/
-    cmdSetRxEarlyThresh = 0x11,        /**                                   **/
-    cmdSetTxStartThresh = 0x13,        /**                                   **/
-    cmdStatisticsEnable = 0x15,        /**                                   **/
-    cmdStatisticsDisable = 0x16,       /**                                   **/
-    cmdDisableDcConverter = 0x17,      /**                                   **/
-    cmdSetTxReclaimThresh = 0x18,      /**                                   **/
-    cmdSetHashFilterBit = 0x19,        /**                                   **/
-    };
-
-
-/*** Values for int status register bitmask **/
-#define	INT_INTERRUPTLATCH	(1<<0)
-#define INT_HOSTERROR		(1<<1)
-#define INT_TXCOMPLETE		(1<<2)
-#define INT_RXCOMPLETE		(1<<4)
-#define INT_RXEARLY		(1<<5)
-#define INT_INTREQUESTED	(1<<6)
-#define INT_UPDATESTATS		(1<<7)
-#define INT_LINKEVENT		(1<<8)
-#define INT_DNCOMPLETE		(1<<9)
-#define INT_UPCOMPLETE		(1<<10)
-#define INT_CMDINPROGRESS	(1<<12)
-#define INT_WINDOWNUMBER	(7<<13)
-
-
-/*** TX descriptor ***/
-typedef struct
-    {
-    unsigned int	DnNextPtr;
-    unsigned int	FrameStartHeader;
-    unsigned int	HdrAddr;
-    unsigned int	HdrLength;
-    unsigned int	DataAddr;
-    unsigned int	DataLength;
-    }
-    TXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
-
-/*** RX descriptor ***/
-typedef struct
-    {
-    unsigned int	UpNextPtr;
-    unsigned int	UpPktStatus;
-    unsigned int	DataAddr;
-    unsigned int	DataLength;
-    }
-    RXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
-
-/*** Global variables ***/
-static struct
-    {
-    unsigned int	is3c556;
-    unsigned char	isBrev;
-    unsigned char	CurrentWindow;
-    unsigned int	IOAddr;
-    unsigned char	HWAddr[ETH_ALEN];
-    TXD			TransmitDPD;
-    RXD			ReceiveUPD;
-    }
-    INF_3C90X;
-
-
-/*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card
- ***/
-static int
-a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
-    {
-    unsigned int val;
+#include "3c90x.h"
+
+/**
+ * a3c90x_internal_IssueCommand: sends a command to the 3c90x card
+ * and waits for it's completion
+ *
+ * @v ioaddr	IOAddress of the NIC
+ * @v cmd	Command to be issued
+ * @v param	Command parameter
+ */
+static void a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
+{
+	unsigned int val = (cmd << 11) | param;
+	int cnt = 0;
 
-	/** Build the cmd. **/
-	val = cmd;
-	val <<= 11;
-	val |= param;
+	DBGP("a3c90x_internal_IssueCommand\n");
 
-	/** Send the cmd to the cmd register **/
+	/* Send the cmd to the cmd register */
 	outw(val, ioaddr + regCommandIntStatus_w);
 
-	/** Wait for the cmd to complete, if necessary **/
-	while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
+	/* Wait for the cmd to complete */
+	for (cnt = 0; cnt < 100000; cnt++) {
+		if (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) {
+			continue;
+		} else {
+			DBG2("Command 0x%04X finished in time. cnt = %d.\n", cmd, cnt);
+			return;
+		}
+	}
 
-    return 0;
-    }
+	DBG("Command 0x%04X DID NOT finish in time. cnt = %d.\n", cmd, cnt);
+}
 
+/**
+ * a3c90x_internal_SetWindow: selects a register window set.
+ *
+ * @v inf_3c90x	private NIC data
+ * @v window	window to be selected
+ */
+static void a3c90x_internal_SetWindow(struct INF_3C90X *inf_3c90x, int window)
+{
+	DBGP("a3c90x_internal_SetWindow\n");
+	/* Window already as set? */
+	if (inf_3c90x->CurrentWindow == window)
+		return;
 
-/*** a3c90x_internal_SetWindow: selects a register window set.
- ***/
-static int
-a3c90x_internal_SetWindow(int ioaddr, int window)
-    {
-
-	/** Window already as set? **/
-	if (INF_3C90X.CurrentWindow == window) return 0;
-
-	/** Issue the window command. **/
-	a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window);
-	INF_3C90X.CurrentWindow = window;
-
-    return 0;
-    }
-
-
-/*** a3c90x_internal_ReadEeprom - read data from the serial eeprom.
- ***/
-static unsigned short
-a3c90x_internal_ReadEeprom(int ioaddr, int address)
-    {
-    unsigned short val;
-
-	/** Select correct window **/
-        a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0);
-
-	/** Make sure the eeprom isn't busy **/
-	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
-
-	/** Read the value. **/
-	if (INF_3C90X.is3c556)
-	 {
-	     outw(address + (0x230), ioaddr + regEepromCommand_0_w);
-	 }
-	else
-	 {
-	     outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w);
-	 }
- 
-	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
-	val = inw(ioaddr + regEepromData_0_w);
-
-    return val;
-    }
-
-
-#if 0
-/*** a3c90x_internal_WriteEepromWord - write a physical word of
- *** data to the onboard serial eeprom (not the BIOS prom, but the
- *** nvram in the card that stores, among other things, the MAC
- *** address).
- ***/
-static int
-a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value)
-    {
-	/** Select register window **/
-        a3c90x_internal_SetWindow(ioaddr, winEepromBios0);
-
-	/** Verify Eeprom not busy **/
-	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
-
-	/** Issue WriteEnable, and wait for completion. **/
-	outw(0x30, ioaddr + regEepromCommand_0_w);
-	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
-
-	/** Issue EraseRegister, and wait for completion. **/
-	outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w);
-	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
-
-	/** Send the new data to the eeprom, and wait for completion. **/
-	outw(value, ioaddr + regEepromData_0_w);
-	outw(0x30, ioaddr + regEepromCommand_0_w);
-	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
-
-	/** Burn the new data into the eeprom, and wait for completion. **/
-	outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w);
-	while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
-
-    return 0;
-    }
-#endif
-
-#if 0
-/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom,
- *** and re-compute the eeprom checksum.
- ***/
-static int
-a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value)
-    {
-    int cksum = 0,v;
-    int i;
-    int maxAddress, cksumAddress;
-
-	if (INF_3C90X.isBrev)
-	    {
-	    maxAddress=0x1f;
-	    cksumAddress=0x20;
-	    }
-	else
-	    {
-	    maxAddress=0x16;
-	    cksumAddress=0x17;
-	    }
-
-	/** Write the value. **/
-	if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1)
-	    return -1;
-
-	/** Recompute the checksum. **/
-	for(i=0;i<=maxAddress;i++)
-	    {
-	    v = a3c90x_internal_ReadEeprom(ioaddr, i);
-	    cksum ^= (v & 0xFF);
-	    cksum ^= ((v>>8) & 0xFF);
-	    }
-	/** Write the checksum to the location in the eeprom **/
-	if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1)
-	    return -1;
-
-    return 0;
-    }
-#endif
-
-/*** a3c90x_reset: exported function that resets the card to its default
- *** state.  This is so the Linux driver can re-set the card up the way
- *** it wants to.  If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
- *** not alter the selected transceiver that we used to download the boot
- *** image.
- ***/
-static void a3c90x_reset(void)
-    {
-#ifdef	CFG_3C90X_PRESERVE_XCVR
-    int cfg;
-    /** Read the current InternalConfig value. **/
-    a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
-    cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
-#endif
-
-    /** Send the reset command to the card **/
-    printf("Issuing RESET:\n");
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0);
-
-    /** wait for reset command to complete **/
-    while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
-
-    /** global reset command resets station mask, non-B revision cards
-     ** require explicit reset of values
-     **/
-    a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
-    outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
-    outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
-    outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
-
-#ifdef	CFG_3C90X_PRESERVE_XCVR
-    /** Re-set the original InternalConfig value from before reset **/
-    a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
-    outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
-
-    /** enable DC converter for 10-Base-T **/
-    if ((cfg&0x0300) == 0x0300)
-	{
-	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
-	}
-#endif
-
-    /** Issue transmit reset, wait for command completion **/
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0);
-    while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
-	;
-    if (! INF_3C90X.isBrev)
-	outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
-
-    /**
-     ** reset of the receiver on B-revision cards re-negotiates the link
-     ** takes several seconds (a computer eternity)
-     **/
-    if (INF_3C90X.isBrev)
-	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
-    else
-	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
-    while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
-	;
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
-
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
-                                 cmdSetInterruptEnable, 0);
-    /** enable rxComplete and txComplete **/
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
-                                 cmdSetIndicationEnable, 0x0014);
-    /** acknowledge any pending status flags **/
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
-                                 cmdAcknowledgeInterrupt, 0x661);
-
-    return;
-    }
-
-
-
-/*** a3c90x_transmit: exported function that transmits a packet.  Does not
- *** return any particular status.  Parameters are:
- *** d[6] - destination address, ethernet;
- *** t - protocol type (ARP, IP, etc);
- *** s - size of the non-header part of the packet that needs transmitted;
- *** p - the pointer to the packet data itself.
- ***/
-static void
-a3c90x_transmit(struct nic *nic __unused, const char *d, unsigned int t,
-                unsigned int s, const char *p)
-    {
-
-    struct eth_hdr
-	{
-	unsigned char dst_addr[ETH_ALEN];
-	unsigned char src_addr[ETH_ALEN];
-	unsigned short type;
-	} hdr;
-
-    unsigned char status;
-    unsigned i, retries;
-    unsigned long ct;
-
-    for (retries=0; retries < XMIT_RETRIES ; retries++)
-	{
-	/** Stall the download engine **/
-	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2);
-
-	/** Make sure the card is not waiting on us **/
-	inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
-	inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
-
-	while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) &
-	       INT_CMDINPROGRESS)
-	    ;
-
-	/** Set the ethernet packet type **/
-	hdr.type = htons(t);
-
-	/** Copy the destination address **/
-	memcpy(hdr.dst_addr, d, ETH_ALEN);
-
-	/** Copy our MAC address **/
-	memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN);
-
-	/** Setup the DPD (download descriptor) **/
-	INF_3C90X.TransmitDPD.DnNextPtr = 0;
-	/** set notification for transmission completion (bit 15) **/
-	INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000;
-	INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr);
-	INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr);
-	INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p);
-	INF_3C90X.TransmitDPD.DataLength = s + (1<<31);
-
-	/** Send the packet **/
-	outl(virt_to_bus(&(INF_3C90X.TransmitDPD)),
-	     INF_3C90X.IOAddr + regDnListPtr_l);
-
-	/** End Stall and Wait for upload to complete. **/
-	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3);
-	while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0)
-	    ;
-
-	/** Wait for NIC Transmit to Complete **/
-	ct = currticks();
-
-	while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) &&
-		ct + 10*1000 < currticks());
-		;
-
-	if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004))
-	    {
-	    printf("3C90X: Tx Timeout\n");
-	    continue;
-	    }
-
-	status = inb(INF_3C90X.IOAddr + regTxStatus_b);
-
-	/** acknowledge transmit interrupt by writing status **/
-	outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
-
-	/** successful completion (sans "interrupt Requested" bit) **/
-	if ((status & 0xbf) == 0x80)
-	    return;
-
-	   printf("3C90X: Status (%hhX)\n", status);
-	/** check error codes **/
-	if (status & 0x02)
-	    {
-	    printf("3C90X: Tx Reclaim Error (%hhX)\n", status);
-	    a3c90x_reset();
-	    }
-	else if (status & 0x04)
-	    {
-	    printf("3C90X: Tx Status Overflow (%hhX)\n", status);
-	    for (i=0; i<32; i++)
-		outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
-	    /** must re-enable after max collisions before re-issuing tx **/
-	    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
-	    }
-	else if (status & 0x08)
-	    {
-	    printf("3C90X: Tx Max Collisions (%hhX)\n", status);
-	    /** must re-enable after max collisions before re-issuing tx **/
-	    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
-	    }
-	else if (status & 0x10)
-	    {
-	    printf("3C90X: Tx Underrun (%hhX)\n", status);
-	    a3c90x_reset();
-	    }
-	else if (status & 0x20)
-	    {
-	    printf("3C90X: Tx Jabber (%hhX)\n", status);
-	    a3c90x_reset();
-	    }
-	else if ((status & 0x80) != 0x80)
-	    {
-	    printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n",
-	           status);
-	    a3c90x_reset();
-	    }
-	}
+	/* Issue the window command. */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
+				     cmdSelectRegisterWindow, window);
+	inf_3c90x->CurrentWindow = window;
 
-    /** failed after RETRY attempts **/
-    printf("Failed to send after %d retries\n", retries);
-    return;
+	return;
+}
 
-    }
+static void a3c90x_internal_WaitForEeprom(struct INF_3C90X *inf_3c90x)
+{
+	int cnt = 0;
 
+	DBGP("a3c90x_internal_WaitForEeprom\n");
 
+	while (eepromBusy & inw(inf_3c90x->IOAddr + regEepromCommand_0_w)) {
+		if (cnt == EEPROM_TIMEOUT) {
+			DBG("Read from eeprom failed: timeout\n");
+			return;
+		}
+		udelay(1);
+		cnt++;
+	}
+}
 
-/*** a3c90x_poll: exported routine that waits for a certain length of time
- *** for a packet, and if it sees none, returns 0.  This routine should
- *** copy the packet to nic->packet if it gets a packet and set the size
- *** in nic->packetlen.  Return 1 if a packet was found.
- ***/
+/**
+ * a3c90x_internal_ReadEeprom - nvs routine to read eeprom data
+ * We only support reading one word(2 byte). The nvs subsystem will make sure
+ * that the routine will never be called with len != 2.
+ *
+ * @v nvs	nvs data.
+ * @v address	eeprom address to read data from.
+ * @v data	data is put here.
+ * @v len	number of bytes to read.
+ */
 static int
-a3c90x_poll(struct nic *nic, int retrieve)
-    {
-    int errcode;
+a3c90x_internal_ReadEeprom(struct nvs_device *nvs, unsigned int address, void *data, size_t len)
+{
+	unsigned short *dest = (unsigned short *) data;
+	struct INF_3C90X *inf_3c90x =
+	    container_of(nvs, struct INF_3C90X, nvs);
+
+	DBGP("a3c90x_internal_ReadEeprom\n");
+
+	/* we support reading 2 bytes only */
+	assert(len == 2);
+
+	/* Select correct window */
+	a3c90x_internal_SetWindow(inf_3c90x, winEepromBios0);
+
+	/* set eepromRead bits in command sent to NIC */
+	address += (inf_3c90x->is3c556 ? eepromRead_556 : eepromRead);
+
+	a3c90x_internal_WaitForEeprom(inf_3c90x);
+	/* send address to NIC */
+	outw(address, inf_3c90x->IOAddr + regEepromCommand_0_w);
+	a3c90x_internal_WaitForEeprom(inf_3c90x);
+
+	/* read value */
+	*dest = inw(inf_3c90x->IOAddr + regEepromData_0_w);
 
-    if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010))
-	{
 	return 0;
+}
+
+/**
+ * a3c90x_internal_WriteEeprom - nvs routine to write eeprom data
+ * currently not implemented
+ *
+ * @v nvs	nvs data.
+ * @v address	eeprom address to read data from.
+ * @v data	data is put here.
+ * @v len	number of bytes to read.
+ */
+static int
+a3c90x_internal_WriteEeprom(struct nvs_device *nvs __unused,
+			    unsigned int address __unused,
+			    const void *data __unused, size_t len __unused)
+{
+	return -ENOTSUP;
+}
+
+static void a3c90x_internal_ReadEepromContents(struct INF_3C90X *inf_3c90x)
+{
+	int eeprom_size = (inf_3c90x->isBrev ? 0x20 : 0x17) * 2;
+
+	DBGP("a3c90x_internal_ReadEepromContents\n");
+
+	nvs_read(&inf_3c90x->nvs, 0, inf_3c90x->eeprom, eeprom_size);
+}
+
+/**
+ * a3c90x_reset: exported function that resets the card to its default
+ * state.  This is so the Linux driver can re-set the card up the way
+ * it wants to.  If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
+ * not alter the selected transceiver that we used to download the boot
+ * image.
+ *
+ * @v inf_3c90x	Private NIC data
+ */
+static void a3c90x_reset(struct INF_3C90X *inf_3c90x)
+{
+	DBGP("a3c90x_reset\n");
+	/* Send the reset command to the card */
+	DBG("3c90x: Issuing RESET\n");
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdGlobalReset, 0);
+
+	/* global reset command resets station mask, non-B revision cards
+	 * require explicit reset of values
+	 */
+	a3c90x_internal_SetWindow(inf_3c90x, winAddressing2);
+	outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 0);
+	outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 2);
+	outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 4);
+
+	/* Issue transmit reset, wait for command completion */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxReset, 0);
+
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxEnable, 0);
+
+	/*
+	 * reset of the receiver on B-revision cards re-negotiates the link
+	 * takes several seconds (a computer eternity)
+	 */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxReset,
+				     inf_3c90x->isBrev ? 0x04 : 0x00);
+
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxEnable, 0);
+
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
+				     cmdSetInterruptEnable, 0);
+	/* enable rxComplete and txComplete */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
+				     cmdSetIndicationEnable,
+				     INT_TXCOMPLETE | INT_UPCOMPLETE);
+	/* acknowledge any pending status flags */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
+				     cmdAcknowledgeInterrupt, 0x661);
+
+	return;
+}
+
+/**
+ * a3c90x_setup_tx_ring - Allocates TX ring, initialize tx_desc values
+ *
+ * @v p	Private NIC data
+ *
+ * @ret Returns 0 on success, negative on failure
+ */
+static int a3c90x_setup_tx_ring(struct INF_3C90X *p)
+{
+	DBGP("a3c90x_setup_tx_ring\n");
+	p->tx_ring =
+	    malloc_dma(TX_RING_SIZE * sizeof(struct TXD), TX_RING_ALIGN);
+
+	if (!p->tx_ring) {
+		DBG("Could not allocate TX-ring\n");
+		return -ENOMEM;
 	}
 
-    if ( ! retrieve ) return 1;
-
-    /** we don't need to acknowledge rxComplete -- the upload engine
-     ** does it for us.
-     **/
-
-    /** Build the up-load descriptor **/
-    INF_3C90X.ReceiveUPD.UpNextPtr = 0;
-    INF_3C90X.ReceiveUPD.UpPktStatus = 0;
-    INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet);
-    INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31);
-
-    /** Submit the upload descriptor to the NIC **/
-    outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)),
-         INF_3C90X.IOAddr + regUpListPtr_l);
-
-    /** Wait for upload completion (upComplete(15) or upError (14)) **/
-    mdelay(1);
-    while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0)
-    	mdelay(1);
-
-    /** Check for Error (else we have good packet) **/
-    if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14))
-	{
-	errcode = INF_3C90X.ReceiveUPD.UpPktStatus;
-	if (errcode & (1<<16))
-	    printf("3C90X: Rx Overrun (%hX)\n",errcode>>16);
-	else if (errcode & (1<<17))
-	    printf("3C90X: Runt Frame (%hX)\n",errcode>>16);
-	else if (errcode & (1<<18))
-	    printf("3C90X: Alignment Error (%hX)\n",errcode>>16);
-	else if (errcode & (1<<19))
-	    printf("3C90X: CRC Error (%hX)\n",errcode>>16);
-	else if (errcode & (1<<20))
-	    printf("3C90X: Oversized Frame (%hX)\n",errcode>>16);
-	else
-	    printf("3C90X: Packet error (%hX)\n",errcode>>16);
+	memset(p->tx_ring, 0, TX_RING_SIZE * sizeof(struct TXD));
+	p->tx_cur = 0;
+	p->tx_cnt = 0;
+	p->tx_tail = 0;
+
 	return 0;
-	}
+}
 
-    /** Ok, got packet.  Set length in nic->packetlen. **/
-    nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF);
+/**
+ * a3c90x_process_tx_packets - Checks for successfully sent packets,
+ * reports them to gPXE with netdev_tx_complete();
+ *
+ * @v netdev	Network device info
+ */
+static void a3c90x_process_tx_packets(struct net_device *netdev)
+{
+	struct INF_3C90X *p = netdev_priv(netdev);
+	unsigned int downlist_ptr;
 
-    return 1;
-    }
+	DBGP("a3c90x_process_tx_packets\n");
 
+	DBG("    tx_cnt: %d\n", p->tx_cnt);
 
+	while (p->tx_tail != p->tx_cur) {
 
-/*** a3c90x_disable: exported routine to disable the card.  What's this for?
- *** the eepro100.c driver didn't have one, so I just left this one empty too.
- *** Ideas anyone?
- *** Must turn off receiver at least so stray packets will not corrupt memory
- *** [Ken]
- ***/
-static void
-a3c90x_disable ( struct nic *nic __unused ) {
-	a3c90x_reset();
-	/* Disable the receiver and transmitter. */
-	outw(cmdRxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
-	outw(cmdTxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
+		downlist_ptr = inl(p->IOAddr + regDnListPtr_l);
+
+		DBG("    downlist_ptr: %#08x\n", downlist_ptr);
+		DBG("    tx_tail: %d tx_cur: %d\n", p->tx_tail, p->tx_cur);
+
+		/* NIC is currently working on this tx desc */
+		if(downlist_ptr == virt_to_bus(p->tx_ring + p->tx_tail))
+			return;
+
+		netdev_tx_complete(netdev, p->tx_iobuf[p->tx_tail]);
+
+		DBG("transmitted packet\n");
+		DBG("    size: %zd\n", iob_len(p->tx_iobuf[p->tx_tail]));
+
+		p->tx_tail = (p->tx_tail + 1) % TX_RING_SIZE;
+		p->tx_cnt--;
+	}
 }
 
-static void a3c90x_irq(struct nic *nic __unused, irq_action_t action __unused)
+static void a3c90x_free_tx_ring(struct INF_3C90X *p)
 {
-  switch ( action ) {
-  case DISABLE :
-    break;
-  case ENABLE :
-    break;
-  case FORCE :
-    break;
-  }
+	DBGP("a3c90x_free_tx_ring\n");
+
+	free_dma(p->tx_ring, TX_RING_SIZE * sizeof(struct TXD));
+	p->tx_ring = NULL;
+	/* io_buffers are free()ed by netdev_tx_complete[,_err]() */
 }
 
-/*** a3c90x_probe: exported routine to probe for the 3c905 card and perform
- *** initialization.  If this routine is called, the pci functions did find the
- *** card.  We just have to init it here.
- ***/
-static int a3c90x_probe ( struct nic *nic, struct pci_device *pci ) {
-
-    int i, c;
-    unsigned short eeprom[0x21];
-    unsigned int cfg;
-    unsigned int mopt;
-    unsigned int mstat;
-    unsigned short linktype;
-#define	HWADDR_OFFSET	10
-    
-    if (pci->ioaddr == 0)
-          return 0;
-
-    adjust_pci_device(pci);
-
-    nic->ioaddr = pci->ioaddr;
-    nic->irqno = 0;
-
-    INF_3C90X.is3c556 = (pci->device == 0x6055);
-    INF_3C90X.IOAddr = pci->ioaddr & ~3;
-    INF_3C90X.CurrentWindow = 255;
-    switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03))
-	{
-	case 0x9000: /** 10 Base TPO             **/
-	case 0x9001: /** 10/100 T4               **/
-	case 0x9050: /** 10/100 TPO              **/
-	case 0x9051: /** 10 Base Combo           **/
-		INF_3C90X.isBrev = 0;
-		break;
+/**
+ * a3c90x_transmit - Transmits a packet.
+ *
+ * @v netdev	Network device info
+ * @v iob		io_buffer containing the data to be send
+ *
+ * @ret	Returns 0 on success, negative on failure
+ */
+static int a3c90x_transmit(struct net_device *netdev,
+			   struct io_buffer *iob)
+{
+	struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
+	struct TXD *tx_cur_desc;
+	struct TXD *tx_prev_desc;
 
-	case 0x9004: /** 10 Base TPO             **/
-	case 0x9005: /** 10 Base Combo           **/
-	case 0x9006: /** 10 Base TPO and Base2   **/
-	case 0x900A: /** 10 Base FL              **/
-	case 0x9055: /** 10/100 TPO              **/
-	case 0x9056: /** 10/100 T4               **/
-	case 0x905A: /** 10 Base FX              **/
-	default:
-		INF_3C90X.isBrev = 1;
-		break;
+	unsigned int len;
+	unsigned int downlist_ptr;
+
+	DBGP("a3c90x_transmit\n");
+
+	if (inf_3c90x->tx_cnt == TX_RING_SIZE) {
+		DBG("TX-Ring overflow\n");
+		return -ENOBUFS;
 	}
 
-    /** Load the EEPROM contents **/
-    if (INF_3C90X.isBrev)
-	{
-	for(i=0;i<=0x20;i++)
-	    {
-	    eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
-	    }
-
-#ifdef	CFG_3C90X_BOOTROM_FIX
-	/** Set xcvrSelect in InternalConfig in eeprom. **/
-	/* only necessary for 3c905b revision cards with boot PROM bug!!! */
-	a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160);
-#endif
-
-#ifdef	CFG_3C90X_XCVR
-	if (CFG_3C90X_XCVR == 255)
-	    {
-	    /** Clear the LanWorks register **/
-	    a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0);
-	    }
-	else
-	    {
-	    /** Set the selected permanent-xcvrSelect in the
-	     ** LanWorks register
-	     **/
-	    a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16,
-	                    XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F));
-	    }
-#endif
+	inf_3c90x->tx_iobuf[inf_3c90x->tx_cur] = iob;
+	tx_cur_desc = inf_3c90x->tx_ring + inf_3c90x->tx_cur;
+
+	tx_prev_desc = inf_3c90x->tx_ring +
+	    (((inf_3c90x->tx_cur + TX_RING_SIZE) - 1) % TX_RING_SIZE);
+
+	len = iob_len(iob);
+
+	/* Setup the DPD (download descriptor) */
+	tx_cur_desc->DnNextPtr = 0;
+
+	/* FrameStartHeader differs in 90x and >= 90xB
+	 * It contains length in 90x and a round up boundary and packet ID for
+	 * 90xB and 90xC. We can leave this to 0 for 90xB and 90xC.
+	 */
+	tx_cur_desc->FrameStartHeader =
+	    fshTxIndicate | (inf_3c90x->isBrev ? 0x00 : len);
+
+	tx_cur_desc->DataAddr = virt_to_bus(iob->data);
+	tx_cur_desc->DataLength = len | downLastFrag;
+
+	/* We have to stall the download engine, so the NIC won't access the
+	 * tx descriptor while we modify it. There is a way around this
+	 * from revision B and upwards. To stay compatible with older revisions
+	 * we don't use it here.
+	 */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdStallCtl,
+				     dnStall);
+
+	tx_prev_desc->DnNextPtr = virt_to_bus(tx_cur_desc);
+
+	downlist_ptr = inl(inf_3c90x->IOAddr + regDnListPtr_l);
+	if (downlist_ptr == 0) {
+		/* currently no DownList, sending a new one */
+		outl(virt_to_bus(tx_cur_desc),
+		     inf_3c90x->IOAddr + regDnListPtr_l);
 	}
-    else
-	{
-	for(i=0;i<=0x17;i++)
-	    {
-	    eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
-	    }
+
+	/* End Stall */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdStallCtl,
+				     dnUnStall);
+
+	inf_3c90x->tx_cur = (inf_3c90x->tx_cur + 1) % TX_RING_SIZE;
+	inf_3c90x->tx_cnt++;
+
+	return 0;
+}
+
+/**
+ * a3c90x_prepare_rx_desc - fills the rx desc with initial data
+ *
+ * @v p		NIC private data
+ * @v index	Index for rx_iobuf and rx_ring array
+ */
+
+static void a3c90x_prepare_rx_desc(struct INF_3C90X *p, unsigned int index)
+{
+	DBGP("a3c90x_prepare_rx_desc\n");
+	DBG("Populating rx_desc %d\n", index);
+
+	/* We have to stall the upload engine, so the NIC won't access the
+	 * rx descriptor while we modify it. There is a way around this
+	 * from revision B and upwards. To stay compatible with older revisions
+	 * we don't use it here.
+	 */
+	a3c90x_internal_IssueCommand(p->IOAddr, cmdStallCtl, upStall);
+
+	p->rx_ring[index].DataAddr = virt_to_bus(p->rx_iobuf[index]->data);
+	p->rx_ring[index].DataLength = RX_BUF_SIZE | upLastFrag;
+	p->rx_ring[index].UpPktStatus = 0;
+
+	/* unstall upload engine */
+	a3c90x_internal_IssueCommand(p->IOAddr, cmdStallCtl, upUnStall);
+}
+
+/**
+ * a3c90x_refill_rx_ring -checks every entry in the rx ring and reallocates
+ * them as necessary. Then it calls a3c90x_prepare_rx_desc to fill the rx desc
+ * with initial data.
+ *
+ * @v p		NIC private data
+ */
+static void a3c90x_refill_rx_ring(struct INF_3C90X *p)
+{
+	int i;
+	unsigned int status;
+	struct RXD *rx_cur_desc;
+
+	DBGP("a3c90x_refill_rx_ring\n");
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		rx_cur_desc = p->rx_ring + i;
+		status = rx_cur_desc->UpPktStatus;
+
+		/* only refill used descriptor */
+		if (!(status & upComplete))
+			continue;
+
+		/* we still need to process this descriptor */
+		if (p->rx_iobuf[i] != NULL)
+			continue;
+
+		p->rx_iobuf[i] = alloc_iob(RX_BUF_SIZE);
+		if (p->rx_iobuf[i] == NULL) {
+			DBG("alloc_iob() failed\n");
+			break;
+		}
+
+		a3c90x_prepare_rx_desc(p, i);
 	}
+}
 
-    /** Print identification message **/
-    printf("\n\n3C90X Driver 2.02 "
-           "Copyright 1999 LightSys Technology Services, Inc.\n"
-           "Portions Copyright 1999 Steve Smith\n");
-    printf("Provided with ABSOLUTELY NO WARRANTY.\n");
-#ifdef	CFG_3C90X_BOOTROM_FIX
-    if (INF_3C90X.isBrev)
-        {
-        printf("NOTE: 3c905b bootrom fix enabled; has side "
-	   "effects.  See 3c90x.txt for info.\n");
+/**
+ * a3c90x_setup_rx_ring - Allocates RX ring, initialize rx_desc values
+ *
+ * @v p	Private NIC data
+ *
+ * @ret Returns 0 on success, negative on failure
+ */
+static int a3c90x_setup_rx_ring(struct INF_3C90X *p)
+{
+	int i;
+
+	DBGP("a3c90x_setup_rx_ring\n");
+
+	p->rx_ring =
+	    malloc_dma(RX_RING_SIZE * sizeof(struct RXD), RX_RING_ALIGN);
+
+	if (!p->rx_ring) {
+		DBG("Could not allocate RX-ring\n");
+		return -ENOMEM;
 	}
-#endif
-    printf("-------------------------------------------------------"
-           "------------------------\n");
-
-    /** Retrieve the Hardware address and print it on the screen. **/
-    INF_3C90X.HWAddr[0] = eeprom[HWADDR_OFFSET + 0]>>8;
-    INF_3C90X.HWAddr[1] = eeprom[HWADDR_OFFSET + 0]&0xFF;
-    INF_3C90X.HWAddr[2] = eeprom[HWADDR_OFFSET + 1]>>8;
-    INF_3C90X.HWAddr[3] = eeprom[HWADDR_OFFSET + 1]&0xFF;
-    INF_3C90X.HWAddr[4] = eeprom[HWADDR_OFFSET + 2]>>8;
-    INF_3C90X.HWAddr[5] = eeprom[HWADDR_OFFSET + 2]&0xFF;
-
-    DBG ( "MAC Address = %s\n", eth_ntoa ( INF_3C90X.HWAddr ) );
-
-    /** 3C556: Invert MII power **/
-    if (INF_3C90X.is3c556) {
-	unsigned int tmp;
-	a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
-	tmp = inw(INF_3C90X.IOAddr + regResetOptions_2_w);
-	tmp |= 0x4000;
-	outw(tmp, INF_3C90X.IOAddr + regResetOptions_2_w);
-    }
-
-    /* Test if the link is good, if not continue */
-    a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winDiagnostics4);
-    mstat = inw(INF_3C90X.IOAddr + regMediaStatus_4_w);
-    if((mstat & (1<<11)) == 0) {
-	printf("Valid link not established\n");
-	return 0;
-    }
-
-    /** Program the MAC address into the station address registers **/
-    a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
-    outw(htons(eeprom[HWADDR_OFFSET + 0]), INF_3C90X.IOAddr + regStationAddress_2_3w);
-    outw(htons(eeprom[HWADDR_OFFSET + 1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2);
-    outw(htons(eeprom[HWADDR_OFFSET + 2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4);
-    outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
-    outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
-    outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
-
-    /** Fill in our entry in the etherboot arp table **/
-    for(i=0;i<ETH_ALEN;i++)
-	nic->node_addr[i] = (eeprom[HWADDR_OFFSET + i/2] >> (8*((i&1)^1))) & 0xff;
-
-    /** Read the media options register, print a message and set default
-     ** xcvr.
-     **
-     ** Uses Media Option command on B revision, Reset Option on non-B
-     ** revision cards -- same register address
-     **/
-    a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
-    mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w);
-
-    /** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/
-    if (! INF_3C90X.isBrev)
-	{
-	mopt &= 0x7F;
+
+	p->rx_cur = 0;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		p->rx_ring[i].UpNextPtr =
+		    virt_to_bus(p->rx_ring + (i + 1));
+
+		/* these are needed so refill_rx_ring initializes the ring */
+		p->rx_ring[i].UpPktStatus = upComplete;
+		p->rx_iobuf[i] = NULL;
 	}
 
-    printf("Connectors present: ");
-    c = 0;
-    linktype = 0x0008;
-    if (mopt & 0x01)
-	{
-	printf("%s100Base-T4",(c++)?", ":"");
-	linktype = 0x0006;
+	/* Loop the ring */
+	p->rx_ring[i - 1].UpNextPtr = virt_to_bus(p->rx_ring);
+
+	a3c90x_refill_rx_ring(p);
+
+	return 0;
+}
+
+static void a3c90x_free_rx_ring(struct INF_3C90X *p)
+{
+	DBGP("a3c90x_free_rx_ring\n");
+
+	free_dma(p->rx_ring, RX_RING_SIZE * sizeof(struct RXD));
+	p->rx_ring = NULL;
+}
+
+static void a3c90x_free_rx_iobuf(struct INF_3C90X *p)
+{
+	int i;
+
+	DBGP("a3c90x_free_rx_iobuf\n");
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		free_iob(p->rx_iobuf[i]);
+		p->rx_iobuf[i] = NULL;
 	}
-    if (mopt & 0x04)
-	{
-	printf("%s100Base-FX",(c++)?", ":"");
-	linktype = 0x0005;
+}
+
+/**
+ * a3c90x_process_rx_packets - Checks for received packets,
+ * reports them to gPXE with netdev_rx() or netdev_rx_err() if there was an
+ * error while receiving the packet
+ *
+ * @v netdev	Network device info
+ */
+static void a3c90x_process_rx_packets(struct net_device *netdev)
+{
+	int i;
+	unsigned int rx_status;
+	struct INF_3C90X *p = netdev_priv(netdev);
+	struct RXD *rx_cur_desc;
+
+	DBGP("a3c90x_process_rx_packets\n");
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		rx_cur_desc = p->rx_ring + p->rx_cur;
+		rx_status = rx_cur_desc->UpPktStatus;
+
+		if (!(rx_status & upComplete) && !(rx_status & upError))
+			break;
+
+		if (p->rx_iobuf[p->rx_cur] == NULL)
+			break;
+
+		if (rx_status & upError) {
+			DBG("Corrupted packet received\n");
+			netdev_rx_err(netdev, p->rx_iobuf[p->rx_cur],
+				      -EINVAL);
+		} else {
+			/* if we're here, we've got good packet */
+			int packet_len;
+
+			packet_len = rx_status & 0x1FFF;
+			iob_put(p->rx_iobuf[p->rx_cur], packet_len);
+
+			DBG("received packet\n");
+			DBG("    size: %d\n", packet_len);
+
+			netdev_rx(netdev, p->rx_iobuf[p->rx_cur]);
+		}
+
+		p->rx_iobuf[p->rx_cur] = NULL;	/* invalidate rx desc */
+		p->rx_cur = (p->rx_cur + 1) % RX_RING_SIZE;
 	}
-    if (mopt & 0x10)
-	{
-	printf("%s10Base-2",(c++)?", ":"");
-	linktype = 0x0003;
+	a3c90x_refill_rx_ring(p);
+
+}
+
+/**
+ * a3c90x_poll - Routine that gets called periodically.
+ * Here we hanle transmitted and received packets.
+ * We could also check the link status from time to time, which we
+ * currently don't do.
+ *
+ * @v netdev	Network device info
+ */
+static void a3c90x_poll(struct net_device *netdev)
+{
+	struct INF_3C90X *p = netdev_priv(netdev);
+	uint16_t raw_status, int_status;
+
+	DBGP("a3c90x_poll\n");
+
+	raw_status = inw(p->IOAddr + regCommandIntStatus_w);
+	int_status = (raw_status & 0x0FFF);
+
+	if ( int_status == 0 )
+		return;
+
+	a3c90x_internal_IssueCommand(p->IOAddr, cmdAcknowledgeInterrupt,
+				     int_status);
+
+	if (int_status & INT_TXCOMPLETE)
+		outb(0x00, p->IOAddr + regTxStatus_b);
+
+	DBG("poll: status = %#04x\n", raw_status);
+
+	a3c90x_process_tx_packets(netdev);
+
+	a3c90x_process_rx_packets(netdev);
+}
+
+
+
+static void a3c90x_free_resources(struct INF_3C90X *p)
+{
+	DBGP("a3c90x_free_resources\n");
+
+	a3c90x_free_tx_ring(p);
+	a3c90x_free_rx_ring(p);
+	a3c90x_free_rx_iobuf(p);
+}
+
+/**
+ * a3c90x_remove - Routine to remove the card. Unregisters
+ * the NIC from gPXE, disables RX/TX and resets the card.
+ *
+ * @v pci	PCI device info
+ */
+static void a3c90x_remove(struct pci_device *pci)
+{
+	struct net_device *netdev = pci_get_drvdata(pci);
+	struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
+
+	DBGP("a3c90x_remove\n");
+
+	a3c90x_reset(inf_3c90x);
+
+	/* Disable the receiver and transmitter. */
+	outw(cmdRxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w);
+	outw(cmdTxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w);
+
+	unregister_netdev(netdev);
+	netdev_nullify(netdev);
+	netdev_put(netdev);
+}
+
+static void a3c90x_irq(struct net_device *netdev, int enable)
+{
+	struct INF_3C90X *p = netdev_priv(netdev);
+
+	DBGP("a3c90x_irq\n");
+
+	if (enable == 0) {
+		/* disable interrupts */
+		a3c90x_internal_IssueCommand(p->IOAddr,
+					     cmdSetInterruptEnable, 0);
+	} else {
+		a3c90x_internal_IssueCommand(p->IOAddr,
+					     cmdSetInterruptEnable,
+					     INT_TXCOMPLETE |
+					     INT_UPCOMPLETE);
+		a3c90x_internal_IssueCommand(p->IOAddr,
+					     cmdAcknowledgeInterrupt,
+					     0x661);
 	}
-    if (mopt & 0x20)
-	{
-	printf("%sAUI",(c++)?", ":"");
-	linktype = 0x0001;
+}
+
+/**
+ * a3c90x_hw_start - Initialize hardware, copy MAC address
+ * to NIC registers, set default receiver
+ */
+static void a3c90x_hw_start(struct net_device *netdev)
+{
+	int i, c;
+	unsigned int cfg;
+	unsigned int mopt;
+	unsigned short linktype;
+	struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
+
+	DBGP("a3c90x_hw_start\n");
+
+	/* 3C556: Invert MII power */
+	if (inf_3c90x->is3c556) {
+		unsigned int tmp;
+		a3c90x_internal_SetWindow(inf_3c90x, winAddressing2);
+		tmp = inw(inf_3c90x->IOAddr + regResetOptions_2_w);
+		tmp |= 0x4000;
+		outw(tmp, inf_3c90x->IOAddr + regResetOptions_2_w);
 	}
-    if (mopt & 0x40)
-	{
-	printf("%sMII",(c++)?", ":"");
-	linktype = 0x0006;
+
+	/* Copy MAC address into the NIC registers */
+	a3c90x_internal_SetWindow(inf_3c90x, winAddressing2);
+	for (i = 0; i < ETH_ALEN; i++)
+		outb(netdev->ll_addr[i],
+		     inf_3c90x->IOAddr + regStationAddress_2_3w + i);
+	for (i = 0; i < ETH_ALEN; i++)
+		outb(0, inf_3c90x->IOAddr + regStationMask_2_3w + i);
+
+	/* Read the media options register, print a message and set default
+	* xcvr.
+	*
+	* Uses Media Option command on B revision, Reset Option on non-B
+	* revision cards -- same register address
+	*/
+	a3c90x_internal_SetWindow(inf_3c90x, winTxRxOptions3);
+	mopt = inw(inf_3c90x->IOAddr + regResetMediaOptions_3_w);
+
+	/* mask out VCO bit that is defined as 10baseFL bit on B-rev cards */
+	if (!inf_3c90x->isBrev) {
+		mopt &= 0x7F;
 	}
-    if ((mopt & 0xA) == 0xA)
-	{
-	printf("%s10Base-T / 100Base-TX",(c++)?", ":"");
+
+	DBG("Connectors present: ");
+	c = 0;
 	linktype = 0x0008;
+	if (mopt & 0x01) {
+		DBG("%s100Base-T4", (c++) ? ", " : "");
+		linktype = linkMII;
 	}
-    else if ((mopt & 0xA) == 0x2)
-	{
-	printf("%s100Base-TX",(c++)?", ":"");
-	linktype = 0x0008;
+	if (mopt & 0x04) {
+		DBG("%s100Base-FX", (c++) ? ", " : "");
+		linktype = link100BaseFX;
 	}
-    else if ((mopt & 0xA) == 0x8)
-	{
-	printf("%s10Base-T",(c++)?", ":"");
-	linktype = 0x0008;
+	if (mopt & 0x10) {
+		DBG("%s10Base-2", (c++) ? ", " : "");
+		linktype = link10Base2;
 	}
-    printf(".\n");
-
-    /** Determine transceiver type to use, depending on value stored in
-     ** eeprom 0x16
-     **/
-    if (INF_3C90X.isBrev)
-	{
-	if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC)
-	    {
-	    /** User-defined **/
-	    linktype = eeprom[0x16] & 0x000F;
-	    }
+	if (mopt & 0x20) {
+		DBG("%sAUI", (c++) ? ", " : "");
+		linktype = linkAUI;
 	}
-    else
-	{
-#ifdef	CFG_3C90X_XCVR
-	    if (CFG_3C90X_XCVR != 255)
-		linktype = CFG_3C90X_XCVR;
-#endif	/* CFG_3C90X_XCVR */
-
-	    /** I don't know what MII MAC only mode is!!! **/
-	    if (linktype == 0x0009)
-		{
-		if (INF_3C90X.isBrev)
-			printf("WARNING: MII External MAC Mode only supported on B-revision "
-			       "cards!!!!\nFalling Back to MII Mode\n");
-		linktype = 0x0006;
+	if (mopt & 0x40) {
+		DBG("%sMII", (c++) ? ", " : "");
+		linktype = linkMII;
+	}
+	if ((mopt & 0xA) == 0xA) {
+		DBG("%s10Base-T / 100Base-TX", (c++) ? ", " : "");
+		linktype = linkAutoneg;
+	} else if ((mopt & 0xA) == 0x2) {
+		DBG("%s100Base-TX", (c++) ? ", " : "");
+		linktype = linkAutoneg;
+	} else if ((mopt & 0xA) == 0x8) {
+		DBG("%s10Base-T", (c++) ? ", " : "");
+		linktype = linkAutoneg;
+	}
+	DBG(".\n");
+
+	/* Determine transceiver type to use, depending on value stored in
+	* eeprom 0x16
+	*/
+	if (inf_3c90x->isBrev) {
+		if ((inf_3c90x->eeprom[0x16] & 0xFF00) == XCVR_MAGIC) {
+			/* User-defined */
+			linktype = inf_3c90x->eeprom[0x16] & 0x000F;
 		}
+	} else {
+		/* I don't know what MII MAC only mode is!!! */
+		if (linktype == linkExternalMII) {
+			if (inf_3c90x->isBrev)
+				DBG("WARNING: MII External MAC Mode only supported on B-revision " "cards!!!!\nFalling Back to MII Mode\n");
+			linktype = linkMII;
+		}
+	}
+
+	/* enable DC converter for 10-Base-T */
+	if (linktype == link10Base2) {
+		a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
+					     cmdEnableDcConverter, 0);
+	}
+
+	/* Set the link to the type we just determined. */
+	a3c90x_internal_SetWindow(inf_3c90x, winTxRxOptions3);
+	cfg = inl(inf_3c90x->IOAddr + regInternalConfig_3_l);
+	cfg &= ~(0xF << 20);
+	cfg |= (linktype << 20);
+
+	DBG("Setting internal cfg register: 0x%08X (linktype: 0x%02X)\n",
+	    cfg, linktype);
+
+	outl(cfg, inf_3c90x->IOAddr + regInternalConfig_3_l);
+
+	/* Now that we set the xcvr type, reset the Tx and Rx */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxReset, 0x00);
+
+	if (!inf_3c90x->isBrev)
+		outb(0x01, inf_3c90x->IOAddr + regTxFreeThresh_b);
+
+	/* Set the RX filter = receive only individual pkts & multicast & bcast. */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdSetRxFilter,
+				     0x01 + 0x02 + 0x04);
+
+
+	/*
+	* set Indication and Interrupt flags , acknowledge any IRQ's
+	*/
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
+				     cmdSetInterruptEnable,
+	 INT_TXCOMPLETE | INT_UPCOMPLETE);
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
+				     cmdSetIndicationEnable,
+	 INT_TXCOMPLETE | INT_UPCOMPLETE);
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
+				     cmdAcknowledgeInterrupt, 0x661);
+}
+
+/**
+ * a3c90x_open - Routine to initialize the card. Initialize hardware,
+ * allocate TX and RX ring, send RX ring address to the NIC.
+ *
+ * @v netdev	Network device info
+ *
+ * @ret Returns 0 on success, negative on failure
+ */
+static int a3c90x_open(struct net_device *netdev)
+{
+	int rc;
+	struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
+
+	DBGP("a3c90x_open\n");
+
+	a3c90x_hw_start(netdev);
+
+	rc = a3c90x_setup_tx_ring(inf_3c90x);
+	if (rc != 0) {
+		DBG("Error setting up TX Ring\n");
+		goto error;
 	}
 
-    /** enable DC converter for 10-Base-T **/
-    if (linktype == 0x0003)
-	{
-	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
+	rc = a3c90x_setup_rx_ring(inf_3c90x);
+	if (rc != 0) {
+		DBG("Error setting up RX Ring\n");
+		goto error;
 	}
 
-    /** Set the link to the type we just determined. **/
-    a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
-    cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
-    cfg &= ~(0xF<<20);
-    cfg |= (linktype<<20);
-    outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
-
-    /** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00);
-    while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
-	;
-
-    if (!INF_3C90X.isBrev)
-	outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
-
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
-
-    /**
-     ** reset of the receiver on B-revision cards re-negotiates the link
-     ** takes several seconds (a computer eternity)
-     **/
-    if (INF_3C90X.isBrev)
-	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
-    else
-	a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
-    while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
-	;
-
-    /** Set the RX filter = receive only individual pkts & multicast & bcast. **/
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x02 + 0x04);
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
-
-
-    /**
-     ** set Indication and Interrupt flags , acknowledge any IRQ's
-     **/
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0);
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
-                                 cmdSetIndicationEnable, 0x0014);
-    a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
-                                 cmdAcknowledgeInterrupt, 0x661);
-
-    /** Set our exported functions **/
-    nic->nic_op	= &a3c90x_operations;
-    return 1;
+	/* send rx_ring address to NIC */
+	outl(virt_to_bus(inf_3c90x->rx_ring),
+	     inf_3c90x->IOAddr + regUpListPtr_l);
+
+	/* enable packet transmission and reception */
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxEnable, 0);
+	a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxEnable, 0);
+
+	return 0;
+
+      error:
+	a3c90x_free_resources(inf_3c90x);
+	a3c90x_reset(inf_3c90x);
+	return rc;
 }
 
-static struct nic_operations a3c90x_operations = {
-	.connect	= dummy_connect,
-	.poll		= a3c90x_poll,
-	.transmit	= a3c90x_transmit,
-	.irq		= a3c90x_irq,
+/**
+ * a3c90x_close - free()s TX and RX ring, disablex RX/TX, resets NIC
+ *
+ * @v netdev	Network device info
+ */
+static void a3c90x_close(struct net_device *netdev)
+{
+	struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
+
+	DBGP("a3c90x_close\n");
 
+	a3c90x_reset(inf_3c90x);
+	outw(cmdRxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w);
+	outw(cmdTxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w);
+	a3c90x_free_resources(inf_3c90x);
+}
+
+static struct net_device_operations a3c90x_operations = {
+	.open = a3c90x_open,
+	.close = a3c90x_close,
+	.poll = a3c90x_poll,
+	.transmit = a3c90x_transmit,
+	.irq = a3c90x_irq,
 };
 
+/**
+ * a3c90x_probe: exported routine to probe for the 3c905 card.
+ * If this routine is called, the pci functions did find the
+ * card.  We read the eeprom here and get the MAC address.
+ * Initialization is done in a3c90x_open().
+ *
+ * @v pci	PCI device info
+ * @ pci_id	PCI device IDs
+ *
+ * @ret rc	Returns 0 on success, negative on failure
+ */
+static int a3c90x_probe(struct pci_device *pci,
+			const struct pci_device_id *pci_id __unused)
+{
+
+	struct net_device *netdev;
+	struct INF_3C90X *inf_3c90x;
+	unsigned char *HWAddr;
+	int rc;
+
+	DBGP("a3c90x_probe\n");
+
+	if (pci->ioaddr == 0)
+		return -EINVAL;
+
+	netdev = alloc_etherdev(sizeof(*inf_3c90x));
+	if (!netdev)
+		return -ENOMEM;
+
+	netdev_init(netdev, &a3c90x_operations);
+	pci_set_drvdata(pci, netdev);
+	netdev->dev = &pci->dev;
+
+	inf_3c90x = netdev_priv(netdev);
+	memset(inf_3c90x, 0, sizeof(*inf_3c90x));
+
+	adjust_pci_device(pci);
+
+	inf_3c90x->is3c556 = (pci->device == 0x6055);
+	inf_3c90x->IOAddr = pci->ioaddr;
+	inf_3c90x->CurrentWindow = winNone;
+
+	inf_3c90x->isBrev = 1;
+	switch (pci->device) {
+	case 0x9000:		/* 10 Base TPO             */
+	case 0x9001:		/* 10/100 T4               */
+	case 0x9050:		/* 10/100 TPO              */
+	case 0x9051:		/* 10 Base Combo           */
+		inf_3c90x->isBrev = 0;
+		break;
+	}
+
+	DBG("[3c90x]: found NIC(0x%04X, 0x%04X), isBrev=%d, is3c556=%d\n",
+	    pci->vendor, pci->device, inf_3c90x->isBrev,
+	    inf_3c90x->is3c556);
+
+	/* initialize nvs device */
+	inf_3c90x->nvs.word_len_log2 = 1;	/* word */
+	inf_3c90x->nvs.size = (inf_3c90x->isBrev ? 0x20 : 0x17);
+	inf_3c90x->nvs.block_size = 1;
+	inf_3c90x->nvs.read = a3c90x_internal_ReadEeprom;
+	inf_3c90x->nvs.write = a3c90x_internal_WriteEeprom;
+
+	/* reset NIC before accessing any data from it */
+	a3c90x_reset(inf_3c90x);
+
+	/* load eeprom contents to inf_3c90x->eeprom */
+	a3c90x_internal_ReadEepromContents(inf_3c90x);
+
+	HWAddr = netdev->hw_addr;
+
+	/* Retrieve the Hardware address */
+	HWAddr[0] = inf_3c90x->eeprom[eepromHwAddrOffset + 0] >> 8;
+	HWAddr[1] = inf_3c90x->eeprom[eepromHwAddrOffset + 0] & 0xFF;
+	HWAddr[2] = inf_3c90x->eeprom[eepromHwAddrOffset + 1] >> 8;
+	HWAddr[3] = inf_3c90x->eeprom[eepromHwAddrOffset + 1] & 0xFF;
+	HWAddr[4] = inf_3c90x->eeprom[eepromHwAddrOffset + 2] >> 8;
+	HWAddr[5] = inf_3c90x->eeprom[eepromHwAddrOffset + 2] & 0xFF;
+
+	/* we don't handle linkstates yet, so we're always up */
+	netdev_link_up(netdev);
+
+	if ((rc = register_netdev(netdev)) != 0) {
+		DBG("3c90x: register_netdev() failed\n");
+		netdev_put(netdev);
+		return rc;
+	}
+
+	return 0;
+}
+
 static struct pci_device_id a3c90x_nics[] = {
 /* Original 90x revisions: */
-PCI_ROM(0x10b7, 0x6055, "3c556",	 "3C556"),		/* Huricane */
-PCI_ROM(0x10b7, 0x9000, "3c905-tpo",     "3Com900-TPO"),	/* 10 Base TPO */
-PCI_ROM(0x10b7, 0x9001, "3c905-t4",      "3Com900-Combo"),	/* 10/100 T4 */
-PCI_ROM(0x10b7, 0x9050, "3c905-tpo100",  "3Com905-TX"),		/* 100 Base TX / 10/100 TPO */
-PCI_ROM(0x10b7, 0x9051, "3c905-combo",   "3Com905-T4"),		/* 100 Base T4 / 10 Base Combo */
+	PCI_ROM(0x10b7, 0x6055, "3c556", "3C556", 0),	/* Huricane */
+	PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO", 0),	/* 10 Base TPO */
+	PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo", 0),	/* 10/100 T4 */
+	PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX", 0),	/* 100 Base TX / 10/100 TPO */
+	PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4", 0),	/* 100 Base T4 / 10 Base Combo */
 /* Newer 90xB revisions: */
-PCI_ROM(0x10b7, 0x9004, "3c905b-tpo",    "3Com900B-TPO"),	/* 10 Base TPO */
-PCI_ROM(0x10b7, 0x9005, "3c905b-combo",  "3Com900B-Combo"),	/* 10 Base Combo */
-PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2",   "3Com900B-2/T"),	/* 10 Base TP and Base2 */
-PCI_ROM(0x10b7, 0x900a, "3c905b-fl",     "3Com900B-FL"),	/* 10 Base FL */
-PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX"),	/* 10/100 TPO */
-PCI_ROM(0x10b7, 0x9056, "3c905b-t4",     "3Com905B-T4"),	/* 10/100 T4 */
-PCI_ROM(0x10b7, 0x9058, "3c905b-9058",   "3Com905B-9058"),	/* Cyclone 10/100/BNC */
-PCI_ROM(0x10b7, 0x905a, "3c905b-fx",     "3Com905B-FL"),	/* 100 Base FX / 10 Base FX */
+	PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO", 0),	/* 10 Base TPO */
+	PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo", 0),	/* 10 Base Combo */
+	PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T", 0),	/* 10 Base TP and Base2 */
+	PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL", 0),	/* 10 Base FL */
+	PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX", 0),	/* 10/100 TPO */
+	PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4", 0),	/* 10/100 T4 */
+	PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058", 0),	/* Cyclone 10/100/BNC */
+	PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL", 0),	/* 100 Base FX / 10 Base FX */
 /* Newer 90xC revision: */
-PCI_ROM(0x10b7, 0x9200, "3c905c-tpo",    "3Com905C-TXM"),	/* 10/100 TPO (3C905C-TXM) */
-PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)"),	/* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */
-PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm","3Com20B-EMB WNM"),
-PCI_ROM(0x10b7, 0x9800, "3c980",         "3Com980-Cyclone"),	/* Cyclone */
-PCI_ROM(0x10b7, 0x9805, "3c9805",        "3Com9805"),		/* Dual Port Server Cyclone */
-PCI_ROM(0x10b7, 0x7646, "3csoho100-tx",  "3CSOHO100-TX"),	/* Hurricane */
-PCI_ROM(0x10b7, 0x4500, "3c450",         "3Com450 HomePNA Tornado"),
-PCI_ROM(0x10b7, 0x1201, "3c982a",        "3Com982A"),
-PCI_ROM(0x10b7, 0x1202, "3c982b",        "3Com982B"),
+	PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM", 0),	/* 10/100 TPO (3C905C-TXM) */
+	PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)", 0),	/* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */
+	PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm", "3Com20B-EMB WNM", 0),
+	PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone", 0),	/* Cyclone */
+	PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805", 0),	/* Dual Port Server Cyclone */
+	PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX", 0),	/* Hurricane */
+	PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado", 0),
+	PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A", 0),
+	PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B", 0),
 };
 
-PCI_DRIVER ( a3c90x_driver, a3c90x_nics, PCI_NO_CLASS );
-
-DRIVER ( "3C90X", nic_driver, pci_driver, a3c90x_driver,
-	 a3c90x_probe, a3c90x_disable );
+struct pci_driver a3c90x_driver __pci_driver = {
+	.ids = a3c90x_nics,
+	.id_count = (sizeof(a3c90x_nics) / sizeof(a3c90x_nics[0])),
+	.probe = a3c90x_probe,
+	.remove = a3c90x_remove,
+};
 
 /*
  * Local variables:
diff --git a/gpxe/src/drivers/net/3c90x.h b/gpxe/src/drivers/net/3c90x.h
new file mode 100644
index 0000000..acbb29d
--- /dev/null
+++ b/gpxe/src/drivers/net/3c90x.h
@@ -0,0 +1,302 @@
+/*
+ * 3c90x.c -- This file implements the 3c90x driver for etherboot.  Written
+ * by Greg Beeley, Greg.Beeley at LightSys.org.  Modified by Steve Smith,
+ * Steve.Smith at Juno.Com. Alignment bug fix Neil Newell (nn at icenoir.net).
+ *
+ * Port from etherboot to gPXE API, implementation of tx/rx ring support
+ * by Thomas Miletich, thomas.miletich at gmail.com
+ * Thanks to Marty Connor and Stefan Hajnoczi for their help and feedback.
+ *
+ * This program Copyright (C) 1999 LightSys Technology Services, Inc.
+ * Portions Copyright (C) 1999 Steve Smith
+ *
+ * This program may be re-distributed in source or binary form, modified,
+ * sold, or copied for any purpose, provided that the above copyright message
+ * and this text are included with all source copies or derivative works, and
+ * provided that the above copyright message and this text are included in the
+ * documentation of any binary-only distributions.  This program is distributed
+ * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
+ * PURPOSE or MERCHANTABILITY.  Please read the associated documentation
+ * "3c90x.txt" before compiling and using this driver.
+ *
+ * --------
+ *
+ * Program written with the assistance of the 3com documentation for
+ * the 3c905B-TX card, as well as with some assistance from the 3c59x
+ * driver Donald Becker wrote for the Linux kernel, and with some assistance
+ * from the remainder of the Etherboot distribution.
+ *
+ * REVISION HISTORY:
+ *
+ * v0.10	1-26-1998	GRB	Initial implementation.
+ * v0.90	1-27-1998	GRB	System works.
+ * v1.00pre1	2-11-1998	GRB	Got prom boot issue fixed.
+ * v2.0		9-24-1999	SCS	Modified for 3c905 (from 3c905b code)
+ *					Re-wrote poll and transmit for
+ *					better error recovery and heavy
+ *					network traffic operation
+ * v2.01    5-26-2003 NN Fixed driver alignment issue which
+ *                  caused system lockups if driver structures
+ *                  not 8-byte aligned.
+ * v2.02   11-28-2007 GSt Got polling working again by replacing
+ * 			"for(i=0;i<40000;i++);" with "mdelay(1);"
+ *
+ *
+ * indent options: indent -kr -i8 3c90x.c
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#ifndef __3C90X_H_
+#define __3C90X_H_
+
+static struct net_device_operations a3c90x_operations;
+
+#define	XCVR_MAGIC	(0x5A00)
+
+/* Register definitions for the 3c905 */
+enum Registers {
+	regPowerMgmtCtrl_w = 0x7c,	/* 905B Revision Only                 */
+	regUpMaxBurst_w = 0x7a,	/* 905B Revision Only                 */
+	regDnMaxBurst_w = 0x78,	/* 905B Revision Only                 */
+	regDebugControl_w = 0x74,	/* 905B Revision Only                 */
+	regDebugData_l = 0x70,	/* 905B Revision Only                 */
+	regRealTimeCnt_l = 0x40,	/* Universal                          */
+	regUpBurstThresh_b = 0x3e,	/* 905B Revision Only                 */
+	regUpPoll_b = 0x3d,	/* 905B Revision Only                 */
+	regUpPriorityThresh_b = 0x3c,	/* 905B Revision Only                 */
+	regUpListPtr_l = 0x38,	/* Universal                          */
+	regCountdown_w = 0x36,	/* Universal                          */
+	regFreeTimer_w = 0x34,	/* Universal                          */
+	regUpPktStatus_l = 0x30,	/* Universal with Exception, pg 130   */
+	regTxFreeThresh_b = 0x2f,	/* 90X Revision Only                  */
+	regDnPoll_b = 0x2d,	/* 905B Revision Only                 */
+	regDnPriorityThresh_b = 0x2c,	/* 905B Revision Only                 */
+	regDnBurstThresh_b = 0x2a,	/* 905B Revision Only                 */
+	regDnListPtr_l = 0x24,	/* Universal with Exception, pg 107   */
+	regDmaCtrl_l = 0x20,	/* Universal with Exception, pg 106   */
+	/*                                    */
+	regIntStatusAuto_w = 0x1e,	/* 905B Revision Only                 */
+	regTxStatus_b = 0x1b,	/* Universal with Exception, pg 113   */
+	regTimer_b = 0x1a,	/* Universal                          */
+	regTxPktId_b = 0x18,	/* 905B Revision Only                 */
+	regCommandIntStatus_w = 0x0e,	/* Universal (Command Variations)     */
+};
+
+/* following are windowed registers */
+enum Registers7 {
+	regPowerMgmtEvent_7_w = 0x0c,	/* 905B Revision Only                 */
+	regVlanEtherType_7_w = 0x04,	/* 905B Revision Only                 */
+	regVlanMask_7_w = 0x00,	/* 905B Revision Only                 */
+};
+
+enum Registers6 {
+	regBytesXmittedOk_6_w = 0x0c,	/* Universal                          */
+	regBytesRcvdOk_6_w = 0x0a,	/* Universal                          */
+	regUpperFramesOk_6_b = 0x09,	/* Universal                          */
+	regFramesDeferred_6_b = 0x08,	/* Universal                          */
+	regFramesRecdOk_6_b = 0x07,	/* Universal with Exceptions, pg 142  */
+	regFramesXmittedOk_6_b = 0x06,	/* Universal                          */
+	regRxOverruns_6_b = 0x05,	/* Universal                          */
+	regLateCollisions_6_b = 0x04,	/* Universal                          */
+	regSingleCollisions_6_b = 0x03,	/* Universal                          */
+	regMultipleCollisions_6_b = 0x02,	/* Universal                          */
+	regSqeErrors_6_b = 0x01,	/* Universal                          */
+	regCarrierLost_6_b = 0x00,	/* Universal                          */
+};
+
+enum Registers5 {
+	regIndicationEnable_5_w = 0x0c,	/* Universal                          */
+	regInterruptEnable_5_w = 0x0a,	/* Universal                          */
+	regTxReclaimThresh_5_b = 0x09,	/* 905B Revision Only                 */
+	regRxFilter_5_b = 0x08,	/* Universal                          */
+	regRxEarlyThresh_5_w = 0x06,	/* Universal                          */
+	regTxStartThresh_5_w = 0x00,	/* Universal                          */
+};
+
+enum Registers4 {
+	regUpperBytesOk_4_b = 0x0d,	/* Universal                          */
+	regBadSSD_4_b = 0x0c,	/* Universal                          */
+	regMediaStatus_4_w = 0x0a,	/* Universal with Exceptions, pg 201  */
+	regPhysicalMgmt_4_w = 0x08,	/* Universal                          */
+	regNetworkDiagnostic_4_w = 0x06,	/* Universal with Exceptions, pg 203  */
+	regFifoDiagnostic_4_w = 0x04,	/* Universal with Exceptions, pg 196  */
+	regVcoDiagnostic_4_w = 0x02,	/* Undocumented?                      */
+};
+
+enum Registers3 {
+	regTxFree_3_w = 0x0c,	/* Universal                          */
+	regRxFree_3_w = 0x0a,	/* Universal with Exceptions, pg 125  */
+	regResetMediaOptions_3_w = 0x08,	/* Media Options on B Revision,       */
+	/* Reset Options on Non-B Revision    */
+	regMacControl_3_w = 0x06,	/* Universal with Exceptions, pg 199  */
+	regMaxPktSize_3_w = 0x04,	/* 905B Revision Only                 */
+	regInternalConfig_3_l = 0x00,	/* Universal, different bit           */
+	/* definitions, pg 59                 */
+};
+
+enum Registers2 {
+	regResetOptions_2_w = 0x0c,	/* 905B Revision Only                 */
+	regStationMask_2_3w = 0x06,	/* Universal with Exceptions, pg 127  */
+	regStationAddress_2_3w = 0x00,	/* Universal with Exceptions, pg 127  */
+};
+
+enum Registers1 {
+	regRxStatus_1_w = 0x0a,	/* 90X Revision Only, Pg 126          */
+};
+
+enum Registers0 {
+	regEepromData_0_w = 0x0c,	/* Universal                          */
+	regEepromCommand_0_w = 0x0a,	/* Universal                          */
+	regBiosRomData_0_b = 0x08,	/* 905B Revision Only                 */
+	regBiosRomAddr_0_l = 0x04,	/* 905B Revision Only                 */
+};
+
+
+/* The names for the eight register windows */
+enum Windows {
+	winNone = 0xff,
+	winPowerVlan7 = 0x07,
+	winStatistics6 = 0x06,
+	winTxRxControl5 = 0x05,
+	winDiagnostics4 = 0x04,
+	winTxRxOptions3 = 0x03,
+	winAddressing2 = 0x02,
+	winUnused1 = 0x01,
+	winEepromBios0 = 0x00,
+};
+
+
+/* Command definitions for the 3c90X */
+enum Commands {
+	cmdGlobalReset = 0x00,	/* Universal with Exceptions, pg 151 */
+	cmdSelectRegisterWindow = 0x01,	/* Universal                         */
+	cmdEnableDcConverter = 0x02,	/*                                   */
+	cmdRxDisable = 0x03,	/*                                   */
+	cmdRxEnable = 0x04,	/* Universal                         */
+	cmdRxReset = 0x05,	/* Universal                         */
+	cmdStallCtl = 0x06,	/* Universal                         */
+	cmdTxEnable = 0x09,	/* Universal                         */
+	cmdTxDisable = 0x0A,	/*                                   */
+	cmdTxReset = 0x0B,	/* Universal                         */
+	cmdRequestInterrupt = 0x0C,	/*                                   */
+	cmdAcknowledgeInterrupt = 0x0D,	/* Universal                         */
+	cmdSetInterruptEnable = 0x0E,	/* Universal                         */
+	cmdSetIndicationEnable = 0x0F,	/* Universal                         */
+	cmdSetRxFilter = 0x10,	/* Universal                         */
+	cmdSetRxEarlyThresh = 0x11,	/*                                   */
+	cmdSetTxStartThresh = 0x13,	/*                                   */
+	cmdStatisticsEnable = 0x15,	/*                                   */
+	cmdStatisticsDisable = 0x16,	/*                                   */
+	cmdDisableDcConverter = 0x17,	/*                                   */
+	cmdSetTxReclaimThresh = 0x18,	/*                                   */
+	cmdSetHashFilterBit = 0x19,	/*                                   */
+};
+
+enum FrameStartHeader {
+	fshTxIndicate = 0x8000,
+	fshDnComplete = 0x10000,
+};
+
+enum UpDownDesc {
+	upLastFrag = (1 << 31),
+	downLastFrag = (1 << 31),
+};
+
+enum UpPktStatus {
+	upComplete = (1 << 15),
+	upError = (1 << 14),
+};
+
+enum Stalls {
+	upStall = 0x00,
+	upUnStall = 0x01,
+
+	dnStall = 0x02,
+	dnUnStall = 0x03,
+};
+
+enum Resources {
+	resRxRing = 0x00,
+	resTxRing = 0x02,
+	resRxIOBuf = 0x04
+};
+
+enum eeprom {
+	eepromBusy = (1 << 15),
+	eepromRead = ((0x02) << 6),
+	eepromRead_556 = 0x230,
+	eepromHwAddrOffset = 0x0a,
+};
+
+/* Bit 4 is only used in revison B and upwards */
+enum linktype {
+	link10BaseT = 0x00,
+	linkAUI = 0x01,
+	link10Base2 = 0x03,
+	link100BaseFX = 0x05,
+	linkMII = 0x06,
+	linkAutoneg = 0x08,
+	linkExternalMII = 0x09,
+};
+
+/* Values for int status register bitmask */
+#define	INT_INTERRUPTLATCH	(1<<0)
+#define INT_HOSTERROR		(1<<1)
+#define INT_TXCOMPLETE		(1<<2)
+#define INT_RXCOMPLETE		(1<<4)
+#define INT_RXEARLY		(1<<5)
+#define INT_INTREQUESTED	(1<<6)
+#define INT_UPDATESTATS		(1<<7)
+#define INT_LINKEVENT		(1<<8)
+#define INT_DNCOMPLETE		(1<<9)
+#define INT_UPCOMPLETE		(1<<10)
+#define INT_CMDINPROGRESS	(1<<12)
+#define INT_WINDOWNUMBER	(7<<13)
+
+/* Buffer sizes */
+#define TX_RING_SIZE 8
+#define RX_RING_SIZE 8
+#define TX_RING_ALIGN 16
+#define RX_RING_ALIGN 16
+#define RX_BUF_SIZE 1536
+
+/* Timeouts for eeprom and command completion */
+/* Timeout 1 second, to be save */
+#define EEPROM_TIMEOUT		1 * 1000 * 1000
+
+/* TX descriptor */
+struct TXD {
+	volatile unsigned int DnNextPtr;
+	volatile unsigned int FrameStartHeader;
+	volatile unsigned int DataAddr;
+	volatile unsigned int DataLength;
+} __attribute__ ((aligned(8)));	/* 64-bit aligned for bus mastering */
+
+/* RX descriptor */
+struct RXD {
+	volatile unsigned int UpNextPtr;
+	volatile unsigned int UpPktStatus;
+	volatile unsigned int DataAddr;
+	volatile unsigned int DataLength;
+} __attribute__ ((aligned(8)));	/* 64-bit aligned for bus mastering */
+
+/* Private NIC dats */
+struct INF_3C90X {
+	unsigned int is3c556;
+	unsigned char isBrev;
+	unsigned char CurrentWindow;
+	unsigned int IOAddr;
+	unsigned short eeprom[0x21];
+	unsigned int tx_cur;	/* current entry in tx_ring */
+	unsigned int tx_cnt;	/* current number of used tx descriptors */
+	unsigned int tx_tail;	/* entry of last finished packet */
+	unsigned int rx_cur;
+	struct TXD *tx_ring;
+	struct RXD *rx_ring;
+	struct io_buffer *tx_iobuf[TX_RING_SIZE];
+	struct io_buffer *rx_iobuf[RX_RING_SIZE];
+	struct nvs_device nvs;
+};
+
+#endif
diff --git a/gpxe/src/drivers/net/3c90x.txt b/gpxe/src/drivers/net/3c90x.txt
deleted file mode 100644
index 3d6746c..0000000
--- a/gpxe/src/drivers/net/3c90x.txt
+++ /dev/null
@@ -1,307 +0,0 @@
-
-	Instructions for use of the 3C90X driver for EtherBoot
-
-		Original 3C905B support by:
-			Greg Beeley (Greg.Beeley at LightSys.org),
-			LightSys Technology Services, Inc.
-			February 11, 1999
-
-		Updates for 3C90X family by:
-			Steve Smith (steve.smith at juno.com)
-			October 1, 1999
-
-		Minor documentation updates by
-			Greg Beeley (Greg.Beeley at LightSys.org)
-			March 29, 2000
-
--------------------------------------------------------------------------------
-
-I   OVERVIEW
-
-    The 3c90X series ethernet cards are a group of high-performance busmaster
-    DMA cards from 3Com.  This particular driver supports both the 3c90x and
-    the 3c90xB revision cards.  3C90xC family support has been tested to some
-    degree but not extensively.
-
-    Here's the licensing information:
-
-    This program Copyright (C) 1999 LightSys Technology Services, Inc.
-    Portions Copyright (C) 1999 Steve Smith.
-
-    This program may be re-distributed in source or binary form, modified,
-    sold, or copied for any purpose, provided that the above copyright message
-    and this text are included with all source copies or derivative works, and
-    provided that the above copyright message and this text are included in the
-    documentation of any binary-only distributions.  This program is
-    distributed WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR
-    A PARTICULAR PURPOSE or MERCHANTABILITY.  Please read the associated
-    documentation "3c90x.txt" before compiling and using this driver.
-
-
-II  FLASH PROMS
-
-    The 3c90xB cards, according to the 3Com documentation, only accept the
-    following flash memory chips:
-
-	Atmel AT29C512 (64 kilobyte)
-	Atmel AT29C010 (128 kilobyte)
-
-    The 3c90x cards, according to the 3Com documentation, accept the
-    following flash memory chips capacities:
-
-	64  kb (8 kB)
-	128 kb (16 kB)
-	256 kb (32 kB) and
-	512 kb (64 kB)
-
-    Atmel AT29C512 (64 kilobyte) chips are specifically listed for both
-    adapters, but flashing on the 3c905b cards would only be supported
-    through the Atmel parts.  Any device, of the supported size, should
-    be supported when programmed by a dedicated PROM programmer (e.g.
-    not the card).
-
-    To use this driver in such a PROM, visit Atmel's web site and download
-    their .PDF file containing a list of their distributors.  Contact the
-    distributors for pricing information.  The prices are quite reasonable
-    (about $3 US each for the 64 kB part), and are comparable to what one would
-    expect for similarly sized standard EPROMs.  And, the flash chips are much
-    easier to work with, as they don't need to be UV-erased to be reprogrammed.
-    The 3C905B card actually provides a method to program the flash memory
-    while it is resident on board the card itself; if someone would like to
-    write a small DOS program to do the programming, I can provide the
-    information about the registers and so forth.
-
-    A utility program, 3c90xutil, is provided with Etherboot in the 'contrib'
-    directory that allows for the on-board flashing of the ROM while Linux
-    is running.  The program has been successfully used under Linux, but I
-    have heard problem reports of its use under FreeBSD.  Anyone willing to
-    make it work under FreeBSD is more than welcome to do so!
-
-    You also have the option of using EPROM chips - the 3C905B-TX-NM has been
-    successfully tested with 27C256 (32kB) and 27C512 (64kB) chips with a
-    specified access time of 100ns and faster.
-
-
-III GENERAL USE
-
-    Normally, the basic procedure for using this driver is as follows:
-
-	1.  Run the 3c90xcfg program on the driver diskette to enable the
-	boot PROM and set it to 64k or 128k, as appropriate.
-	2.  Build the appropriate 3c90x.fd0 or 3c90x.fd0 floppy image with
-	possibly the value CFG_3C90X_XCVR defined to the transceiver type that
-	you want to use (i.e., 10/100 rj45, AUI, coax, MII).
-	3.  Run the floppy image on the PC to be network booted, to get
-	it configured, and to verify that it will boot properly.
-	4.  Build the 3c90x.rom or 3c90x.lzrom PROM image and program
-	it into the flash or EPROM memory chip.
-	5.  Put the PROM in the ethernet card, boot and enable 'boot from
-	network first' in the system BIOS, save and reboot.
-
-    Here are some issues to be aware of:
-
-	1.  If you experience crashes or different behaviour when using the
-	boot PROM, add the setting CFG_3C90X_BOOTROM_FIX and go through the
-	steps 2-5 above.  This works around a bug in some 3c905B cards (see
-	below), but has some side-effects which may not be desirable.
-        Please note that you have to boot off a floppy (not PROM!) once for
-        this fix to take effect.
-	2.  The possible need to manually set the CFG_3C90X_XCVR value to
-	configure the transceiver type.  Values are listed below.
-	3.  The possible need to define CFG_3C90X_PRESERVE_XCVR for use in
-	operating systems that don't intelligently determine the
-	transceiver type.
-
-    Some things that are on the 'To-Do' list, perhaps for me, but perhaps
-    for any other volunteers out there:
-
-	1.  Extend the driver to fully implement the auto-select
-	algorithm if the card has multiple media ports.
-	2.  Fix any bugs in the code <grin>....
-	3.  Extend the driver to support the 3c905c revision cards
-	"officially".  Right now, the support has been primarily empirical
-	and not based on 3c905C documentation.
-
-    Now for the details....
-
-    This driver has been tested on roughly 300 systems.  The main two
-    configuration issues to contend with are:
-
-	1.  Ensure that PCI Busmastering is enabled for the adapter (configured
-	in the CMOS setup)
-	2.  Some systems don't work properly with the adapter when plug and
-	play OS is enabled; I always set it to "No" or "Disabled" -- this makes
-	it easier and really doesn't adversely affect anything.
-
-    Roughly 95% of the systems worked when configured properly.  A few
-    have issues with booting locally once the boot PROM has been installed
-    (this number has been less than 2%).  Other configuration issues that
-    to check:
-
-	1.  Newer BIOS's actually work correctly with the network boot order.
-	Set the network adapter first.  Most older BIOS's automatically go to
-	the network boot PROM first.
-	2.  For systems where the adapter was already installed and is just
-	having the PROM installed, try setting the "reset configuration data"
-	to yes in the CMOS setup if the BIOS isn't seen at first.  If your BIOS
-	doesn't have this option, remove the card, start the system, shut down,
-	install the card and restart (or switch to a different PCI slot).
-	3.  Make sure the CMOS security settings aren't preventing a boot.
-
-    The 3c905B cards have a significant 'bug' that relates to the flash prom:
-    unless the card is set internally to the MII transceiver, it will only
-    read the first 8k of the PROM image.  Don't ask why -- it seems really
-    obscure, but it has to do with the way they mux'd the address lines
-    from the PCI bus to the ROM.  Unfortunately, most of us are not using
-    MII transceivers, and even the .lzrom image ends up being just a little
-    bit larger than 8k.  Note that the workaround for this is disabled by
-    default, because the Windows NT 4.0 driver does not like it (no packets
-    are transmitted).
-
-    So, the solution that I've used is to internally set the card's nvram
-    configuration to use MII when it boots.  The 3c905b driver does this
-    automatically.  This way, the 16k prom image can be loaded into memory,
-    and then the 3c905b driver can set the temporary configuration of the
-    card to an appropriate value, either configurable by the user or chosen
-    by the driver.
-
-    To enable the 3c905B bugfix, which is necessary for these cards when 
-    booting from the Flash ROM, define -DCFG_3C90X_BOOTROM_FIX when building,
-    create a floppy image and boot it once.
-    Thereafter, the card should accept the larger prom image.
-
-    The driver should choose an appropriate transceiver on the card.  However,
-    if it doesn't on your card or if you need to, for instance, set your
-    card to 10mbps when connected to an unmanaged 10/100 hub, you can specify
-    which transceiver you want to use.  To do this, build the 3c905b.fd0
-    image with -DCFG_3C90X_XCVR=x, where 'x' is one of the following
-    values:
-
-	0	10Base-T
-	1	10mbps AUI
-	3	10Base-2 (thinnet/coax)
-	4	100Base-TX
-	5	100Base-FX
-	6	MII
-	8	Auto-negotiation 10Base-T / 100Base-TX (usually the default)
-	9	MII External MAC Mode
-	255	Allow driver to choose an 'appropriate' media port.
-
-    Then proceed from step 2 in the above 'general use' instructions.  The
-    .rom image can be built with CFG_3C90X_XCVR set to a value, but you
-    normally don't want to do this, since it is easier to change the
-    transceiver type by rebuilding a new floppy, changing the BIOS to floppy
-    boot, booting, and then changing the BIOS back to network boot.  If
-    CFG_3C90X_XCVR is not set in a particular build, it just uses the
-    current configuration (either its 'best guess' or whatever the stored
-    CFG_3C90X_XCVR value was from the last time it was set).
-
-    [[ Note for the more technically inclined:  The CFG_3C90X_XCVR value is
-    programmed into a register in the card's NVRAM that was reserved for
-    LanWorks PROM images to use.  When the driver boots, the card comes
-    up in MII mode, and the driver checks the LanWorks register to find
-    out if the user specified a transceiver type.  If it finds that
-    information, it uses that, otherwise it picks a transceiver that the
-    card has based on the 3c905b's MediaOptions register.  This driver isn't
-    quite smart enough to always determine which media port is actually
-    _connected_; maybe someone else would like to take on that task (it
-    actually involves sending a self-directed packet and seeing if it
-    comes back.  IF it does, that port is connected). ]]
-
-    Another issue to keep in mind is that it is possible that some OS'es
-    might not be happy with the way I've handled the PROM-image hack with
-    setting MII mode on bootup.  Linux 2.0.35 does not have this problem.
-    Behavior of other systems may vary.  The 3com documentation specifically
-    says that, at least with the card that I have, the device driver in the
-    OS should auto-select the media port, so other drivers should work fine
-    with this 'hack'.  However, if yours doesn't seem to, you can try defining
-    CFG_3C90X_PRESERVE_XCVR when building to cause Etherboot to keep the
-    working setting (that allowed the bootp/tftp process) across the eth_reset
-    operation.
-
-
-IV  FOR DEVELOPERS....
-
-    If you would like to fix/extend/etc. this driver, feel free to do so; just
-    be sure you can test the modified version on the 3c905B-TX cards that the
-    driver was originally designed for.  This section of this document gives
-    some information that might be relevant to a programmer.
-
-    A.  Main Entry Point
-
-	a3c90x_probe is the main entry point for this driver.  It is referred
-	to in an array in 'config.c'.
-
-    B.  Other Important Functions
-
-	The functions a3c90x_transmit, a3c90x_poll, a3c90x_reset, and
-	a3c90x_disable are static functions that EtherBoot finds out about
-	as a  result of a3c90x_probe setting entries in the nic structure
-	for them.  The EtherBoot framework does not use interrupts.  It is
-	polled.  All transmit and receive operations are initiated by the
-	etherboot framework, not by an interrupt or by the driver.
-
-    C.  Internal Functions
-
-	The following functions are internal to the driver:
-
-	a3c90x_internal_IssueCommand - sends a command to the 3c905b card.
-	a3c90x_internal_SetWindow - shifts between one of eight register
-	windows onboard the 3c90x.  The bottom 16 bytes of the card's
-	I/O space are multiplexed among 128 bytes, only 16 of which are
-	visible at any one time.  This SetWindow function selects one of
-	the eight sets.
-	a3c90x_internal_ReadEeprom - reads a word (16 bits) from the
-	card's onboard nvram.  This is NOT the BIOS boot rom.  This is
-	where the card stores such things as its hardware address.
-	a3c90x_internal_WriteEeprom - writes a word (16 bits) to the
-	card's nvram, and recomputes the eeprom checksum.
-	a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
-	card's nvram.  Used by the above routine.
-	a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
-	card's nvram.  Used by the above routine.
-
-    D.  Globals
-
-	All global variables are inside a global structure named INF_3C90X.
-	So, wherever you see that structure referenced, you know the variable
-	is a global.  Just keeps things a little neater.
-
-    E.  Enumerations
-
-	There are quite a few enumerated type definitions for registers and
-	so forth, many for registers that I didn't even touch in the driver.
-	Register types start with 'reg', window numbers (for SetWindow)
-	start with 'win', and commands (for IssueCommand) start with 'cmd'.
-	Register offsets also include an indication in the name as to the
-	size of the register (_b = byte, _w = word, _l = long), and which
-	window the register is in, if it is windowed (0-7).
-
-    F.  Why the 'a3c90x' name?
-
-	I had to come up with a letter at the beginning of all of the
-	identifiers, since 3com so conveniently had their name start with a
-	number.  Another driver used 't' (for 'three'?); I chose 'a' for
-	no reason at all.
-
-Addendum by Jorge L. deLyra <delyra at latt.if.usp.br>, 22Nov2000 re
-working around the 3C905 hardware bug mentioned above:
-
-Use this floppy to fix any 3COM model 3C905B PCI 10/100 Ethernet cards
-that fail to load and run the boot program the first time around. If
-they have a "Lucent" rather than a "Broadcom" chipset these cards have
-a configuration bug that causes a hang when trying to load the boot
-program from the PROM, if you try to use them right out of the box.
-
-The boot program in this floppy is the file named 3c905b-tpo100.rom
-from Etherboot version 4.6.10, compiled with the bugfix parameter
-
-			CFG_3C90X_BOOTROM_FIX
-
-You have to take the chip off the card and boot the system once using
-this floppy. Once loaded from the floppy, the boot program will access
-the card and change some setting in it, correcting the problem. After
-that you may use either this boot program or the normal one, compiled
-without this bugfix parameter, to boot the machine from the PROM chip.
-
-[Any recent Etherboot version should do, not just 4.6.10 - Ed.]
diff --git a/gpxe/src/drivers/net/amd8111e.c b/gpxe/src/drivers/net/amd8111e.c
index 1c41add..1b1fdc1 100644
--- a/gpxe/src/drivers/net/amd8111e.c
+++ b/gpxe/src/drivers/net/amd8111e.c
@@ -28,6 +28,8 @@
  * USA
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "etherboot.h"
 #include "nic.h"
 #include "mii.h"
@@ -674,7 +676,7 @@ static int amd8111e_probe(struct nic *nic, struct pci_device *pdev)
 }
 
 static struct pci_device_id amd8111e_nics[] = {
-	PCI_ROM(0x1022, 0x7462, "amd8111e",	"AMD8111E"),
+	PCI_ROM(0x1022, 0x7462, "amd8111e",	"AMD8111E", 0),
 };
 
 PCI_DRIVER ( amd8111e_driver, amd8111e_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/amd8111e.h b/gpxe/src/drivers/net/amd8111e.h
index 82b8f7a..a402a63 100644
--- a/gpxe/src/drivers/net/amd8111e.h
+++ b/gpxe/src/drivers/net/amd8111e.h
@@ -35,6 +35,8 @@ Revision History:
 	3.0.1
 */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef _AMD811E_H
 #define _AMD811E_H
 
diff --git a/gpxe/src/drivers/net/ath5k/ath5k.c b/gpxe/src/drivers/net/ath5k/ath5k.c
new file mode 100644
index 0000000..37defce
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k.c
@@ -0,0 +1,1700 @@
+/*
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications, Inc.
+ * Copyright (c) 2006 Devicescape Software, Inc.
+ * Copyright (c) 2007 Jiri Slaby <jirislaby at gmail.com>
+ * Copyright (c) 2007 Luis R. Rodriguez <mcgrof at winlab.rutgers.edu>
+ *
+ * Modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>
+ * Original from Linux kernel 2.6.30.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+FILE_LICENCE ( BSD3 );
+
+#include <stdlib.h>
+#include <gpxe/malloc.h>
+#include <gpxe/timer.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/pci.h>
+#include <gpxe/pci_io.h>
+
+#include "base.h"
+#include "reg.h"
+
+#define ATH5K_CALIB_INTERVAL	10 /* Calibrate PHY every 10 seconds */
+#define ATH5K_RETRIES		4  /* Number of times to retry packet sends */
+#define ATH5K_DESC_ALIGN	16 /* Alignment for TX/RX descriptors */
+
+/******************\
+* Internal defines *
+\******************/
+
+/* Known PCI ids */
+static struct pci_device_id ath5k_nics[] = {
+	PCI_ROM(0x168c, 0x0207, "ath5210e", "Atheros 5210 early", AR5K_AR5210),
+	PCI_ROM(0x168c, 0x0007, "ath5210", "Atheros 5210", AR5K_AR5210),
+	PCI_ROM(0x168c, 0x0011, "ath5311", "Atheros 5311 (AHB)", AR5K_AR5211),
+	PCI_ROM(0x168c, 0x0012, "ath5211", "Atheros 5211", AR5K_AR5211),
+	PCI_ROM(0x168c, 0x0013, "ath5212", "Atheros 5212", AR5K_AR5212),
+	PCI_ROM(0xa727, 0x0013, "ath5212c","3com Ath 5212", AR5K_AR5212),
+	PCI_ROM(0x10b7, 0x0013, "rdag675", "3com 3CRDAG675", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x1014, "ath5212m", "Ath 5212 miniPCI", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x0014, "ath5212x14", "Atheros 5212 x14", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x0015, "ath5212x15", "Atheros 5212 x15", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x0016, "ath5212x16", "Atheros 5212 x16", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x0017, "ath5212x17", "Atheros 5212 x17", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x0018, "ath5212x18", "Atheros 5212 x18", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x0019, "ath5212x19", "Atheros 5212 x19", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x001a, "ath2413", "Atheros 2413 Griffin", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x001b, "ath5413", "Atheros 5413 Eagle", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x001c, "ath5212e", "Atheros 5212 PCI-E", AR5K_AR5212),
+	PCI_ROM(0x168c, 0x001d, "ath2417", "Atheros 2417 Nala", AR5K_AR5212),
+};
+
+/* Known SREVs */
+static const struct ath5k_srev_name srev_names[] = {
+	{ "5210",	AR5K_VERSION_MAC,	AR5K_SREV_AR5210 },
+	{ "5311",	AR5K_VERSION_MAC,	AR5K_SREV_AR5311 },
+	{ "5311A",	AR5K_VERSION_MAC,	AR5K_SREV_AR5311A },
+	{ "5311B",	AR5K_VERSION_MAC,	AR5K_SREV_AR5311B },
+	{ "5211",	AR5K_VERSION_MAC,	AR5K_SREV_AR5211 },
+	{ "5212",	AR5K_VERSION_MAC,	AR5K_SREV_AR5212 },
+	{ "5213",	AR5K_VERSION_MAC,	AR5K_SREV_AR5213 },
+	{ "5213A",	AR5K_VERSION_MAC,	AR5K_SREV_AR5213A },
+	{ "2413",	AR5K_VERSION_MAC,	AR5K_SREV_AR2413 },
+	{ "2414",	AR5K_VERSION_MAC,	AR5K_SREV_AR2414 },
+	{ "5424",	AR5K_VERSION_MAC,	AR5K_SREV_AR5424 },
+	{ "5413",	AR5K_VERSION_MAC,	AR5K_SREV_AR5413 },
+	{ "5414",	AR5K_VERSION_MAC,	AR5K_SREV_AR5414 },
+	{ "2415",	AR5K_VERSION_MAC,	AR5K_SREV_AR2415 },
+	{ "5416",	AR5K_VERSION_MAC,	AR5K_SREV_AR5416 },
+	{ "5418",	AR5K_VERSION_MAC,	AR5K_SREV_AR5418 },
+	{ "2425",	AR5K_VERSION_MAC,	AR5K_SREV_AR2425 },
+	{ "2417",	AR5K_VERSION_MAC,	AR5K_SREV_AR2417 },
+	{ "xxxxx",	AR5K_VERSION_MAC,	AR5K_SREV_UNKNOWN },
+	{ "5110",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5110 },
+	{ "5111",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5111 },
+	{ "5111A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5111A },
+	{ "2111",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2111 },
+	{ "5112",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5112 },
+	{ "5112A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5112A },
+	{ "5112B",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5112B },
+	{ "2112",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112 },
+	{ "2112A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112A },
+	{ "2112B",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112B },
+	{ "2413",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2413 },
+	{ "5413",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5413 },
+	{ "2316",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2316 },
+	{ "2317",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2317 },
+	{ "5424",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5424 },
+	{ "5133",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5133 },
+	{ "xxxxx",	AR5K_VERSION_RAD,	AR5K_SREV_UNKNOWN },
+};
+
+#define ATH5K_SPMBL_NO   1
+#define ATH5K_SPMBL_YES  2
+#define ATH5K_SPMBL_BOTH 3
+
+static const struct {
+	u16 bitrate;
+	u8 short_pmbl;
+	u8 hw_code;
+} ath5k_rates[] = {
+	{ 10, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_1M },
+	{ 20, ATH5K_SPMBL_NO, ATH5K_RATE_CODE_2M },
+	{ 55, ATH5K_SPMBL_NO, ATH5K_RATE_CODE_5_5M },
+	{ 110, ATH5K_SPMBL_NO, ATH5K_RATE_CODE_11M },
+	{ 60, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_6M },
+	{ 90, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_9M },
+	{ 120, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_12M },
+	{ 180, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_18M },
+	{ 240, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_24M },
+	{ 360, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_36M },
+	{ 480, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_48M },
+	{ 540, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_54M },
+	{ 20, ATH5K_SPMBL_YES, ATH5K_RATE_CODE_2M | AR5K_SET_SHORT_PREAMBLE },
+	{ 55, ATH5K_SPMBL_YES, ATH5K_RATE_CODE_5_5M | AR5K_SET_SHORT_PREAMBLE },
+	{ 110, ATH5K_SPMBL_YES, ATH5K_RATE_CODE_11M | AR5K_SET_SHORT_PREAMBLE },
+	{ 0, 0, 0 },
+};
+
+#define ATH5K_NR_RATES 15
+
+/*
+ * Prototypes - PCI stack related functions
+ */
+static int 		ath5k_probe(struct pci_device *pdev,
+				    const struct pci_device_id *id);
+static void		ath5k_remove(struct pci_device *pdev);
+
+struct pci_driver ath5k_pci_driver __pci_driver = {
+	.ids		= ath5k_nics,
+	.id_count	= sizeof(ath5k_nics) / sizeof(ath5k_nics[0]),
+	.probe		= ath5k_probe,
+	.remove		= ath5k_remove,
+};
+
+
+
+/*
+ * Prototypes - MAC 802.11 stack related functions
+ */
+static int ath5k_tx(struct net80211_device *dev, struct io_buffer *skb);
+static int ath5k_reset(struct ath5k_softc *sc, struct net80211_channel *chan);
+static int ath5k_reset_wake(struct ath5k_softc *sc);
+static int ath5k_start(struct net80211_device *dev);
+static void ath5k_stop(struct net80211_device *dev);
+static int ath5k_config(struct net80211_device *dev, int changed);
+static void ath5k_poll(struct net80211_device *dev);
+static void ath5k_irq(struct net80211_device *dev, int enable);
+
+static struct net80211_device_operations ath5k_ops = {
+	.open		= ath5k_start,
+	.close		= ath5k_stop,
+	.transmit	= ath5k_tx,
+	.poll		= ath5k_poll,
+	.irq		= ath5k_irq,
+	.config		= ath5k_config,
+};
+
+/*
+ * Prototypes - Internal functions
+ */
+/* Attach detach */
+static int 	ath5k_attach(struct net80211_device *dev);
+static void 	ath5k_detach(struct net80211_device *dev);
+/* Channel/mode setup */
+static unsigned int ath5k_copy_channels(struct ath5k_hw *ah,
+				struct net80211_channel *channels,
+				unsigned int mode,
+				unsigned int max);
+static int 	ath5k_setup_bands(struct net80211_device *dev);
+static int 	ath5k_chan_set(struct ath5k_softc *sc,
+				struct net80211_channel *chan);
+static void	ath5k_setcurmode(struct ath5k_softc *sc,
+				unsigned int mode);
+static void	ath5k_mode_setup(struct ath5k_softc *sc);
+
+/* Descriptor setup */
+static int	ath5k_desc_alloc(struct ath5k_softc *sc);
+static void	ath5k_desc_free(struct ath5k_softc *sc);
+/* Buffers setup */
+static int 	ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf);
+static int 	ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf);
+
+static inline void ath5k_txbuf_free(struct ath5k_softc *sc,
+				    struct ath5k_buf *bf)
+{
+	if (!bf->iob)
+		return;
+
+	net80211_tx_complete(sc->dev, bf->iob, 0, ECANCELED);
+	bf->iob = NULL;
+}
+
+static inline void ath5k_rxbuf_free(struct ath5k_softc *sc __unused,
+				    struct ath5k_buf *bf)
+{
+	free_iob(bf->iob);
+	bf->iob = NULL;
+}
+
+/* Queues setup */
+static int 	ath5k_txq_setup(struct ath5k_softc *sc,
+					   int qtype, int subtype);
+static void 	ath5k_txq_drainq(struct ath5k_softc *sc,
+				 struct ath5k_txq *txq);
+static void 	ath5k_txq_cleanup(struct ath5k_softc *sc);
+static void 	ath5k_txq_release(struct ath5k_softc *sc);
+/* Rx handling */
+static int 	ath5k_rx_start(struct ath5k_softc *sc);
+static void 	ath5k_rx_stop(struct ath5k_softc *sc);
+/* Tx handling */
+static void 	ath5k_tx_processq(struct ath5k_softc *sc,
+				  struct ath5k_txq *txq);
+
+/* Interrupt handling */
+static int 	ath5k_init(struct ath5k_softc *sc);
+static int 	ath5k_stop_hw(struct ath5k_softc *sc);
+
+static void 	ath5k_calibrate(struct ath5k_softc *sc);
+
+/* Filter */
+static void	ath5k_configure_filter(struct ath5k_softc *sc);
+
+/********************\
+* PCI Initialization *
+\********************/
+
+#if DBGLVL_MAX
+static const char *
+ath5k_chip_name(enum ath5k_srev_type type, u16 val)
+{
+	const char *name = "xxxxx";
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(srev_names); i++) {
+		if (srev_names[i].sr_type != type)
+			continue;
+
+		if ((val & 0xf0) == srev_names[i].sr_val)
+			name = srev_names[i].sr_name;
+
+		if ((val & 0xff) == srev_names[i].sr_val) {
+			name = srev_names[i].sr_name;
+			break;
+		}
+	}
+
+	return name;
+}
+#endif
+
+static int ath5k_probe(struct pci_device *pdev,
+		       const struct pci_device_id *id)
+{
+	void *mem;
+	struct ath5k_softc *sc;
+	struct net80211_device *dev;
+	int ret;
+	u8 csz;
+
+	adjust_pci_device(pdev);
+
+	/*
+	 * Cache line size is used to size and align various
+	 * structures used to communicate with the hardware.
+	 */
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
+	if (csz == 0) {
+		/*
+		 * We must have this setup properly for rx buffer
+		 * DMA to work so force a reasonable value here if it
+		 * comes up zero.
+		 */
+		csz = 16;
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
+	}
+	/*
+	 * The default setting of latency timer yields poor results,
+	 * set it to the value used by other systems.  It may be worth
+	 * tweaking this setting more.
+	 */
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
+
+	/*
+	 * Disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state.
+	 */
+	pci_write_config_byte(pdev, 0x41, 0);
+
+	mem = ioremap(pdev->membase, 0x10000);
+	if (!mem) {
+		DBG("ath5k: cannot remap PCI memory region\n");
+		ret = -EIO;
+		goto err;
+	}
+
+	/*
+	 * Allocate dev (net80211 main struct)
+	 * and dev->priv (driver private data)
+	 */
+	dev = net80211_alloc(sizeof(*sc));
+	if (!dev) {
+		DBG("ath5k: cannot allocate 802.11 device\n");
+		ret = -ENOMEM;
+		goto err_map;
+	}
+
+	/* Initialize driver private data */
+	sc = dev->priv;
+	sc->dev = dev;
+	sc->pdev = pdev;
+
+	sc->hwinfo = zalloc(sizeof(*sc->hwinfo));
+	if (!sc->hwinfo) {
+		DBG("ath5k: cannot allocate 802.11 hardware info structure\n");
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
+	sc->hwinfo->flags = NET80211_HW_RX_HAS_FCS;
+	sc->hwinfo->signal_type = NET80211_SIGNAL_DB;
+	sc->hwinfo->signal_max = 40; /* 35dB should give perfect 54Mbps */
+	sc->hwinfo->channel_change_time = 5000;
+
+	/* Avoid working with the device until setup is complete */
+	sc->status |= ATH_STAT_INVALID;
+
+	sc->iobase = mem;
+	sc->cachelsz = csz * 4; /* convert to bytes */
+
+	DBG("ath5k: register base at %p (%08lx)\n", sc->iobase, pdev->membase);
+	DBG("ath5k: cache line size %d\n", sc->cachelsz);
+
+	/* Set private data */
+	pci_set_drvdata(pdev, dev);
+	dev->netdev->dev = (struct device *)pdev;
+
+	/* Initialize device */
+	ret = ath5k_hw_attach(sc, id->driver_data, &sc->ah);
+	if (ret)
+		goto err_free_hwinfo;
+
+	/* Finish private driver data initialization */
+	ret = ath5k_attach(dev);
+	if (ret)
+		goto err_ah;
+
+#if DBGLVL_MAX
+	DBG("Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
+	    ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev),
+	    sc->ah->ah_mac_srev, sc->ah->ah_phy_revision);
+
+	if (!sc->ah->ah_single_chip) {
+		/* Single chip radio (!RF5111) */
+		if (sc->ah->ah_radio_5ghz_revision &&
+		    !sc->ah->ah_radio_2ghz_revision) {
+			/* No 5GHz support -> report 2GHz radio */
+			if (!(sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11A)) {
+				DBG("RF%s 2GHz radio found (0x%x)\n",
+				    ath5k_chip_name(AR5K_VERSION_RAD,
+						    sc->ah->ah_radio_5ghz_revision),
+				    sc->ah->ah_radio_5ghz_revision);
+			/* No 2GHz support (5110 and some
+			 * 5Ghz only cards) -> report 5Ghz radio */
+			} else if (!(sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11B)) {
+				DBG("RF%s 5GHz radio found (0x%x)\n",
+				    ath5k_chip_name(AR5K_VERSION_RAD,
+						    sc->ah->ah_radio_5ghz_revision),
+				    sc->ah->ah_radio_5ghz_revision);
+			/* Multiband radio */
+			} else {
+				DBG("RF%s multiband radio found (0x%x)\n",
+				    ath5k_chip_name(AR5K_VERSION_RAD,
+						    sc->ah->ah_radio_5ghz_revision),
+				    sc->ah->ah_radio_5ghz_revision);
+			}
+		}
+		/* Multi chip radio (RF5111 - RF2111) ->
+		 * report both 2GHz/5GHz radios */
+		else if (sc->ah->ah_radio_5ghz_revision &&
+			 sc->ah->ah_radio_2ghz_revision) {
+			DBG("RF%s 5GHz radio found (0x%x)\n",
+			    ath5k_chip_name(AR5K_VERSION_RAD,
+					    sc->ah->ah_radio_5ghz_revision),
+			    sc->ah->ah_radio_5ghz_revision);
+			DBG("RF%s 2GHz radio found (0x%x)\n",
+			    ath5k_chip_name(AR5K_VERSION_RAD,
+					    sc->ah->ah_radio_2ghz_revision),
+			    sc->ah->ah_radio_2ghz_revision);
+		}
+	}
+#endif
+
+	/* Ready to go */
+	sc->status &= ~ATH_STAT_INVALID;
+
+	return 0;
+err_ah:
+	ath5k_hw_detach(sc->ah);
+err_free_hwinfo:
+	free(sc->hwinfo);
+err_free:
+	net80211_free(dev);
+err_map:
+	iounmap(mem);
+err:
+	return ret;
+}
+
+static void ath5k_remove(struct pci_device *pdev)
+{
+	struct net80211_device *dev = pci_get_drvdata(pdev);
+	struct ath5k_softc *sc = dev->priv;
+
+	ath5k_detach(dev);
+	ath5k_hw_detach(sc->ah);
+	iounmap(sc->iobase);
+	free(sc->hwinfo);
+	net80211_free(dev);
+}
+
+
+/***********************\
+* Driver Initialization *
+\***********************/
+
+static int
+ath5k_attach(struct net80211_device *dev)
+{
+	struct ath5k_softc *sc = dev->priv;
+	struct ath5k_hw *ah = sc->ah;
+	int ret;
+
+	/*
+	 * Collect the channel list.  The 802.11 layer
+	 * is resposible for filtering this list based
+	 * on settings like the phy mode and regulatory
+	 * domain restrictions.
+	 */
+	ret = ath5k_setup_bands(dev);
+	if (ret) {
+		DBG("ath5k: can't get channels\n");
+		goto err;
+	}
+
+	/* NB: setup here so ath5k_rate_update is happy */
+	if (ah->ah_modes & AR5K_MODE_BIT_11A)
+		ath5k_setcurmode(sc, AR5K_MODE_11A);
+	else
+		ath5k_setcurmode(sc, AR5K_MODE_11B);
+
+	/*
+	 * Allocate tx+rx descriptors and populate the lists.
+	 */
+	ret = ath5k_desc_alloc(sc);
+	if (ret) {
+		DBG("ath5k: can't allocate descriptors\n");
+		goto err;
+	}
+
+	/*
+	 * Allocate hardware transmit queues. Note that hw functions
+	 * handle reseting these queues at the needed time.
+	 */
+	ret = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
+	if (ret) {
+		DBG("ath5k: can't setup xmit queue\n");
+		goto err_desc;
+	}
+
+	sc->last_calib_ticks = currticks();
+
+	ret = ath5k_eeprom_read_mac(ah, sc->hwinfo->hwaddr);
+	if (ret) {
+		DBG("ath5k: unable to read address from EEPROM: 0x%04x\n",
+		    sc->pdev->device);
+		goto err_queues;
+	}
+
+	memset(sc->bssidmask, 0xff, ETH_ALEN);
+	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
+
+	ret = net80211_register(sc->dev, &ath5k_ops, sc->hwinfo);
+	if (ret) {
+		DBG("ath5k: can't register ieee80211 hw\n");
+		goto err_queues;
+	}
+
+	return 0;
+err_queues:
+	ath5k_txq_release(sc);
+err_desc:
+	ath5k_desc_free(sc);
+err:
+	return ret;
+}
+
+static void
+ath5k_detach(struct net80211_device *dev)
+{
+	struct ath5k_softc *sc = dev->priv;
+
+	net80211_unregister(dev);
+	ath5k_desc_free(sc);
+	ath5k_txq_release(sc);
+}
+
+
+
+
+/********************\
+* Channel/mode setup *
+\********************/
+
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+static inline short
+ath5k_ieee2mhz(short chan)
+{
+	if (chan < 14)
+		return 2407 + 5 * chan;
+	if (chan == 14)
+		return 2484;
+	if (chan < 27)
+		return 2212 + 20 * chan;
+	return 5000 + 5 * chan;
+}
+
+static unsigned int
+ath5k_copy_channels(struct ath5k_hw *ah,
+		    struct net80211_channel *channels,
+		    unsigned int mode, unsigned int max)
+{
+	unsigned int i, count, size, chfreq, freq, ch;
+
+	if (!(ah->ah_modes & (1 << mode)))
+		return 0;
+
+	switch (mode) {
+	case AR5K_MODE_11A:
+	case AR5K_MODE_11A_TURBO:
+		/* 1..220, but 2GHz frequencies are filtered by check_channel */
+		size = 220;
+		chfreq = CHANNEL_5GHZ;
+		break;
+	case AR5K_MODE_11B:
+	case AR5K_MODE_11G:
+	case AR5K_MODE_11G_TURBO:
+		size = 26;
+		chfreq = CHANNEL_2GHZ;
+		break;
+	default:
+		return 0;
+	}
+
+	for (i = 0, count = 0; i < size && max > 0; i++) {
+		ch = i + 1 ;
+		freq = ath5k_ieee2mhz(ch);
+
+		/* Check if channel is supported by the chipset */
+		if (!ath5k_channel_ok(ah, freq, chfreq))
+			continue;
+
+		/* Write channel info and increment counter */
+		channels[count].center_freq = freq;
+		channels[count].maxpower = 0; /* use regulatory */
+		channels[count].band = (chfreq == CHANNEL_2GHZ) ?
+			NET80211_BAND_2GHZ : NET80211_BAND_5GHZ;
+		switch (mode) {
+		case AR5K_MODE_11A:
+		case AR5K_MODE_11G:
+			channels[count].hw_value = chfreq | CHANNEL_OFDM;
+			break;
+		case AR5K_MODE_11A_TURBO:
+		case AR5K_MODE_11G_TURBO:
+			channels[count].hw_value = chfreq |
+				CHANNEL_OFDM | CHANNEL_TURBO;
+			break;
+		case AR5K_MODE_11B:
+			channels[count].hw_value = CHANNEL_B;
+		}
+
+		count++;
+		max--;
+	}
+
+	return count;
+}
+
+static int
+ath5k_setup_bands(struct net80211_device *dev)
+{
+	struct ath5k_softc *sc = dev->priv;
+	struct ath5k_hw *ah = sc->ah;
+	int max_c, count_c = 0;
+	int i;
+	int band;
+
+	max_c = sizeof(sc->hwinfo->channels) / sizeof(sc->hwinfo->channels[0]);
+
+	/* 2GHz band */
+	if (sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11G) {
+		/* G mode */
+		band = NET80211_BAND_2GHZ;
+		sc->hwinfo->bands = NET80211_BAND_BIT_2GHZ;
+		sc->hwinfo->modes = (NET80211_MODE_G | NET80211_MODE_B);
+
+		for (i = 0; i < 12; i++)
+			sc->hwinfo->rates[band][i] = ath5k_rates[i].bitrate;
+		sc->hwinfo->nr_rates[band] = 12;
+
+		sc->hwinfo->nr_channels =
+			ath5k_copy_channels(ah, sc->hwinfo->channels,
+					    AR5K_MODE_11G, max_c);
+		count_c = sc->hwinfo->nr_channels;
+		max_c -= count_c;
+	} else if (sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11B) {
+		/* B mode */
+		band = NET80211_BAND_2GHZ;
+		sc->hwinfo->bands = NET80211_BAND_BIT_2GHZ;
+		sc->hwinfo->modes = NET80211_MODE_B;
+
+		for (i = 0; i < 4; i++)
+			sc->hwinfo->rates[band][i] = ath5k_rates[i].bitrate;
+		sc->hwinfo->nr_rates[band] = 4;
+
+		sc->hwinfo->nr_channels =
+			ath5k_copy_channels(ah, sc->hwinfo->channels,
+					    AR5K_MODE_11B, max_c);
+		count_c = sc->hwinfo->nr_channels;
+		max_c -= count_c;
+	}
+
+	/* 5GHz band, A mode */
+	if (sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11A) {
+		band = NET80211_BAND_5GHZ;
+		sc->hwinfo->bands |= NET80211_BAND_BIT_5GHZ;
+		sc->hwinfo->modes |= NET80211_MODE_A;
+
+		for (i = 0; i < 8; i++)
+			sc->hwinfo->rates[band][i] = ath5k_rates[i+4].bitrate;
+		sc->hwinfo->nr_rates[band] = 8;
+
+		sc->hwinfo->nr_channels =
+			ath5k_copy_channels(ah, sc->hwinfo->channels,
+					    AR5K_MODE_11B, max_c);
+		count_c = sc->hwinfo->nr_channels;
+		max_c -= count_c;
+	}
+
+	return 0;
+}
+
+/*
+ * Set/change channels.  If the channel is really being changed,
+ * it's done by reseting the chip.  To accomplish this we must
+ * first cleanup any pending DMA, then restart stuff after a la
+ * ath5k_init.
+ */
+static int
+ath5k_chan_set(struct ath5k_softc *sc, struct net80211_channel *chan)
+{
+	if (chan->center_freq != sc->curchan->center_freq ||
+	    chan->hw_value != sc->curchan->hw_value) {
+		/*
+		 * To switch channels clear any pending DMA operations;
+		 * wait long enough for the RX fifo to drain, reset the
+		 * hardware at the new frequency, and then re-enable
+		 * the relevant bits of the h/w.
+		 */
+		DBG2("ath5k: resetting for channel change (%d -> %d MHz)\n",
+		     sc->curchan->center_freq, chan->center_freq);
+		return ath5k_reset(sc, chan);
+	}
+
+	return 0;
+}
+
+static void
+ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
+{
+	sc->curmode = mode;
+
+	if (mode == AR5K_MODE_11A) {
+		sc->curband = NET80211_BAND_5GHZ;
+	} else {
+		sc->curband = NET80211_BAND_2GHZ;
+	}
+}
+
+static void
+ath5k_mode_setup(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+	u32 rfilt;
+
+	/* configure rx filter */
+	rfilt = sc->filter_flags;
+	ath5k_hw_set_rx_filter(ah, rfilt);
+
+	if (ath5k_hw_hasbssidmask(ah))
+		ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
+
+	/* configure operational mode */
+	ath5k_hw_set_opmode(ah);
+
+	ath5k_hw_set_mcast_filter(ah, 0, 0);
+}
+
+static inline int
+ath5k_hw_rix_to_bitrate(int hw_rix)
+{
+	int i;
+
+	for (i = 0; i < ATH5K_NR_RATES; i++) {
+		if (ath5k_rates[i].hw_code == hw_rix)
+			return ath5k_rates[i].bitrate;
+	}
+
+	DBG("ath5k: invalid rix %02x\n", hw_rix);
+	return 10;		/* use lowest rate */
+}
+
+int ath5k_bitrate_to_hw_rix(int bitrate)
+{
+	int i;
+
+	for (i = 0; i < ATH5K_NR_RATES; i++) {
+		if (ath5k_rates[i].bitrate == bitrate)
+			return ath5k_rates[i].hw_code;
+	}
+
+	DBG("ath5k: invalid bitrate %d\n", bitrate);
+	return ATH5K_RATE_CODE_1M; /* use lowest rate */
+}
+
+/***************\
+* Buffers setup *
+\***************/
+
+static struct io_buffer *
+ath5k_rx_iob_alloc(struct ath5k_softc *sc, u32 *iob_addr)
+{
+	struct io_buffer *iob;
+	unsigned int off;
+
+	/*
+	 * Allocate buffer with headroom_needed space for the
+	 * fake physical layer header at the start.
+	 */
+	iob = alloc_iob(sc->rxbufsize + sc->cachelsz - 1);
+
+	if (!iob) {
+		DBG("ath5k: can't alloc iobuf of size %d\n",
+		    sc->rxbufsize + sc->cachelsz - 1);
+		return NULL;
+	}
+
+	*iob_addr = virt_to_bus(iob->data);
+
+	/*
+	 * Cache-line-align.  This is important (for the
+	 * 5210 at least) as not doing so causes bogus data
+	 * in rx'd frames.
+	 */
+	off = *iob_addr % sc->cachelsz;
+	if (off != 0) {
+		iob_reserve(iob, sc->cachelsz - off);
+		*iob_addr += sc->cachelsz - off;
+	}
+
+	return iob;
+}
+
+static int
+ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct io_buffer *iob = bf->iob;
+	struct ath5k_desc *ds;
+
+	if (!iob) {
+		iob = ath5k_rx_iob_alloc(sc, &bf->iobaddr);
+		if (!iob)
+			return -ENOMEM;
+		bf->iob = iob;
+	}
+
+	/*
+	 * Setup descriptors.  For receive we always terminate
+	 * the descriptor list with a self-linked entry so we'll
+	 * not get overrun under high load (as can happen with a
+	 * 5212 when ANI processing enables PHY error frames).
+	 *
+	 * To insure the last descriptor is self-linked we create
+	 * each descriptor as self-linked and add it to the end.  As
+	 * each additional descriptor is added the previous self-linked
+	 * entry is ``fixed'' naturally.  This should be safe even
+	 * if DMA is happening.  When processing RX interrupts we
+	 * never remove/process the last, self-linked, entry on the
+	 * descriptor list.  This insures the hardware always has
+	 * someplace to write a new frame.
+	 */
+	ds = bf->desc;
+	ds->ds_link = bf->daddr;	/* link to self */
+	ds->ds_data = bf->iobaddr;
+	if (ah->ah_setup_rx_desc(ah, ds,
+				 iob_tailroom(iob),	/* buffer size */
+				 0) != 0) {
+		DBG("ath5k: error setting up RX descriptor for %d bytes\n", iob_tailroom(iob));
+		return -EINVAL;
+	}
+
+	if (sc->rxlink != NULL)
+		*sc->rxlink = bf->daddr;
+	sc->rxlink = &ds->ds_link;
+	return 0;
+}
+
+static int
+ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct ath5k_txq *txq = &sc->txq;
+	struct ath5k_desc *ds = bf->desc;
+	struct io_buffer *iob = bf->iob;
+	unsigned int pktlen, flags;
+	int ret;
+	u16 duration = 0;
+	u16 cts_rate = 0;
+
+	flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
+	bf->iobaddr = virt_to_bus(iob->data);
+	pktlen = iob_len(iob);
+
+	/* FIXME: If we are in g mode and rate is a CCK rate
+	 * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
+	 * from tx power (value is in dB units already) */
+	if (sc->dev->phy_flags & NET80211_PHY_USE_PROTECTION) {
+		struct net80211_device *dev = sc->dev;
+
+		flags |= AR5K_TXDESC_CTSENA;
+		cts_rate = sc->hw_rtscts_rate;
+		duration = net80211_cts_duration(dev, pktlen);
+	}
+	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
+				   IEEE80211_TYP_FRAME_HEADER_LEN,
+				   AR5K_PKT_TYPE_NORMAL, sc->power_level * 2,
+				   sc->hw_rate, ATH5K_RETRIES,
+				   AR5K_TXKEYIX_INVALID, 0, flags,
+				   cts_rate, duration);
+	if (ret)
+		return ret;
+
+	ds->ds_link = 0;
+	ds->ds_data = bf->iobaddr;
+
+	list_add_tail(&bf->list, &txq->q);
+	if (txq->link == NULL) /* is this first packet? */
+		ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
+	else /* no, so only link it */
+		*txq->link = bf->daddr;
+
+	txq->link = &ds->ds_link;
+	ath5k_hw_start_tx_dma(ah, txq->qnum);
+	mb();
+
+	return 0;
+}
+
+/*******************\
+* Descriptors setup *
+\*******************/
+
+static int
+ath5k_desc_alloc(struct ath5k_softc *sc)
+{
+	struct ath5k_desc *ds;
+	struct ath5k_buf *bf;
+	u32 da;
+	unsigned int i;
+	int ret;
+
+	/* allocate descriptors */
+	sc->desc_len = sizeof(struct ath5k_desc) * (ATH_TXBUF + ATH_RXBUF + 1);
+	sc->desc = malloc_dma(sc->desc_len, ATH5K_DESC_ALIGN);
+	if (sc->desc == NULL) {
+		DBG("ath5k: can't allocate descriptors\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	memset(sc->desc, 0, sc->desc_len);
+	sc->desc_daddr = virt_to_bus(sc->desc);
+
+	ds = sc->desc;
+	da = sc->desc_daddr;
+
+	bf = calloc(ATH_TXBUF + ATH_RXBUF + 1, sizeof(struct ath5k_buf));
+	if (bf == NULL) {
+		DBG("ath5k: can't allocate buffer pointers\n");
+		ret = -ENOMEM;
+		goto err_free;
+	}
+	sc->bufptr = bf;
+
+	INIT_LIST_HEAD(&sc->rxbuf);
+	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+		bf->desc = ds;
+		bf->daddr = da;
+		list_add_tail(&bf->list, &sc->rxbuf);
+	}
+
+	INIT_LIST_HEAD(&sc->txbuf);
+	sc->txbuf_len = ATH_TXBUF;
+	for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+		bf->desc = ds;
+		bf->daddr = da;
+		list_add_tail(&bf->list, &sc->txbuf);
+	}
+
+	return 0;
+
+err_free:
+	free_dma(sc->desc, sc->desc_len);
+err:
+	sc->desc = NULL;
+	return ret;
+}
+
+static void
+ath5k_desc_free(struct ath5k_softc *sc)
+{
+	struct ath5k_buf *bf;
+
+	list_for_each_entry(bf, &sc->txbuf, list)
+		ath5k_txbuf_free(sc, bf);
+	list_for_each_entry(bf, &sc->rxbuf, list)
+		ath5k_rxbuf_free(sc, bf);
+
+	/* Free memory associated with all descriptors */
+	free_dma(sc->desc, sc->desc_len);
+
+	free(sc->bufptr);
+	sc->bufptr = NULL;
+}
+
+
+
+
+
+/**************\
+* Queues setup *
+\**************/
+
+static int
+ath5k_txq_setup(struct ath5k_softc *sc, int qtype, int subtype)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct ath5k_txq *txq;
+	struct ath5k_txq_info qi = {
+		.tqi_subtype = subtype,
+		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_max = AR5K_TXQ_USEDEFAULT
+	};
+	int qnum;
+
+	/*
+	 * Enable interrupts only for EOL and DESC conditions.
+	 * We mark tx descriptors to receive a DESC interrupt
+	 * when a tx queue gets deep; otherwise waiting for the
+	 * EOL to reap descriptors.  Note that this is done to
+	 * reduce interrupt load and this only defers reaping
+	 * descriptors, never transmitting frames.  Aside from
+	 * reducing interrupts this also permits more concurrency.
+	 * The only potential downside is if the tx queue backs
+	 * up in which case the top half of the kernel may backup
+	 * due to a lack of tx descriptors.
+	 */
+	qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
+				AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
+	qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
+	if (qnum < 0) {
+		DBG("ath5k: can't set up a TX queue\n");
+		return -EIO;
+	}
+
+	txq = &sc->txq;
+	if (!txq->setup) {
+		txq->qnum = qnum;
+		txq->link = NULL;
+		INIT_LIST_HEAD(&txq->q);
+		txq->setup = 1;
+	}
+	return 0;
+}
+
+static void
+ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+{
+	struct ath5k_buf *bf, *bf0;
+
+	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+		ath5k_txbuf_free(sc, bf);
+
+		list_del(&bf->list);
+		list_add_tail(&bf->list, &sc->txbuf);
+		sc->txbuf_len++;
+	}
+	txq->link = NULL;
+}
+
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+static void
+ath5k_txq_cleanup(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+
+	if (!(sc->status & ATH_STAT_INVALID)) {
+		/* don't touch the hardware if marked invalid */
+		if (sc->txq.setup) {
+			ath5k_hw_stop_tx_dma(ah, sc->txq.qnum);
+			DBG("ath5k: txq [%d] %x, link %p\n",
+			    sc->txq.qnum,
+			    ath5k_hw_get_txdp(ah, sc->txq.qnum),
+			    sc->txq.link);
+		}
+	}
+
+	if (sc->txq.setup)
+		ath5k_txq_drainq(sc, &sc->txq);
+}
+
+static void
+ath5k_txq_release(struct ath5k_softc *sc)
+{
+	if (sc->txq.setup) {
+		ath5k_hw_release_tx_queue(sc->ah);
+		sc->txq.setup = 0;
+	}
+}
+
+
+
+
+/*************\
+* RX Handling *
+\*************/
+
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int
+ath5k_rx_start(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct ath5k_buf *bf;
+	int ret;
+
+	sc->rxbufsize = IEEE80211_MAX_LEN;
+	if (sc->rxbufsize % sc->cachelsz != 0)
+		sc->rxbufsize += sc->cachelsz - (sc->rxbufsize % sc->cachelsz);
+
+	sc->rxlink = NULL;
+
+	list_for_each_entry(bf, &sc->rxbuf, list) {
+		ret = ath5k_rxbuf_setup(sc, bf);
+		if (ret != 0)
+			return ret;
+	}
+
+	bf = list_entry(sc->rxbuf.next, struct ath5k_buf, list);
+
+	ath5k_hw_set_rxdp(ah, bf->daddr);
+	ath5k_hw_start_rx_dma(ah);	/* enable recv descriptors */
+	ath5k_mode_setup(sc);		/* set filters, etc. */
+	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */
+
+	return 0;
+}
+
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void
+ath5k_rx_stop(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+
+	ath5k_hw_stop_rx_pcu(ah);	/* disable PCU */
+	ath5k_hw_set_rx_filter(ah, 0);	/* clear recv filter */
+	ath5k_hw_stop_rx_dma(ah);	/* disable DMA engine */
+
+	sc->rxlink = NULL;		/* just in case */
+}
+
+static void
+ath5k_handle_rx(struct ath5k_softc *sc)
+{
+	struct ath5k_rx_status rs;
+	struct io_buffer *iob, *next_iob;
+	u32 next_iob_addr;
+	struct ath5k_buf *bf, *bf_last;
+	struct ath5k_desc *ds;
+	int ret;
+
+	memset(&rs, 0, sizeof(rs));
+
+	if (list_empty(&sc->rxbuf)) {
+		DBG("ath5k: empty rx buf pool\n");
+		return;
+	}
+
+	bf_last = list_entry(sc->rxbuf.prev, struct ath5k_buf, list);
+
+	do {
+		bf = list_entry(sc->rxbuf.next, struct ath5k_buf, list);
+		assert(bf->iob != NULL);
+		iob = bf->iob;
+		ds = bf->desc;
+
+		/*
+		 * last buffer must not be freed to ensure proper hardware
+		 * function. When the hardware finishes also a packet next to
+		 * it, we are sure, it doesn't use it anymore and we can go on.
+		 */
+		if (bf_last == bf)
+			bf->flags |= 1;
+		if (bf->flags) {
+			struct ath5k_buf *bf_next = list_entry(bf->list.next,
+					struct ath5k_buf, list);
+			ret = sc->ah->ah_proc_rx_desc(sc->ah, bf_next->desc,
+					&rs);
+			if (ret)
+				break;
+			bf->flags &= ~1;
+			/* skip the overwritten one (even status is martian) */
+			goto next;
+		}
+
+		ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs);
+		if (ret) {
+			if (ret != -EINPROGRESS) {
+				DBG("ath5k: error in processing rx desc: %s\n",
+				    strerror(ret));
+				net80211_rx_err(sc->dev, NULL, -ret);
+			} else {
+				/* normal return, reached end of
+				   available descriptors */
+			}
+			return;
+		}
+
+		if (rs.rs_more) {
+			DBG("ath5k: unsupported fragmented rx\n");
+			goto next;
+		}
+
+		if (rs.rs_status) {
+			if (rs.rs_status & AR5K_RXERR_PHY) {
+				/* These are uncommon, and may indicate a real problem. */
+				net80211_rx_err(sc->dev, NULL, EIO);
+				goto next;
+			}
+			if (rs.rs_status & AR5K_RXERR_CRC) {
+				/* These occur *all the time*. */
+				goto next;
+			}
+			if (rs.rs_status & AR5K_RXERR_DECRYPT) {
+				/*
+				 * Decrypt error.  If the error occurred
+				 * because there was no hardware key, then
+				 * let the frame through so the upper layers
+				 * can process it.  This is necessary for 5210
+				 * parts which have no way to setup a ``clear''
+				 * key cache entry.
+				 *
+				 * XXX do key cache faulting
+				 */
+				if (rs.rs_keyix == AR5K_RXKEYIX_INVALID &&
+				    !(rs.rs_status & AR5K_RXERR_CRC))
+					goto accept;
+			}
+
+			/* any other error, unhandled */
+			DBG("ath5k: packet rx status %x\n", rs.rs_status);
+			goto next;
+		}
+accept:
+		next_iob = ath5k_rx_iob_alloc(sc, &next_iob_addr);
+
+		/*
+		 * If we can't replace bf->iob with a new iob under memory
+		 * pressure, just skip this packet
+		 */
+		if (!next_iob) {
+			DBG("ath5k: dropping packet under memory pressure\n");
+			goto next;
+		}
+
+		iob_put(iob, rs.rs_datalen);
+
+		/* The MAC header is padded to have 32-bit boundary if the
+		 * packet payload is non-zero. However, gPXE only
+		 * supports standard 802.11 packets with 24-byte
+		 * header, so no padding correction should be needed.
+		 */
+
+		DBG2("ath5k: rx %d bytes, signal %d\n", rs.rs_datalen,
+		     rs.rs_rssi);
+
+		net80211_rx(sc->dev, iob, rs.rs_rssi,
+			    ath5k_hw_rix_to_bitrate(rs.rs_rate));
+
+		bf->iob = next_iob;
+		bf->iobaddr = next_iob_addr;
+next:
+		list_del(&bf->list);
+		list_add_tail(&bf->list, &sc->rxbuf);
+	} while (ath5k_rxbuf_setup(sc, bf) == 0);
+}
+
+
+
+
+/*************\
+* TX Handling *
+\*************/
+
+static void
+ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+{
+	struct ath5k_tx_status ts;
+	struct ath5k_buf *bf, *bf0;
+	struct ath5k_desc *ds;
+	struct io_buffer *iob;
+	int ret;
+
+	memset(&ts, 0, sizeof(ts));
+
+	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+		ds = bf->desc;
+
+		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
+		if (ret) {
+			if (ret != -EINPROGRESS) {
+				DBG("ath5k: error in processing tx desc: %s\n",
+				    strerror(ret));
+			} else {
+				/* normal return, reached end of tx completions */
+			}
+			break;
+		}
+
+		iob = bf->iob;
+		bf->iob = NULL;
+
+		DBG2("ath5k: tx %d bytes complete, %d retries\n",
+		     iob_len(iob), ts.ts_retry[0]);
+
+		net80211_tx_complete(sc->dev, iob, ts.ts_retry[0],
+				     ts.ts_status ? EIO : 0);
+
+		list_del(&bf->list);
+		list_add_tail(&bf->list, &sc->txbuf);
+		sc->txbuf_len++;
+	}
+
+	if (list_empty(&txq->q))
+		txq->link = NULL;
+}
+
+static void
+ath5k_handle_tx(struct ath5k_softc *sc)
+{
+	ath5k_tx_processq(sc, &sc->txq);
+}
+
+
+/********************\
+* Interrupt handling *
+\********************/
+
+static void
+ath5k_irq(struct net80211_device *dev, int enable)
+{
+	struct ath5k_softc *sc = dev->priv;
+	struct ath5k_hw *ah = sc->ah;
+
+	sc->irq_ena = enable;
+	ah->ah_ier = enable ? AR5K_IER_ENABLE : AR5K_IER_DISABLE;
+
+	ath5k_hw_reg_write(ah, ah->ah_ier, AR5K_IER);
+	ath5k_hw_set_imr(ah, sc->imask);
+}
+
+static int
+ath5k_init(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+	int ret, i;
+
+	/*
+	 * Stop anything previously setup.  This is safe
+	 * no matter this is the first time through or not.
+	 */
+	ath5k_stop_hw(sc);
+
+	/*
+	 * The basic interface to setting the hardware in a good
+	 * state is ``reset''.  On return the hardware is known to
+	 * be powered up and with interrupts disabled.  This must
+	 * be followed by initialization of the appropriate bits
+	 * and then setup of the interrupt mask.
+	 */
+	sc->curchan = sc->dev->channels + sc->dev->channel;
+	sc->curband = sc->curchan->band;
+	sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
+		AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
+		AR5K_INT_FATAL | AR5K_INT_GLOBAL;
+	ret = ath5k_reset(sc, NULL);
+	if (ret)
+		goto done;
+
+	ath5k_rfkill_hw_start(ah);
+
+	/*
+	 * Reset the key cache since some parts do not reset the
+	 * contents on initial power up or resume from suspend.
+	 */
+	for (i = 0; i < AR5K_KEYTABLE_SIZE; i++)
+		ath5k_hw_reset_key(ah, i);
+
+	/* Set ack to be sent at low bit-rates */
+	ath5k_hw_set_ack_bitrate_high(ah, 0);
+
+	ret = 0;
+done:
+	mb();
+	return ret;
+}
+
+static int
+ath5k_stop_hw(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+
+	/*
+	 * Shutdown the hardware and driver:
+	 *    stop output from above
+	 *    disable interrupts
+	 *    turn off timers
+	 *    turn off the radio
+	 *    clear transmit machinery
+	 *    clear receive machinery
+	 *    drain and release tx queues
+	 *    reclaim beacon resources
+	 *    power down hardware
+	 *
+	 * Note that some of this work is not possible if the
+	 * hardware is gone (invalid).
+	 */
+
+	if (!(sc->status & ATH_STAT_INVALID)) {
+		ath5k_hw_set_imr(ah, 0);
+	}
+	ath5k_txq_cleanup(sc);
+	if (!(sc->status & ATH_STAT_INVALID)) {
+		ath5k_rx_stop(sc);
+		ath5k_hw_phy_disable(ah);
+	} else
+		sc->rxlink = NULL;
+
+	ath5k_rfkill_hw_stop(sc->ah);
+
+	return 0;
+}
+
+static void
+ath5k_poll(struct net80211_device *dev)
+{
+	struct ath5k_softc *sc = dev->priv;
+	struct ath5k_hw *ah = sc->ah;
+	enum ath5k_int status;
+	unsigned int counter = 1000;
+
+	if (currticks() - sc->last_calib_ticks >
+	    ATH5K_CALIB_INTERVAL * ticks_per_sec()) {
+		ath5k_calibrate(sc);
+		sc->last_calib_ticks = currticks();
+	}
+
+	if ((sc->status & ATH_STAT_INVALID) ||
+	    (sc->irq_ena && !ath5k_hw_is_intr_pending(ah)))
+		return;
+
+	do {
+		ath5k_hw_get_isr(ah, &status);		/* NB: clears IRQ too */
+		DBGP("ath5k: status %#x/%#x\n", status, sc->imask);
+		if (status & AR5K_INT_FATAL) {
+			/*
+			 * Fatal errors are unrecoverable.
+			 * Typically these are caused by DMA errors.
+			 */
+			DBG("ath5k: fatal error, resetting\n");
+			ath5k_reset_wake(sc);
+		} else if (status & AR5K_INT_RXORN) {
+			DBG("ath5k: rx overrun, resetting\n");
+			ath5k_reset_wake(sc);
+		} else {
+			if (status & AR5K_INT_RXEOL) {
+				/*
+				 * NB: the hardware should re-read the link when
+				 *     RXE bit is written, but it doesn't work at
+				 *     least on older hardware revs.
+				 */
+				DBG("ath5k: rx EOL\n");
+				sc->rxlink = NULL;
+			}
+			if (status & AR5K_INT_TXURN) {
+				/* bump tx trigger level */
+				DBG("ath5k: tx underrun\n");
+				ath5k_hw_update_tx_triglevel(ah, 1);
+			}
+			if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
+				ath5k_handle_rx(sc);
+			if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
+				      | AR5K_INT_TXERR | AR5K_INT_TXEOL))
+				ath5k_handle_tx(sc);
+		}
+	} while (ath5k_hw_is_intr_pending(ah) && counter-- > 0);
+
+	if (!counter)
+		DBG("ath5k: too many interrupts, giving up for now\n");
+}
+
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
+static void
+ath5k_calibrate(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+
+	if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
+		/*
+		 * Rfgain is out of bounds, reset the chip
+		 * to load new gain values.
+		 */
+		DBG("ath5k: resetting for calibration\n");
+		ath5k_reset_wake(sc);
+	}
+	if (ath5k_hw_phy_calibrate(ah, sc->curchan))
+		DBG("ath5k: calibration of channel %d failed\n",
+		    sc->curchan->channel_nr);
+}
+
+
+/********************\
+* Net80211 functions *
+\********************/
+
+static int
+ath5k_tx(struct net80211_device *dev, struct io_buffer *iob)
+{
+	struct ath5k_softc *sc = dev->priv;
+	struct ath5k_buf *bf;
+	int rc;
+
+	/*
+	 * The hardware expects the header padded to 4 byte boundaries.
+	 * gPXE only ever sends 24-byte headers, so no action necessary.
+	 */
+
+	if (list_empty(&sc->txbuf)) {
+		DBG("ath5k: dropping packet because no tx bufs available\n");
+		return -ENOBUFS;
+	}
+
+	bf = list_entry(sc->txbuf.next, struct ath5k_buf, list);
+	list_del(&bf->list);
+	sc->txbuf_len--;
+
+	bf->iob = iob;
+
+	if ((rc = ath5k_txbuf_setup(sc, bf)) != 0) {
+		bf->iob = NULL;
+		list_add_tail(&bf->list, &sc->txbuf);
+		sc->txbuf_len++;
+		return rc;
+	}
+	return 0;
+}
+
+/*
+ * Reset the hardware.  If chan is not NULL, then also pause rx/tx
+ * and change to the given channel.
+ */
+static int
+ath5k_reset(struct ath5k_softc *sc, struct net80211_channel *chan)
+{
+	struct ath5k_hw *ah = sc->ah;
+	int ret;
+
+	if (chan) {
+		ath5k_hw_set_imr(ah, 0);
+		ath5k_txq_cleanup(sc);
+		ath5k_rx_stop(sc);
+
+		sc->curchan = chan;
+		sc->curband = chan->band;
+	}
+
+	ret = ath5k_hw_reset(ah, sc->curchan, 1);
+	if (ret) {
+		DBG("ath5k: can't reset hardware: %s\n", strerror(ret));
+		return ret;
+	}
+
+	ret = ath5k_rx_start(sc);
+	if (ret) {
+		DBG("ath5k: can't start rx logic: %s\n", strerror(ret));
+		return ret;
+	}
+
+	/*
+	 * Change channels and update the h/w rate map if we're switching;
+	 * e.g. 11a to 11b/g.
+	 *
+	 * We may be doing a reset in response to an ioctl that changes the
+	 * channel so update any state that might change as a result.
+	 *
+	 * XXX needed?
+	 */
+/*	ath5k_chan_change(sc, c); */
+
+	/* Reenable interrupts if necessary */
+	ath5k_irq(sc->dev, sc->irq_ena);
+
+	return 0;
+}
+
+static int ath5k_reset_wake(struct ath5k_softc *sc)
+{
+	return ath5k_reset(sc, sc->curchan);
+}
+
+static int ath5k_start(struct net80211_device *dev)
+{
+	struct ath5k_softc *sc = dev->priv;
+	int ret;
+
+	if ((ret = ath5k_init(sc)) != 0)
+		return ret;
+
+	sc->assoc = 0;
+	ath5k_configure_filter(sc);
+	ath5k_hw_set_lladdr(sc->ah, dev->netdev->ll_addr);
+
+	return 0;
+}
+
+static void ath5k_stop(struct net80211_device *dev)
+{
+	struct ath5k_softc *sc = dev->priv;
+	u8 mac[ETH_ALEN] = {};
+
+	ath5k_hw_set_lladdr(sc->ah, mac);
+
+	ath5k_stop_hw(sc);
+}
+
+static int
+ath5k_config(struct net80211_device *dev, int changed)
+{
+	struct ath5k_softc *sc = dev->priv;
+	struct ath5k_hw *ah = sc->ah;
+	struct net80211_channel *chan = &dev->channels[dev->channel];
+	int ret;
+
+	if (changed & NET80211_CFG_CHANNEL) {
+		sc->power_level = chan->maxpower;
+		if ((ret = ath5k_chan_set(sc, chan)) != 0)
+			return ret;
+	}
+
+	if ((changed & NET80211_CFG_RATE) ||
+	    (changed & NET80211_CFG_PHY_PARAMS)) {
+		int spmbl = ATH5K_SPMBL_NO;
+		u16 rate = dev->rates[dev->rate];
+		u16 slowrate = dev->rates[dev->rtscts_rate];
+		int i;
+
+		if (dev->phy_flags & NET80211_PHY_USE_SHORT_PREAMBLE)
+			spmbl = ATH5K_SPMBL_YES;
+
+		for (i = 0; i < ATH5K_NR_RATES; i++) {
+			if (ath5k_rates[i].bitrate == rate &&
+			    (ath5k_rates[i].short_pmbl & spmbl))
+				sc->hw_rate = ath5k_rates[i].hw_code;
+
+			if (ath5k_rates[i].bitrate == slowrate &&
+			    (ath5k_rates[i].short_pmbl & spmbl))
+				sc->hw_rtscts_rate = ath5k_rates[i].hw_code;
+		}
+	}
+
+	if (changed & NET80211_CFG_ASSOC) {
+		sc->assoc = !!(dev->state & NET80211_ASSOCIATED);
+		if (sc->assoc) {
+			memcpy(ah->ah_bssid, dev->bssid, ETH_ALEN);
+		} else {
+			memset(ah->ah_bssid, 0xff, ETH_ALEN);
+		}
+		ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
+	}
+
+	return 0;
+}
+
+/*
+ * o always accept unicast, broadcast, and multicast traffic
+ * o multicast traffic for all BSSIDs will be enabled if mac80211
+ *   says it should be
+ * o maintain current state of phy ofdm or phy cck error reception.
+ *   If the hardware detects any of these type of errors then
+ *   ath5k_hw_get_rx_filter() will pass to us the respective
+ *   hardware filters to be able to receive these type of frames.
+ * o probe request frames are accepted only when operating in
+ *   hostap, adhoc, or monitor modes
+ * o enable promiscuous mode according to the interface state
+ * o accept beacons:
+ *   - when operating in adhoc mode so the 802.11 layer creates
+ *     node table entries for peers,
+ *   - when operating in station mode for collecting rssi data when
+ *     the station is otherwise quiet, or
+ *   - when scanning
+ */
+static void ath5k_configure_filter(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+	u32 mfilt[2], rfilt;
+
+	/* Enable all multicast */
+	mfilt[0] = ~0;
+	mfilt[1] = ~0;
+
+	/* Enable data frames and beacons */
+	rfilt = (AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST |
+		 AR5K_RX_FILTER_MCAST | AR5K_RX_FILTER_BEACON);
+
+	/* Set filters */
+	ath5k_hw_set_rx_filter(ah, rfilt);
+
+	/* Set multicast bits */
+	ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
+
+	/* Set the cached hw filter flags, this will alter actually
+	 * be set in HW */
+	sc->filter_flags = rfilt;
+}
diff --git a/gpxe/src/drivers/net/ath5k/ath5k.h b/gpxe/src/drivers/net/ath5k/ath5k.h
new file mode 100644
index 0000000..e54433d
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k.h
@@ -0,0 +1,1279 @@
+/*
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2007 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>
+ * Original from Linux kernel 2.6.30.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _ATH5K_H
+#define _ATH5K_H
+
+FILE_LICENCE ( MIT );
+
+#include <stddef.h>
+#include <byteswap.h>
+#include <gpxe/io.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/net80211.h>
+#include <errno.h>
+
+/* Keep all ath5k files under one errfile ID */
+#undef ERRFILE
+#define ERRFILE ERRFILE_ath5k
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+/* RX/TX descriptor hw structs */
+#include "desc.h"
+
+/* EEPROM structs/offsets */
+#include "eeprom.h"
+
+/* PCI IDs */
+#define PCI_DEVICE_ID_ATHEROS_AR5210 		0x0007 /* AR5210 */
+#define PCI_DEVICE_ID_ATHEROS_AR5311 		0x0011 /* AR5311 */
+#define PCI_DEVICE_ID_ATHEROS_AR5211 		0x0012 /* AR5211 */
+#define PCI_DEVICE_ID_ATHEROS_AR5212 		0x0013 /* AR5212 */
+#define PCI_DEVICE_ID_3COM_3CRDAG675 		0x0013 /* 3CRDAG675 (Atheros AR5212) */
+#define PCI_DEVICE_ID_3COM_2_3CRPAG175 		0x0013 /* 3CRPAG175 (Atheros AR5212) */
+#define PCI_DEVICE_ID_ATHEROS_AR5210_AP 	0x0207 /* AR5210 (Early) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_IBM	0x1014 /* AR5212 (IBM MiniPCI) */
+#define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 	0x1107 /* AR5210 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 	0x1113 /* AR5212 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 	0x1112 /* AR5211 (no eeprom) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 	0xf013 /* AR5212 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 	0xff12 /* AR5211 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 	0xf11b /* AR5211 (emulation board) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV2 	0x0052 /* AR5312 WMAC (AP31) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV7 	0x0057 /* AR5312 WMAC (AP30-040) */
+#define PCI_DEVICE_ID_ATHEROS_AR5312_REV8 	0x0058 /* AR5312 WMAC (AP43-030) */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0014 	0x0014 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0015 	0x0015 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0016 	0x0016 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0017 	0x0017 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0018 	0x0018 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR5212_0019 	0x0019 /* AR5212 compatible */
+#define PCI_DEVICE_ID_ATHEROS_AR2413 		0x001a /* AR2413 (Griffin-lite) */
+#define PCI_DEVICE_ID_ATHEROS_AR5413 		0x001b /* AR5413 (Eagle) */
+#define PCI_DEVICE_ID_ATHEROS_AR5424 		0x001c /* AR5424 (Condor PCI-E) */
+#define PCI_DEVICE_ID_ATHEROS_AR5416 		0x0023 /* AR5416 */
+#define PCI_DEVICE_ID_ATHEROS_AR5418 		0x0024 /* AR5418 */
+
+/****************************\
+  GENERIC DRIVER DEFINITIONS
+\****************************/
+
+/*
+ * AR5K REGISTER ACCESS
+ */
+
+/* Some macros to read/write fields */
+
+/* First shift, then mask */
+#define AR5K_REG_SM(_val, _flags)					\
+	(((_val) << _flags##_S) & (_flags))
+
+/* First mask, then shift */
+#define AR5K_REG_MS(_val, _flags)					\
+	(((_val) & (_flags)) >> _flags##_S)
+
+/* Some registers can hold multiple values of interest. For this
+ * reason when we want to write to these registers we must first
+ * retrieve the values which we do not want to clear (lets call this
+ * old_data) and then set the register with this and our new_value:
+ * ( old_data | new_value) */
+#define AR5K_REG_WRITE_BITS(ah, _reg, _flags, _val)			\
+	ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) & ~(_flags)) | \
+	    (((_val) << _flags##_S) & (_flags)), _reg)
+
+#define AR5K_REG_MASKED_BITS(ah, _reg, _flags, _mask)			\
+	ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) &		\
+			(_mask)) | (_flags), _reg)
+
+#define AR5K_REG_ENABLE_BITS(ah, _reg, _flags)				\
+	ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) | (_flags), _reg)
+
+#define AR5K_REG_DISABLE_BITS(ah, _reg, _flags)			\
+	ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) & ~(_flags), _reg)
+
+/* Access to PHY registers */
+#define AR5K_PHY_READ(ah, _reg)					\
+	ath5k_hw_reg_read(ah, (ah)->ah_phy + ((_reg) << 2))
+
+#define AR5K_PHY_WRITE(ah, _reg, _val)					\
+	ath5k_hw_reg_write(ah, _val, (ah)->ah_phy + ((_reg) << 2))
+
+/* Access QCU registers per queue */
+#define AR5K_REG_READ_Q(ah, _reg, _queue)				\
+	(ath5k_hw_reg_read(ah, _reg) & (1 << _queue))			\
+
+#define AR5K_REG_WRITE_Q(ah, _reg, _queue)				\
+	ath5k_hw_reg_write(ah, (1 << _queue), _reg)
+
+#define AR5K_Q_ENABLE_BITS(_reg, _queue) do {				\
+	_reg |= 1 << _queue;						\
+} while (0)
+
+#define AR5K_Q_DISABLE_BITS(_reg, _queue) do {				\
+	_reg &= ~(1 << _queue);						\
+} while (0)
+
+/* Used while writing initvals */
+#define AR5K_REG_WAIT(_i) do {						\
+	if (_i % 64)							\
+		udelay(1);						\
+} while (0)
+
+/* Register dumps are done per operation mode */
+#define AR5K_INI_RFGAIN_5GHZ		0
+#define AR5K_INI_RFGAIN_2GHZ		1
+
+/* TODO: Clean this up */
+#define AR5K_INI_VAL_11A		0
+#define AR5K_INI_VAL_11A_TURBO		1
+#define AR5K_INI_VAL_11B		2
+#define AR5K_INI_VAL_11G		3
+#define AR5K_INI_VAL_11G_TURBO		4
+#define AR5K_INI_VAL_XR			0
+#define AR5K_INI_VAL_MAX		5
+
+/* Used for BSSID etc manipulation */
+#define AR5K_LOW_ID(_a)(				\
+(_a)[0] | (_a)[1] << 8 | (_a)[2] << 16 | (_a)[3] << 24	\
+)
+
+#define AR5K_HIGH_ID(_a)	((_a)[4] | (_a)[5] << 8)
+
+#define IEEE80211_MAX_LEN	2352
+
+/*
+ * Some tuneable values (these should be changeable by the user)
+ */
+#define AR5K_TUNE_DMA_BEACON_RESP		2
+#define AR5K_TUNE_SW_BEACON_RESP		10
+#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF	0
+#define AR5K_TUNE_RADAR_ALERT			0
+#define AR5K_TUNE_MIN_TX_FIFO_THRES		1
+#define AR5K_TUNE_MAX_TX_FIFO_THRES		((IEEE80211_MAX_LEN / 64) + 1)
+#define AR5K_TUNE_REGISTER_TIMEOUT		20000
+/* Register for RSSI threshold has a mask of 0xff, so 255 seems to
+ * be the max value. */
+#define AR5K_TUNE_RSSI_THRES			129
+/* This must be set when setting the RSSI threshold otherwise it can
+ * prevent a reset. If AR5K_RSSI_THR is read after writing to it
+ * the BMISS_THRES will be seen as 0, seems harware doesn't keep
+ * track of it. Max value depends on harware. For AR5210 this is just 7.
+ * For AR5211+ this seems to be up to 255. */
+#define AR5K_TUNE_BMISS_THRES			7
+#define AR5K_TUNE_REGISTER_DWELL_TIME		20000
+#define AR5K_TUNE_BEACON_INTERVAL		100
+#define AR5K_TUNE_AIFS				2
+#define AR5K_TUNE_AIFS_11B			2
+#define AR5K_TUNE_AIFS_XR			0
+#define AR5K_TUNE_CWMIN				15
+#define AR5K_TUNE_CWMIN_11B			31
+#define AR5K_TUNE_CWMIN_XR			3
+#define AR5K_TUNE_CWMAX				1023
+#define AR5K_TUNE_CWMAX_11B			1023
+#define AR5K_TUNE_CWMAX_XR			7
+#define AR5K_TUNE_NOISE_FLOOR			-72
+#define AR5K_TUNE_MAX_TXPOWER			63
+#define AR5K_TUNE_DEFAULT_TXPOWER		25
+#define AR5K_TUNE_TPC_TXPOWER			0
+#define AR5K_TUNE_ANT_DIVERSITY			1
+#define AR5K_TUNE_HWTXTRIES			4
+
+#define AR5K_INIT_CARR_SENSE_EN			1
+
+/*Swap RX/TX Descriptor for big endian archs*/
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define AR5K_INIT_CFG	(		\
+	AR5K_CFG_SWTD | AR5K_CFG_SWRD	\
+)
+#else
+#define AR5K_INIT_CFG	0x00000000
+#endif
+
+/* Initial values */
+#define	AR5K_INIT_CYCRSSI_THR1			2
+#define AR5K_INIT_TX_LATENCY			502
+#define AR5K_INIT_USEC				39
+#define AR5K_INIT_USEC_TURBO			79
+#define AR5K_INIT_USEC_32			31
+#define AR5K_INIT_SLOT_TIME			396
+#define AR5K_INIT_SLOT_TIME_TURBO		480
+#define AR5K_INIT_ACK_CTS_TIMEOUT		1024
+#define AR5K_INIT_ACK_CTS_TIMEOUT_TURBO		0x08000800
+#define AR5K_INIT_PROG_IFS			920
+#define AR5K_INIT_PROG_IFS_TURBO		960
+#define AR5K_INIT_EIFS				3440
+#define AR5K_INIT_EIFS_TURBO			6880
+#define AR5K_INIT_SIFS				560
+#define AR5K_INIT_SIFS_TURBO			480
+#define AR5K_INIT_SH_RETRY			10
+#define AR5K_INIT_LG_RETRY			AR5K_INIT_SH_RETRY
+#define AR5K_INIT_SSH_RETRY			32
+#define AR5K_INIT_SLG_RETRY			AR5K_INIT_SSH_RETRY
+#define AR5K_INIT_TX_RETRY			10
+
+#define AR5K_INIT_TRANSMIT_LATENCY		(			\
+	(AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) |	\
+	(AR5K_INIT_USEC)						\
+)
+#define AR5K_INIT_TRANSMIT_LATENCY_TURBO	(			\
+	(AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) |	\
+	(AR5K_INIT_USEC_TURBO)						\
+)
+#define AR5K_INIT_PROTO_TIME_CNTRL		(			\
+	(AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS << 12) |	\
+	(AR5K_INIT_PROG_IFS)						\
+)
+#define AR5K_INIT_PROTO_TIME_CNTRL_TURBO	(			\
+	(AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS_TURBO << 12) | \
+	(AR5K_INIT_PROG_IFS_TURBO)					\
+)
+
+/* token to use for aifs, cwmin, cwmax in MadWiFi */
+#define	AR5K_TXQ_USEDEFAULT	((u32) -1)
+
+/* GENERIC CHIPSET DEFINITIONS */
+
+/* MAC Chips */
+enum ath5k_version {
+	AR5K_AR5210	= 0,
+	AR5K_AR5211	= 1,
+	AR5K_AR5212	= 2,
+};
+
+/* PHY Chips */
+enum ath5k_radio {
+	AR5K_RF5110	= 0,
+	AR5K_RF5111	= 1,
+	AR5K_RF5112	= 2,
+	AR5K_RF2413	= 3,
+	AR5K_RF5413	= 4,
+	AR5K_RF2316	= 5,
+	AR5K_RF2317	= 6,
+	AR5K_RF2425	= 7,
+};
+
+/*
+ * Common silicon revision/version values
+ */
+
+enum ath5k_srev_type {
+	AR5K_VERSION_MAC,
+	AR5K_VERSION_RAD,
+};
+
+struct ath5k_srev_name {
+	const char		*sr_name;
+	enum ath5k_srev_type	sr_type;
+	unsigned		sr_val;
+};
+
+#define AR5K_SREV_UNKNOWN	0xffff
+
+#define AR5K_SREV_AR5210	0x00 /* Crete */
+#define AR5K_SREV_AR5311	0x10 /* Maui 1 */
+#define AR5K_SREV_AR5311A	0x20 /* Maui 2 */
+#define AR5K_SREV_AR5311B	0x30 /* Spirit */
+#define AR5K_SREV_AR5211	0x40 /* Oahu */
+#define AR5K_SREV_AR5212	0x50 /* Venice */
+#define AR5K_SREV_AR5213	0x55 /* ??? */
+#define AR5K_SREV_AR5213A	0x59 /* Hainan */
+#define AR5K_SREV_AR2413	0x78 /* Griffin lite */
+#define AR5K_SREV_AR2414	0x70 /* Griffin */
+#define AR5K_SREV_AR5424	0x90 /* Condor */
+#define AR5K_SREV_AR5413	0xa4 /* Eagle lite */
+#define AR5K_SREV_AR5414	0xa0 /* Eagle */
+#define AR5K_SREV_AR2415	0xb0 /* Talon */
+#define AR5K_SREV_AR5416	0xc0 /* PCI-E */
+#define AR5K_SREV_AR5418	0xca /* PCI-E */
+#define AR5K_SREV_AR2425	0xe0 /* Swan */
+#define AR5K_SREV_AR2417	0xf0 /* Nala */
+
+#define AR5K_SREV_RAD_5110	0x00
+#define AR5K_SREV_RAD_5111	0x10
+#define AR5K_SREV_RAD_5111A	0x15
+#define AR5K_SREV_RAD_2111	0x20
+#define AR5K_SREV_RAD_5112	0x30
+#define AR5K_SREV_RAD_5112A	0x35
+#define	AR5K_SREV_RAD_5112B	0x36
+#define AR5K_SREV_RAD_2112	0x40
+#define AR5K_SREV_RAD_2112A	0x45
+#define	AR5K_SREV_RAD_2112B	0x46
+#define AR5K_SREV_RAD_2413	0x50
+#define AR5K_SREV_RAD_5413	0x60
+#define AR5K_SREV_RAD_2316	0x70 /* Cobra SoC */
+#define AR5K_SREV_RAD_2317	0x80
+#define AR5K_SREV_RAD_5424	0xa0 /* Mostly same as 5413 */
+#define AR5K_SREV_RAD_2425	0xa2
+#define AR5K_SREV_RAD_5133	0xc0
+
+#define AR5K_SREV_PHY_5211	0x30
+#define AR5K_SREV_PHY_5212	0x41
+#define	AR5K_SREV_PHY_5212A	0x42
+#define AR5K_SREV_PHY_5212B	0x43
+#define AR5K_SREV_PHY_2413	0x45
+#define AR5K_SREV_PHY_5413	0x61
+#define AR5K_SREV_PHY_2425	0x70
+
+/*
+ * Some of this information is based on Documentation from:
+ *
+ * http://madwifi.org/wiki/ChipsetFeatures/SuperAG
+ *
+ * Modulation for Atheros' eXtended Range - range enhancing extension that is
+ * supposed to double the distance an Atheros client device can keep a
+ * connection with an Atheros access point. This is achieved by increasing
+ * the receiver sensitivity up to, -105dBm, which is about 20dB above what
+ * the 802.11 specifications demand. In addition, new (proprietary) data rates
+ * are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s.
+ *
+ * Please note that can you either use XR or TURBO but you cannot use both,
+ * they are exclusive.
+ *
+ */
+#define MODULATION_XR 		0x00000200
+
+/*
+ * Modulation for Atheros' Turbo G and Turbo A, its supposed to provide a
+ * throughput transmission speed up to 40Mbit/s-60Mbit/s at a 108Mbit/s
+ * signaling rate achieved through the bonding of two 54Mbit/s 802.11g
+ * channels. To use this feature your Access Point must also suport it.
+ * There is also a distinction between "static" and "dynamic" turbo modes:
+ *
+ * - Static: is the dumb version: devices set to this mode stick to it until
+ *     the mode is turned off.
+ * - Dynamic: is the intelligent version, the network decides itself if it
+ *     is ok to use turbo. As soon as traffic is detected on adjacent channels
+ *     (which would get used in turbo mode), or when a non-turbo station joins
+ *     the network, turbo mode won't be used until the situation changes again.
+ *     Dynamic mode is achieved by Atheros' Adaptive Radio (AR) feature which
+ *     monitors the used radio band in order to decide whether turbo mode may
+ *     be used or not.
+ *
+ * This article claims Super G sticks to bonding of channels 5 and 6 for
+ * USA:
+ *
+ * http://www.pcworld.com/article/id,113428-page,1/article.html
+ *
+ * The channel bonding seems to be driver specific though. In addition to
+ * deciding what channels will be used, these "Turbo" modes are accomplished
+ * by also enabling the following features:
+ *
+ * - Bursting: allows multiple frames to be sent at once, rather than pausing
+ *     after each frame. Bursting is a standards-compliant feature that can be
+ *     used with any Access Point.
+ * - Fast frames: increases the amount of information that can be sent per
+ *     frame, also resulting in a reduction of transmission overhead. It is a
+ *     proprietary feature that needs to be supported by the Access Point.
+ * - Compression: data frames are compressed in real time using a Lempel Ziv
+ *     algorithm. This is done transparently. Once this feature is enabled,
+ *     compression and decompression takes place inside the chipset, without
+ *     putting additional load on the host CPU.
+ *
+ */
+#define MODULATION_TURBO	0x00000080
+
+enum ath5k_driver_mode {
+	AR5K_MODE_11A		= 0,
+	AR5K_MODE_11A_TURBO	= 1,
+	AR5K_MODE_11B		= 2,
+	AR5K_MODE_11G		= 3,
+	AR5K_MODE_11G_TURBO	= 4,
+	AR5K_MODE_XR		= 5,
+};
+
+enum {
+	AR5K_MODE_BIT_11A	= (1 << AR5K_MODE_11A),
+	AR5K_MODE_BIT_11A_TURBO	= (1 << AR5K_MODE_11A_TURBO),
+	AR5K_MODE_BIT_11B	= (1 << AR5K_MODE_11B),
+	AR5K_MODE_BIT_11G	= (1 << AR5K_MODE_11G),
+	AR5K_MODE_BIT_11G_TURBO	= (1 << AR5K_MODE_11G_TURBO),
+	AR5K_MODE_BIT_XR	= (1 << AR5K_MODE_XR),
+};
+
+/****************\
+  TX DEFINITIONS
+\****************/
+
+/*
+ * TX Status descriptor
+ */
+struct ath5k_tx_status {
+	u16	ts_seqnum;
+	u16	ts_tstamp;
+	u8	ts_status;
+	u8	ts_rate[4];
+	u8	ts_retry[4];
+	u8	ts_final_idx;
+	s8	ts_rssi;
+	u8	ts_shortretry;
+	u8	ts_longretry;
+	u8	ts_virtcol;
+	u8	ts_antenna;
+} __attribute__ ((packed));
+
+#define AR5K_TXSTAT_ALTRATE	0x80
+#define AR5K_TXERR_XRETRY	0x01
+#define AR5K_TXERR_FILT		0x02
+#define AR5K_TXERR_FIFO		0x04
+
+/**
+ * enum ath5k_tx_queue - Queue types used to classify tx queues.
+ * @AR5K_TX_QUEUE_INACTIVE: q is unused -- see ath5k_hw_release_tx_queue
+ * @AR5K_TX_QUEUE_DATA: A normal data queue
+ * @AR5K_TX_QUEUE_XR_DATA: An XR-data queue
+ * @AR5K_TX_QUEUE_BEACON: The beacon queue
+ * @AR5K_TX_QUEUE_CAB: The after-beacon queue
+ * @AR5K_TX_QUEUE_UAPSD: Unscheduled Automatic Power Save Delivery queue
+ */
+enum ath5k_tx_queue {
+	AR5K_TX_QUEUE_INACTIVE = 0,
+	AR5K_TX_QUEUE_DATA,
+	AR5K_TX_QUEUE_XR_DATA,
+	AR5K_TX_QUEUE_BEACON,
+	AR5K_TX_QUEUE_CAB,
+	AR5K_TX_QUEUE_UAPSD,
+};
+
+/*
+ * Queue syb-types to classify normal data queues.
+ * These are the 4 Access Categories as defined in
+ * WME spec. 0 is the lowest priority and 4 is the
+ * highest. Normal data that hasn't been classified
+ * goes to the Best Effort AC.
+ */
+enum ath5k_tx_queue_subtype {
+	AR5K_WME_AC_BK = 0,	/*Background traffic*/
+	AR5K_WME_AC_BE, 	/*Best-effort (normal) traffic)*/
+	AR5K_WME_AC_VI, 	/*Video traffic*/
+	AR5K_WME_AC_VO, 	/*Voice traffic*/
+};
+
+/*
+ * Queue ID numbers as returned by the hw functions, each number
+ * represents a hw queue. If hw does not support hw queues
+ * (eg 5210) all data goes in one queue. These match
+ * d80211 definitions (net80211/MadWiFi don't use them).
+ */
+enum ath5k_tx_queue_id {
+	AR5K_TX_QUEUE_ID_NOQCU_DATA	= 0,
+	AR5K_TX_QUEUE_ID_NOQCU_BEACON	= 1,
+	AR5K_TX_QUEUE_ID_DATA_MIN	= 0, /*IEEE80211_TX_QUEUE_DATA0*/
+	AR5K_TX_QUEUE_ID_DATA_MAX	= 4, /*IEEE80211_TX_QUEUE_DATA4*/
+	AR5K_TX_QUEUE_ID_DATA_SVP	= 5, /*IEEE80211_TX_QUEUE_SVP - Spectralink Voice Protocol*/
+	AR5K_TX_QUEUE_ID_CAB		= 6, /*IEEE80211_TX_QUEUE_AFTER_BEACON*/
+	AR5K_TX_QUEUE_ID_BEACON		= 7, /*IEEE80211_TX_QUEUE_BEACON*/
+	AR5K_TX_QUEUE_ID_UAPSD		= 8,
+	AR5K_TX_QUEUE_ID_XR_DATA	= 9,
+};
+
+/*
+ * Flags to set hw queue's parameters...
+ */
+#define AR5K_TXQ_FLAG_TXOKINT_ENABLE		0x0001	/* Enable TXOK interrupt */
+#define AR5K_TXQ_FLAG_TXERRINT_ENABLE		0x0002	/* Enable TXERR interrupt */
+#define AR5K_TXQ_FLAG_TXEOLINT_ENABLE		0x0004	/* Enable TXEOL interrupt -not used- */
+#define AR5K_TXQ_FLAG_TXDESCINT_ENABLE		0x0008	/* Enable TXDESC interrupt -not used- */
+#define AR5K_TXQ_FLAG_TXURNINT_ENABLE		0x0010	/* Enable TXURN interrupt */
+#define AR5K_TXQ_FLAG_CBRORNINT_ENABLE		0x0020	/* Enable CBRORN interrupt */
+#define AR5K_TXQ_FLAG_CBRURNINT_ENABLE		0x0040	/* Enable CBRURN interrupt */
+#define AR5K_TXQ_FLAG_QTRIGINT_ENABLE		0x0080	/* Enable QTRIG interrupt */
+#define AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE		0x0100	/* Enable TXNOFRM interrupt */
+#define AR5K_TXQ_FLAG_BACKOFF_DISABLE		0x0200	/* Disable random post-backoff */
+#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE	0x0300	/* Enable ready time expiry policy (?)*/
+#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE	0x0800	/* Enable backoff while bursting */
+#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS		0x1000	/* Disable backoff while bursting */
+#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE	0x2000	/* Enable hw compression -not implemented-*/
+
+/*
+ * A struct to hold tx queue's parameters
+ */
+struct ath5k_txq_info {
+	enum ath5k_tx_queue tqi_type;
+	enum ath5k_tx_queue_subtype tqi_subtype;
+	u16	tqi_flags;	/* Tx queue flags (see above) */
+	u32	tqi_aifs;	/* Arbitrated Interframe Space */
+	s32	tqi_cw_min;	/* Minimum Contention Window */
+	s32	tqi_cw_max;	/* Maximum Contention Window */
+	u32	tqi_cbr_period; /* Constant bit rate period */
+	u32	tqi_cbr_overflow_limit;
+	u32	tqi_burst_time;
+	u32	tqi_ready_time; /* Not used */
+};
+
+/*
+ * Transmit packet types.
+ * used on tx control descriptor
+ * TODO: Use them inside base.c corectly
+ */
+enum ath5k_pkt_type {
+	AR5K_PKT_TYPE_NORMAL		= 0,
+	AR5K_PKT_TYPE_ATIM		= 1,
+	AR5K_PKT_TYPE_PSPOLL		= 2,
+	AR5K_PKT_TYPE_BEACON		= 3,
+	AR5K_PKT_TYPE_PROBE_RESP	= 4,
+	AR5K_PKT_TYPE_PIFS		= 5,
+};
+
+/*
+ * TX power and TPC settings
+ */
+#define AR5K_TXPOWER_OFDM(_r, _v)	(			\
+	((0 & 1) << ((_v) + 6)) |				\
+	(((ah->ah_txpower.txp_rates_power_table[(_r)]) & 0x3f) << (_v))	\
+)
+
+#define AR5K_TXPOWER_CCK(_r, _v)	(			\
+	(ah->ah_txpower.txp_rates_power_table[(_r)] & 0x3f) << (_v)	\
+)
+
+/*
+ * DMA size definitions (2^n+2)
+ */
+enum ath5k_dmasize {
+	AR5K_DMASIZE_4B	= 0,
+	AR5K_DMASIZE_8B,
+	AR5K_DMASIZE_16B,
+	AR5K_DMASIZE_32B,
+	AR5K_DMASIZE_64B,
+	AR5K_DMASIZE_128B,
+	AR5K_DMASIZE_256B,
+	AR5K_DMASIZE_512B
+};
+
+
+/****************\
+  RX DEFINITIONS
+\****************/
+
+/*
+ * RX Status descriptor
+ */
+struct ath5k_rx_status {
+	u16	rs_datalen;
+	u16	rs_tstamp;
+	u8	rs_status;
+	u8	rs_phyerr;
+	s8	rs_rssi;
+	u8	rs_keyix;
+	u8	rs_rate;
+	u8	rs_antenna;
+	u8	rs_more;
+};
+
+#define AR5K_RXERR_CRC		0x01
+#define AR5K_RXERR_PHY		0x02
+#define AR5K_RXERR_FIFO		0x04
+#define AR5K_RXERR_DECRYPT	0x08
+#define AR5K_RXERR_MIC		0x10
+#define AR5K_RXKEYIX_INVALID	((u8) - 1)
+#define AR5K_TXKEYIX_INVALID	((u32) - 1)
+
+
+/*
+ * TSF to TU conversion:
+ *
+ * TSF is a 64bit value in usec (microseconds).
+ * TU is a 32bit value and defined by IEEE802.11 (page 6) as "A measurement of
+ * time equal to 1024 usec", so it's roughly milliseconds (usec / 1024).
+ */
+#define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10)
+
+
+/*******************************\
+  GAIN OPTIMIZATION DEFINITIONS
+\*******************************/
+
+enum ath5k_rfgain {
+	AR5K_RFGAIN_INACTIVE = 0,
+	AR5K_RFGAIN_ACTIVE,
+	AR5K_RFGAIN_READ_REQUESTED,
+	AR5K_RFGAIN_NEED_CHANGE,
+};
+
+struct ath5k_gain {
+	u8			g_step_idx;
+	u8			g_current;
+	u8			g_target;
+	u8			g_low;
+	u8			g_high;
+	u8			g_f_corr;
+	u8			g_state;
+};
+
+/********************\
+  COMMON DEFINITIONS
+\********************/
+
+#define AR5K_SLOT_TIME_9	396
+#define AR5K_SLOT_TIME_20	880
+#define AR5K_SLOT_TIME_MAX	0xffff
+
+/* channel_flags */
+#define	CHANNEL_CW_INT	0x0008	/* Contention Window interference detected */
+#define	CHANNEL_TURBO	0x0010	/* Turbo Channel */
+#define	CHANNEL_CCK	0x0020	/* CCK channel */
+#define	CHANNEL_OFDM	0x0040	/* OFDM channel */
+#define	CHANNEL_2GHZ	0x0080	/* 2GHz channel. */
+#define	CHANNEL_5GHZ	0x0100	/* 5GHz channel */
+#define	CHANNEL_PASSIVE	0x0200	/* Only passive scan allowed */
+#define	CHANNEL_DYN	0x0400	/* Dynamic CCK-OFDM channel (for g operation) */
+#define	CHANNEL_XR	0x0800	/* XR channel */
+
+#define	CHANNEL_A	(CHANNEL_5GHZ|CHANNEL_OFDM)
+#define	CHANNEL_B	(CHANNEL_2GHZ|CHANNEL_CCK)
+#define	CHANNEL_G	(CHANNEL_2GHZ|CHANNEL_OFDM)
+#define	CHANNEL_T	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+#define	CHANNEL_TG	(CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+#define	CHANNEL_108A	CHANNEL_T
+#define	CHANNEL_108G	CHANNEL_TG
+#define	CHANNEL_X	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR)
+
+#define	CHANNEL_ALL 	(CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_2GHZ|CHANNEL_5GHZ| \
+		CHANNEL_TURBO)
+
+#define	CHANNEL_ALL_NOTURBO 	(CHANNEL_ALL & ~CHANNEL_TURBO)
+#define CHANNEL_MODES		CHANNEL_ALL
+
+/*
+ * Used internaly for reset_tx_queue).
+ * Also see struct struct net80211_channel.
+ */
+#define IS_CHAN_XR(_c)	((_c->hw_value & CHANNEL_XR) != 0)
+#define IS_CHAN_B(_c)	((_c->hw_value & CHANNEL_B) != 0)
+
+/*
+ * The following structure is used to map 2GHz channels to
+ * 5GHz Atheros channels.
+ * TODO: Clean up
+ */
+struct ath5k_athchan_2ghz {
+	u32	a2_flags;
+	u16	a2_athchan;
+};
+
+
+/******************\
+  RATE DEFINITIONS
+\******************/
+
+/**
+ * Seems the ar5xxx harware supports up to 32 rates, indexed by 1-32.
+ *
+ * The rate code is used to get the RX rate or set the TX rate on the
+ * hardware descriptors. It is also used for internal modulation control
+ * and settings.
+ *
+ * This is the hardware rate map we are aware of:
+ *
+ * rate_code   0x01    0x02    0x03    0x04    0x05    0x06    0x07    0x08
+ * rate_kbps   3000    1000    ?       ?       ?       2000    500     48000
+ *
+ * rate_code   0x09    0x0A    0x0B    0x0C    0x0D    0x0E    0x0F    0x10
+ * rate_kbps   24000   12000   6000    54000   36000   18000   9000    ?
+ *
+ * rate_code   17      18      19      20      21      22      23      24
+ * rate_kbps   ?       ?       ?       ?       ?       ?       ?       11000
+ *
+ * rate_code   25      26      27      28      29      30      31      32
+ * rate_kbps   5500    2000    1000    11000S  5500S   2000S   ?       ?
+ *
+ * "S" indicates CCK rates with short preamble.
+ *
+ * AR5211 has different rate codes for CCK (802.11B) rates. It only uses the
+ * lowest 4 bits, so they are the same as below with a 0xF mask.
+ * (0xB, 0xA, 0x9 and 0x8 for 1M, 2M, 5.5M and 11M).
+ * We handle this in ath5k_setup_bands().
+ */
+#define AR5K_MAX_RATES 32
+
+/* B */
+#define ATH5K_RATE_CODE_1M	0x1B
+#define ATH5K_RATE_CODE_2M	0x1A
+#define ATH5K_RATE_CODE_5_5M	0x19
+#define ATH5K_RATE_CODE_11M	0x18
+/* A and G */
+#define ATH5K_RATE_CODE_6M	0x0B
+#define ATH5K_RATE_CODE_9M	0x0F
+#define ATH5K_RATE_CODE_12M	0x0A
+#define ATH5K_RATE_CODE_18M	0x0E
+#define ATH5K_RATE_CODE_24M	0x09
+#define ATH5K_RATE_CODE_36M	0x0D
+#define ATH5K_RATE_CODE_48M	0x08
+#define ATH5K_RATE_CODE_54M	0x0C
+/* XR */
+#define ATH5K_RATE_CODE_XR_500K	0x07
+#define ATH5K_RATE_CODE_XR_1M	0x02
+#define ATH5K_RATE_CODE_XR_2M	0x06
+#define ATH5K_RATE_CODE_XR_3M	0x01
+
+/* adding this flag to rate_code enables short preamble */
+#define AR5K_SET_SHORT_PREAMBLE 0x04
+
+/*
+ * Crypto definitions
+ */
+
+#define AR5K_KEYCACHE_SIZE	8
+
+/***********************\
+ HW RELATED DEFINITIONS
+\***********************/
+
+/*
+ * Misc definitions
+ */
+#define	AR5K_RSSI_EP_MULTIPLIER	(1<<7)
+
+#define AR5K_ASSERT_ENTRY(_e, _s) do {		\
+	if (_e >= _s)				\
+		return 0;			\
+} while (0)
+
+/*
+ * Hardware interrupt abstraction
+ */
+
+/**
+ * enum ath5k_int - Hardware interrupt masks helpers
+ *
+ * @AR5K_INT_RX: mask to identify received frame interrupts, of type
+ * 	AR5K_ISR_RXOK or AR5K_ISR_RXERR
+ * @AR5K_INT_RXDESC: Request RX descriptor/Read RX descriptor (?)
+ * @AR5K_INT_RXNOFRM: No frame received (?)
+ * @AR5K_INT_RXEOL: received End Of List for VEOL (Virtual End Of List). The
+ * 	Queue Control Unit (QCU) signals an EOL interrupt only if a descriptor's
+ * 	LinkPtr is NULL. For more details, refer to:
+ * 	http://www.freepatentsonline.com/20030225739.html
+ * @AR5K_INT_RXORN: Indicates we got RX overrun (eg. no more descriptors).
+ * 	Note that Rx overrun is not always fatal, on some chips we can continue
+ * 	operation without reseting the card, that's why int_fatal is not
+ * 	common for all chips.
+ * @AR5K_INT_TX: mask to identify received frame interrupts, of type
+ * 	AR5K_ISR_TXOK or AR5K_ISR_TXERR
+ * @AR5K_INT_TXDESC: Request TX descriptor/Read TX status descriptor (?)
+ * @AR5K_INT_TXURN: received when we should increase the TX trigger threshold
+ * 	We currently do increments on interrupt by
+ * 	(AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2
+ * @AR5K_INT_MIB: Indicates the Management Information Base counters should be
+ * 	checked. We should do this with ath5k_hw_update_mib_counters() but
+ * 	it seems we should also then do some noise immunity work.
+ * @AR5K_INT_RXPHY: RX PHY Error
+ * @AR5K_INT_RXKCM: RX Key cache miss
+ * @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a
+ * 	beacon that must be handled in software. The alternative is if you
+ * 	have VEOL support, in that case you let the hardware deal with things.
+ * @AR5K_INT_BMISS: If in STA mode this indicates we have stopped seeing
+ * 	beacons from the AP have associated with, we should probably try to
+ * 	reassociate. When in IBSS mode this might mean we have not received
+ * 	any beacons from any local stations. Note that every station in an
+ * 	IBSS schedules to send beacons at the Target Beacon Transmission Time
+ * 	(TBTT) with a random backoff.
+ * @AR5K_INT_BNR: Beacon Not Ready interrupt - ??
+ * @AR5K_INT_GPIO: GPIO interrupt is used for RF Kill, disabled for now
+ * 	until properly handled
+ * @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by DMA
+ * 	errors. These types of errors we can enable seem to be of type
+ * 	AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR.
+ * @AR5K_INT_GLOBAL: Used to clear and set the IER
+ * @AR5K_INT_NOCARD: signals the card has been removed
+ * @AR5K_INT_COMMON: common interrupts shared amogst MACs with the same
+ * 	bit value
+ *
+ * These are mapped to take advantage of some common bits
+ * between the MACs, to be able to set intr properties
+ * easier. Some of them are not used yet inside hw.c. Most map
+ * to the respective hw interrupt value as they are common amogst different
+ * MACs.
+ */
+enum ath5k_int {
+	AR5K_INT_RXOK	= 0x00000001,
+	AR5K_INT_RXDESC	= 0x00000002,
+	AR5K_INT_RXERR	= 0x00000004,
+	AR5K_INT_RXNOFRM = 0x00000008,
+	AR5K_INT_RXEOL	= 0x00000010,
+	AR5K_INT_RXORN	= 0x00000020,
+	AR5K_INT_TXOK	= 0x00000040,
+	AR5K_INT_TXDESC	= 0x00000080,
+	AR5K_INT_TXERR	= 0x00000100,
+	AR5K_INT_TXNOFRM = 0x00000200,
+	AR5K_INT_TXEOL	= 0x00000400,
+	AR5K_INT_TXURN	= 0x00000800,
+	AR5K_INT_MIB	= 0x00001000,
+	AR5K_INT_SWI	= 0x00002000,
+	AR5K_INT_RXPHY	= 0x00004000,
+	AR5K_INT_RXKCM	= 0x00008000,
+	AR5K_INT_SWBA	= 0x00010000,
+	AR5K_INT_BRSSI	= 0x00020000,
+	AR5K_INT_BMISS	= 0x00040000,
+	AR5K_INT_FATAL	= 0x00080000, /* Non common */
+	AR5K_INT_BNR	= 0x00100000, /* Non common */
+	AR5K_INT_TIM	= 0x00200000, /* Non common */
+	AR5K_INT_DTIM	= 0x00400000, /* Non common */
+	AR5K_INT_DTIM_SYNC =	0x00800000, /* Non common */
+	AR5K_INT_GPIO	=	0x01000000,
+	AR5K_INT_BCN_TIMEOUT =	0x02000000, /* Non common */
+	AR5K_INT_CAB_TIMEOUT =	0x04000000, /* Non common */
+	AR5K_INT_RX_DOPPLER =	0x08000000, /* Non common */
+	AR5K_INT_QCBRORN =	0x10000000, /* Non common */
+	AR5K_INT_QCBRURN =	0x20000000, /* Non common */
+	AR5K_INT_QTRIG	=	0x40000000, /* Non common */
+	AR5K_INT_GLOBAL =	0x80000000,
+
+	AR5K_INT_COMMON  = AR5K_INT_RXOK
+		| AR5K_INT_RXDESC
+		| AR5K_INT_RXERR
+		| AR5K_INT_RXNOFRM
+		| AR5K_INT_RXEOL
+		| AR5K_INT_RXORN
+		| AR5K_INT_TXOK
+		| AR5K_INT_TXDESC
+		| AR5K_INT_TXERR
+		| AR5K_INT_TXNOFRM
+		| AR5K_INT_TXEOL
+		| AR5K_INT_TXURN
+		| AR5K_INT_MIB
+		| AR5K_INT_SWI
+		| AR5K_INT_RXPHY
+		| AR5K_INT_RXKCM
+		| AR5K_INT_SWBA
+		| AR5K_INT_BRSSI
+		| AR5K_INT_BMISS
+		| AR5K_INT_GPIO
+		| AR5K_INT_GLOBAL,
+
+	AR5K_INT_NOCARD	= 0xffffffff
+};
+
+/*
+ * Power management
+ */
+enum ath5k_power_mode {
+	AR5K_PM_UNDEFINED = 0,
+	AR5K_PM_AUTO,
+	AR5K_PM_AWAKE,
+	AR5K_PM_FULL_SLEEP,
+	AR5K_PM_NETWORK_SLEEP,
+};
+
+/* GPIO-controlled software LED */
+#define AR5K_SOFTLED_PIN	0
+#define AR5K_SOFTLED_ON		0
+#define AR5K_SOFTLED_OFF	1
+
+/*
+ * Chipset capabilities -see ath5k_hw_get_capability-
+ * get_capability function is not yet fully implemented
+ * in ath5k so most of these don't work yet...
+ * TODO: Implement these & merge with _TUNE_ stuff above
+ */
+enum ath5k_capability_type {
+	AR5K_CAP_REG_DMN		= 0,	/* Used to get current reg. domain id */
+	AR5K_CAP_TKIP_MIC		= 2,	/* Can handle TKIP MIC in hardware */
+	AR5K_CAP_TKIP_SPLIT		= 3,	/* TKIP uses split keys */
+	AR5K_CAP_PHYCOUNTERS		= 4,	/* PHY error counters */
+	AR5K_CAP_DIVERSITY		= 5,	/* Supports fast diversity */
+	AR5K_CAP_NUM_TXQUEUES		= 6,	/* Used to get max number of hw txqueues */
+	AR5K_CAP_VEOL			= 7,	/* Supports virtual EOL */
+	AR5K_CAP_COMPRESSION		= 8,	/* Supports compression */
+	AR5K_CAP_BURST			= 9,	/* Supports packet bursting */
+	AR5K_CAP_FASTFRAME		= 10,	/* Supports fast frames */
+	AR5K_CAP_TXPOW			= 11,	/* Used to get global tx power limit */
+	AR5K_CAP_TPC			= 12,	/* Can do per-packet tx power control (needed for 802.11a) */
+	AR5K_CAP_BSSIDMASK		= 13,	/* Supports bssid mask */
+	AR5K_CAP_MCAST_KEYSRCH		= 14,	/* Supports multicast key search */
+	AR5K_CAP_TSF_ADJUST		= 15,	/* Supports beacon tsf adjust */
+	AR5K_CAP_XR			= 16,	/* Supports XR mode */
+	AR5K_CAP_WME_TKIPMIC 		= 17,	/* Supports TKIP MIC when using WMM */
+	AR5K_CAP_CHAN_HALFRATE 		= 18,	/* Supports half rate channels */
+	AR5K_CAP_CHAN_QUARTERRATE 	= 19,	/* Supports quarter rate channels */
+	AR5K_CAP_RFSILENT		= 20,	/* Supports RFsilent */
+};
+
+
+/* XXX: we *may* move cap_range stuff to struct wiphy */
+struct ath5k_capabilities {
+	/*
+	 * Supported PHY modes
+	 * (ie. CHANNEL_A, CHANNEL_B, ...)
+	 */
+	u16 cap_mode;
+
+	/*
+	 * Frequency range (without regulation restrictions)
+	 */
+	struct {
+		u16	range_2ghz_min;
+		u16	range_2ghz_max;
+		u16	range_5ghz_min;
+		u16	range_5ghz_max;
+	} cap_range;
+
+	/*
+	 * Values stored in the EEPROM (some of them...)
+	 */
+	struct ath5k_eeprom_info	cap_eeprom;
+
+	/*
+	 * Queue information
+	 */
+	struct {
+		u8	q_tx_num;
+	} cap_queues;
+};
+
+
+/***************************************\
+  HARDWARE ABSTRACTION LAYER STRUCTURE
+\***************************************/
+
+/*
+ * Misc defines
+ */
+
+#define AR5K_MAX_GPIO		10
+#define AR5K_MAX_RF_BANKS	8
+
+/* TODO: Clean up and merge with ath5k_softc */
+struct ath5k_hw {
+	struct ath5k_softc	*ah_sc;
+	void			*ah_iobase;
+
+	enum ath5k_int		ah_imr;
+	int			ah_ier;
+
+	struct net80211_channel	*ah_current_channel;
+	int			ah_turbo;
+	int			ah_calibration;
+	int			ah_running;
+	int			ah_single_chip;
+	int			ah_combined_mic;
+
+	u32			ah_mac_srev;
+	u16			ah_mac_version;
+	u16			ah_mac_revision;
+	u16			ah_phy_revision;
+	u16			ah_radio_5ghz_revision;
+	u16			ah_radio_2ghz_revision;
+
+	enum ath5k_version	ah_version;
+	enum ath5k_radio	ah_radio;
+	u32			ah_phy;
+
+	int			ah_5ghz;
+	int			ah_2ghz;
+
+#define ah_regdomain		ah_capabilities.cap_regdomain.reg_current
+#define ah_regdomain_hw		ah_capabilities.cap_regdomain.reg_hw
+#define ah_modes		ah_capabilities.cap_mode
+#define ah_ee_version		ah_capabilities.cap_eeprom.ee_version
+
+	u32			ah_atim_window;
+	u32			ah_aifs;
+	u32			ah_cw_min;
+	u32			ah_cw_max;
+	int			ah_software_retry;
+	u32			ah_limit_tx_retries;
+
+	u32			ah_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
+	int			ah_ant_diversity;
+
+	u8			ah_sta_id[ETH_ALEN];
+
+	/* Current BSSID we are trying to assoc to / create.
+	 * This is passed by mac80211 on config_interface() and cached here for
+	 * use in resets */
+	u8			ah_bssid[ETH_ALEN];
+	u8			ah_bssid_mask[ETH_ALEN];
+
+	u32			ah_gpio[AR5K_MAX_GPIO];
+	int			ah_gpio_npins;
+
+	struct ath5k_capabilities ah_capabilities;
+
+	struct ath5k_txq_info	ah_txq;
+	u32			ah_txq_status;
+	u32			ah_txq_imr_txok;
+	u32			ah_txq_imr_txerr;
+	u32			ah_txq_imr_txurn;
+	u32			ah_txq_imr_txdesc;
+	u32			ah_txq_imr_txeol;
+	u32			ah_txq_imr_cbrorn;
+	u32			ah_txq_imr_cbrurn;
+	u32			ah_txq_imr_qtrig;
+	u32			ah_txq_imr_nofrm;
+	u32			ah_txq_isr;
+	u32			*ah_rf_banks;
+	size_t			ah_rf_banks_size;
+	size_t			ah_rf_regs_count;
+	struct ath5k_gain	ah_gain;
+	u8			ah_offset[AR5K_MAX_RF_BANKS];
+
+
+	struct {
+		/* Temporary tables used for interpolation */
+		u8		tmpL[AR5K_EEPROM_N_PD_GAINS]
+					[AR5K_EEPROM_POWER_TABLE_SIZE];
+		u8		tmpR[AR5K_EEPROM_N_PD_GAINS]
+					[AR5K_EEPROM_POWER_TABLE_SIZE];
+		u8		txp_pd_table[AR5K_EEPROM_POWER_TABLE_SIZE * 2];
+		u16		txp_rates_power_table[AR5K_MAX_RATES];
+		u8		txp_min_idx;
+		int		txp_tpc;
+		/* Values in 0.25dB units */
+		s16		txp_min_pwr;
+		s16		txp_max_pwr;
+		s16		txp_offset;
+		s16		txp_ofdm;
+		/* Values in dB units */
+		s16		txp_cck_ofdm_pwr_delta;
+		s16		txp_cck_ofdm_gainf_delta;
+	} ah_txpower;
+
+	/* noise floor from last periodic calibration */
+	s32			ah_noise_floor;
+
+	/*
+	 * Function pointers
+	 */
+	int (*ah_setup_rx_desc)(struct ath5k_hw *ah, struct ath5k_desc *desc,
+				u32 size, unsigned int flags);
+	int (*ah_setup_tx_desc)(struct ath5k_hw *, struct ath5k_desc *,
+		unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
+		unsigned int, unsigned int, unsigned int, unsigned int,
+		unsigned int, unsigned int, unsigned int);
+	int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *,
+		struct ath5k_tx_status *);
+	int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *,
+		struct ath5k_rx_status *);
+};
+
+/*
+ * Prototypes
+ */
+
+extern int ath5k_bitrate_to_hw_rix(int bitrate);
+
+/* Attach/Detach Functions */
+extern int ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version, struct ath5k_hw **ah);
+extern void ath5k_hw_detach(struct ath5k_hw *ah);
+
+/* LED functions */
+extern int ath5k_init_leds(struct ath5k_softc *sc);
+extern void ath5k_led_enable(struct ath5k_softc *sc);
+extern void ath5k_led_off(struct ath5k_softc *sc);
+extern void ath5k_unregister_leds(struct ath5k_softc *sc);
+
+/* Reset Functions */
+extern int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, int initial);
+extern int ath5k_hw_reset(struct ath5k_hw *ah, struct net80211_channel *channel, int change_channel);
+/* Power management functions */
+extern int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, int set_chip, u16 sleep_duration);
+
+/* DMA Related Functions */
+extern void ath5k_hw_start_rx_dma(struct ath5k_hw *ah);
+extern int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah);
+extern u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah);
+extern void ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr);
+extern int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue);
+extern int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue);
+extern u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue);
+extern int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue,
+				u32 phys_addr);
+extern int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, int increase);
+/* Interrupt handling */
+extern int ath5k_hw_is_intr_pending(struct ath5k_hw *ah);
+extern int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask);
+extern enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask);
+
+/* EEPROM access functions */
+extern int ath5k_eeprom_init(struct ath5k_hw *ah);
+extern void ath5k_eeprom_detach(struct ath5k_hw *ah);
+extern int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac);
+extern int ath5k_eeprom_is_hb63(struct ath5k_hw *ah);
+
+/* Protocol Control Unit Functions */
+extern int ath5k_hw_set_opmode(struct ath5k_hw *ah);
+/* BSSID Functions */
+extern void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac);
+extern int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac);
+extern void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id);
+extern int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask);
+/* Receive start/stop functions */
+extern void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah);
+extern void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah);
+/* RX Filter functions */
+extern void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1);
+extern u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah);
+extern void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter);
+/* ACK bit rate */
+void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, int high);
+/* ACK/CTS Timeouts */
+extern int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout);
+extern unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah);
+extern int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout);
+extern unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah);
+/* Key table (WEP) functions */
+extern int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry);
+
+/* Queue Control Unit, DFS Control Unit Functions */
+extern int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, const struct ath5k_txq_info *queue_info);
+extern int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah,
+				enum ath5k_tx_queue queue_type,
+				struct ath5k_txq_info *queue_info);
+extern u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah);
+extern void ath5k_hw_release_tx_queue(struct ath5k_hw *ah);
+extern int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah);
+extern int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time);
+
+/* Hardware Descriptor Functions */
+extern int ath5k_hw_init_desc_functions(struct ath5k_hw *ah);
+
+/* GPIO Functions */
+extern int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio);
+extern int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio);
+extern u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio);
+extern int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val);
+extern void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, u32 interrupt_level);
+
+/* rfkill Functions */
+extern void ath5k_rfkill_hw_start(struct ath5k_hw *ah);
+extern void ath5k_rfkill_hw_stop(struct ath5k_hw *ah);
+
+/* Misc functions */
+int ath5k_hw_set_capabilities(struct ath5k_hw *ah);
+extern int ath5k_hw_get_capability(struct ath5k_hw *ah, enum ath5k_capability_type cap_type, u32 capability, u32 *result);
+extern int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid, u16 assoc_id);
+extern int ath5k_hw_disable_pspoll(struct ath5k_hw *ah);
+
+/* Initial register settings functions */
+extern int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, int change_channel);
+
+/* Initialize RF */
+extern int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
+				struct net80211_channel *channel,
+				unsigned int mode);
+extern int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq);
+extern enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah);
+extern int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah);
+/* PHY/RF channel functions */
+extern int ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags);
+extern int ath5k_hw_channel(struct ath5k_hw *ah, struct net80211_channel *channel);
+/* PHY calibration */
+extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct net80211_channel *channel);
+extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq);
+/* Misc PHY functions */
+extern u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan);
+extern void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant);
+extern unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah);
+extern int ath5k_hw_phy_disable(struct ath5k_hw *ah);
+/* TX power setup */
+extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct net80211_channel *channel, u8 ee_mode, u8 txpower);
+extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 ee_mode, u8 txpower);
+
+/*
+ * Functions used internaly
+ */
+
+/*
+ * Translate usec to hw clock units
+ * TODO: Half/quarter rate
+ */
+static inline unsigned int ath5k_hw_htoclock(unsigned int usec, int turbo)
+{
+	return turbo ? (usec * 80) : (usec * 40);
+}
+
+/*
+ * Translate hw clock units to usec
+ * TODO: Half/quarter rate
+ */
+static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, int turbo)
+{
+	return turbo ? (clock / 80) : (clock / 40);
+}
+
+/*
+ * Read from a register
+ */
+static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg)
+{
+	return readl(ah->ah_iobase + reg);
+}
+
+/*
+ * Write to a register
+ */
+static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg)
+{
+	writel(val, ah->ah_iobase + reg);
+}
+
+#if defined(_ATH5K_RESET) || defined(_ATH5K_PHY)
+/*
+ * Check if a register write has been completed
+ */
+static int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag,
+		u32 val, int is_set)
+{
+	int i;
+	u32 data;
+
+	for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) {
+		data = ath5k_hw_reg_read(ah, reg);
+		if (is_set && (data & flag))
+			break;
+		else if ((data & flag) == val)
+			break;
+		udelay(15);
+	}
+
+	return (i <= 0) ? -EAGAIN : 0;
+}
+
+/*
+ * Convert channel frequency to channel number
+ */
+static inline int ath5k_freq_to_channel(int freq)
+{
+	if (freq == 2484)
+		return 14;
+
+	if (freq < 2484)
+		return (freq - 2407) / 5;
+
+	return freq/5 - 1000;
+}
+
+#endif
+
+static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits)
+{
+	u32 retval = 0, bit, i;
+
+	for (i = 0; i < bits; i++) {
+		bit = (val >> i) & 1;
+		retval = (retval << 1) | bit;
+	}
+
+	return retval;
+}
+
+#endif
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_attach.c b/gpxe/src/drivers/net/ath5k/ath5k_attach.c
new file mode 100644
index 0000000..36dc243
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_attach.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>
+ * Original from Linux kernel 2.6.30.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+/*************************************\
+* Attach/Detach Functions and helpers *
+\*************************************/
+
+#include <gpxe/pci.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/**
+ * ath5k_hw_post - Power On Self Test helper function
+ *
+ * @ah: The &struct ath5k_hw
+ */
+static int ath5k_hw_post(struct ath5k_hw *ah)
+{
+
+	static const u32 static_pattern[4] = {
+		0x55555555,	0xaaaaaaaa,
+		0x66666666,	0x99999999
+	};
+	static const u16 regs[2] = { AR5K_STA_ID0, AR5K_PHY(8) };
+	int i, c;
+	u16 cur_reg;
+	u32 var_pattern;
+	u32 init_val;
+	u32 cur_val;
+
+	for (c = 0; c < 2; c++) {
+
+		cur_reg = regs[c];
+
+		/* Save previous value */
+		init_val = ath5k_hw_reg_read(ah, cur_reg);
+
+		for (i = 0; i < 256; i++) {
+			var_pattern = i << 16 | i;
+			ath5k_hw_reg_write(ah, var_pattern, cur_reg);
+			cur_val = ath5k_hw_reg_read(ah, cur_reg);
+
+			if (cur_val != var_pattern) {
+				DBG("ath5k: POST failed!\n");
+				return -EAGAIN;
+			}
+
+			/* Found on ndiswrapper dumps */
+			var_pattern = 0x0039080f;
+			ath5k_hw_reg_write(ah, var_pattern, cur_reg);
+		}
+
+		for (i = 0; i < 4; i++) {
+			var_pattern = static_pattern[i];
+			ath5k_hw_reg_write(ah, var_pattern, cur_reg);
+			cur_val = ath5k_hw_reg_read(ah, cur_reg);
+
+			if (cur_val != var_pattern) {
+				DBG("ath5k: POST failed!\n");
+				return -EAGAIN;
+			}
+
+			/* Found on ndiswrapper dumps */
+			var_pattern = 0x003b080f;
+			ath5k_hw_reg_write(ah, var_pattern, cur_reg);
+		}
+
+		/* Restore previous value */
+		ath5k_hw_reg_write(ah, init_val, cur_reg);
+
+	}
+
+	return 0;
+
+}
+
+/**
+ * ath5k_hw_attach - Check if hw is supported and init the needed structs
+ *
+ * @sc: The &struct ath5k_softc we got from the driver's attach function
+ * @mac_version: The mac version id (check out ath5k.h) based on pci id
+ * @hw: Returned newly allocated hardware structure, on success
+ *
+ * Check if the device is supported, perform a POST and initialize the needed
+ * structs. Returns -ENOMEM if we don't have memory for the needed structs,
+ * -ENODEV if the device is not supported or prints an error msg if something
+ * else went wrong.
+ */
+int ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version,
+		    struct ath5k_hw **hw)
+{
+	struct ath5k_hw *ah;
+	struct pci_device *pdev = sc->pdev;
+	int ret;
+	u32 srev;
+
+	ah = zalloc(sizeof(struct ath5k_hw));
+	if (ah == NULL) {
+		ret = -ENOMEM;
+		DBG("ath5k: out of memory\n");
+		goto err;
+	}
+
+	ah->ah_sc = sc;
+	ah->ah_iobase = sc->iobase;
+
+	/*
+	 * HW information
+	 */
+	ah->ah_turbo = 0;
+	ah->ah_txpower.txp_tpc = 0;
+	ah->ah_imr = 0;
+	ah->ah_atim_window = 0;
+	ah->ah_aifs = AR5K_TUNE_AIFS;
+	ah->ah_cw_min = AR5K_TUNE_CWMIN;
+	ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY;
+	ah->ah_software_retry = 0;
+	ah->ah_ant_diversity = AR5K_TUNE_ANT_DIVERSITY;
+
+	/*
+	 * Set the mac version based on the pci id
+	 */
+	ah->ah_version = mac_version;
+
+	/*Fill the ath5k_hw struct with the needed functions*/
+	ret = ath5k_hw_init_desc_functions(ah);
+	if (ret)
+		goto err_free;
+
+	/* Bring device out of sleep and reset it's units */
+	ret = ath5k_hw_nic_wakeup(ah, CHANNEL_B, 1);
+	if (ret)
+		goto err_free;
+
+	/* Get MAC, PHY and RADIO revisions */
+	srev = ath5k_hw_reg_read(ah, AR5K_SREV);
+	ah->ah_mac_srev = srev;
+	ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
+	ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV);
+	ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID);
+	ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah, CHANNEL_5GHZ);
+	ah->ah_phy = AR5K_PHY(0);
+
+	/* Try to identify radio chip based on it's srev */
+	switch (ah->ah_radio_5ghz_revision & 0xf0) {
+	case AR5K_SREV_RAD_5111:
+		ah->ah_radio = AR5K_RF5111;
+		ah->ah_single_chip = 0;
+		ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
+							CHANNEL_2GHZ);
+		break;
+	case AR5K_SREV_RAD_5112:
+	case AR5K_SREV_RAD_2112:
+		ah->ah_radio = AR5K_RF5112;
+		ah->ah_single_chip = 0;
+		ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
+							CHANNEL_2GHZ);
+		break;
+	case AR5K_SREV_RAD_2413:
+		ah->ah_radio = AR5K_RF2413;
+		ah->ah_single_chip = 1;
+		break;
+	case AR5K_SREV_RAD_5413:
+		ah->ah_radio = AR5K_RF5413;
+		ah->ah_single_chip = 1;
+		break;
+	case AR5K_SREV_RAD_2316:
+		ah->ah_radio = AR5K_RF2316;
+		ah->ah_single_chip = 1;
+		break;
+	case AR5K_SREV_RAD_2317:
+		ah->ah_radio = AR5K_RF2317;
+		ah->ah_single_chip = 1;
+		break;
+	case AR5K_SREV_RAD_5424:
+		if (ah->ah_mac_version == AR5K_SREV_AR2425 ||
+		    ah->ah_mac_version == AR5K_SREV_AR2417) {
+			ah->ah_radio = AR5K_RF2425;
+		} else {
+			ah->ah_radio = AR5K_RF5413;
+		}
+		ah->ah_single_chip = 1;
+		break;
+	default:
+		/* Identify radio based on mac/phy srev */
+		if (ah->ah_version == AR5K_AR5210) {
+			ah->ah_radio = AR5K_RF5110;
+			ah->ah_single_chip = 0;
+		} else if (ah->ah_version == AR5K_AR5211) {
+			ah->ah_radio = AR5K_RF5111;
+			ah->ah_single_chip = 0;
+			ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
+								CHANNEL_2GHZ);
+		} else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) ||
+			   ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) ||
+			   ah->ah_phy_revision == AR5K_SREV_PHY_2425) {
+			ah->ah_radio = AR5K_RF2425;
+			ah->ah_single_chip = 1;
+			ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2425;
+		} else if (srev == AR5K_SREV_AR5213A &&
+			   ah->ah_phy_revision == AR5K_SREV_PHY_5212B) {
+			ah->ah_radio = AR5K_RF5112;
+			ah->ah_single_chip = 0;
+			ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5112B;
+		} else if (ah->ah_mac_version == (AR5K_SREV_AR2415 >> 4)) {
+			ah->ah_radio = AR5K_RF2316;
+			ah->ah_single_chip = 1;
+			ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2316;
+		} else if (ah->ah_mac_version == (AR5K_SREV_AR5414 >> 4) ||
+			   ah->ah_phy_revision == AR5K_SREV_PHY_5413) {
+			ah->ah_radio = AR5K_RF5413;
+			ah->ah_single_chip = 1;
+			ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5413;
+		} else if (ah->ah_mac_version == (AR5K_SREV_AR2414 >> 4) ||
+			   ah->ah_phy_revision == AR5K_SREV_PHY_2413) {
+			ah->ah_radio = AR5K_RF2413;
+			ah->ah_single_chip = 1;
+			ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2413;
+		} else {
+			DBG("ath5k: Couldn't identify radio revision.\n");
+			ret = -ENOTSUP;
+			goto err_free;
+		}
+	}
+
+	/* Return on unsuported chips (unsupported eeprom etc) */
+	if ((srev >= AR5K_SREV_AR5416) &&
+	    (srev < AR5K_SREV_AR2425)) {
+		DBG("ath5k: Device not yet supported.\n");
+		ret = -ENOTSUP;
+		goto err_free;
+	}
+
+	/*
+	 * Write PCI-E power save settings
+	 */
+	if ((ah->ah_version == AR5K_AR5212) &&
+	    pci_find_capability(pdev, PCI_CAP_ID_EXP)) {
+		ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES);
+		ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES);
+		/* Shut off RX when elecidle is asserted */
+		ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES);
+		ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES);
+		/* TODO: EEPROM work */
+		ath5k_hw_reg_write(ah, 0xe5980579, AR5K_PCIE_SERDES);
+		/* Shut off PLL and CLKREQ active in L1 */
+		ath5k_hw_reg_write(ah, 0x001defff, AR5K_PCIE_SERDES);
+		/* Preserce other settings */
+		ath5k_hw_reg_write(ah, 0x1aaabe40, AR5K_PCIE_SERDES);
+		ath5k_hw_reg_write(ah, 0xbe105554, AR5K_PCIE_SERDES);
+		ath5k_hw_reg_write(ah, 0x000e3007, AR5K_PCIE_SERDES);
+		/* Reset SERDES to load new settings */
+		ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET);
+		mdelay(1);
+	}
+
+	/*
+	 * POST
+	 */
+	ret = ath5k_hw_post(ah);
+	if (ret)
+		goto err_free;
+
+	/* Enable pci core retry fix on Hainan (5213A) and later chips */
+	if (srev >= AR5K_SREV_AR5213A)
+		ath5k_hw_reg_write(ah, AR5K_PCICFG_RETRY_FIX, AR5K_PCICFG);
+
+	/*
+	 * Get card capabilities, calibration values etc
+	 * TODO: EEPROM work
+	 */
+	ret = ath5k_eeprom_init(ah);
+	if (ret) {
+		DBG("ath5k: unable to init EEPROM\n");
+		goto err_free;
+	}
+
+	/* Get misc capabilities */
+	ret = ath5k_hw_set_capabilities(ah);
+	if (ret) {
+		DBG("ath5k: unable to get device capabilities: 0x%04x\n",
+		    sc->pdev->device);
+		goto err_free;
+	}
+
+	if (srev >= AR5K_SREV_AR2414) {
+		ah->ah_combined_mic = 1;
+		AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE,
+				     AR5K_MISC_MODE_COMBINED_MIC);
+	}
+
+	/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
+	memset(ah->ah_bssid, 0xff, ETH_ALEN);
+	ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
+	ath5k_hw_set_opmode(ah);
+
+	ath5k_hw_rfgain_opt_init(ah);
+
+	*hw = ah;
+	return 0;
+err_free:
+	free(ah);
+err:
+	return ret;
+}
+
+/**
+ * ath5k_hw_detach - Free the ath5k_hw struct
+ *
+ * @ah: The &struct ath5k_hw
+ */
+void ath5k_hw_detach(struct ath5k_hw *ah)
+{
+	free(ah->ah_rf_banks);
+	ath5k_eeprom_detach(ah);
+	free(ah);
+}
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_caps.c b/gpxe/src/drivers/net/ath5k/ath5k_caps.c
new file mode 100644
index 0000000..1d60d74
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_caps.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby at gmail.com>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+/**************\
+* Capabilities *
+\**************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/*
+ * Fill the capabilities struct
+ * TODO: Merge this with EEPROM code when we are done with it
+ */
+int ath5k_hw_set_capabilities(struct ath5k_hw *ah)
+{
+	u16 ee_header;
+
+	/* Capabilities stored in the EEPROM */
+	ee_header = ah->ah_capabilities.cap_eeprom.ee_header;
+
+	if (ah->ah_version == AR5K_AR5210) {
+		/*
+		 * Set radio capabilities
+		 * (The AR5110 only supports the middle 5GHz band)
+		 */
+		ah->ah_capabilities.cap_range.range_5ghz_min = 5120;
+		ah->ah_capabilities.cap_range.range_5ghz_max = 5430;
+		ah->ah_capabilities.cap_range.range_2ghz_min = 0;
+		ah->ah_capabilities.cap_range.range_2ghz_max = 0;
+
+		/* Set supported modes */
+		ah->ah_capabilities.cap_mode |= AR5K_MODE_BIT_11A;
+		ah->ah_capabilities.cap_mode |= AR5K_MODE_BIT_11A_TURBO;
+	} else {
+		/*
+		 * XXX The tranceiver supports frequencies from 4920 to 6100GHz
+		 * XXX and from 2312 to 2732GHz. There are problems with the
+		 * XXX current ieee80211 implementation because the IEEE
+		 * XXX channel mapping does not support negative channel
+		 * XXX numbers (2312MHz is channel -19). Of course, this
+		 * XXX doesn't matter because these channels are out of range
+		 * XXX but some regulation domains like MKK (Japan) will
+		 * XXX support frequencies somewhere around 4.8GHz.
+		 */
+
+		/*
+		 * Set radio capabilities
+		 */
+
+		if (AR5K_EEPROM_HDR_11A(ee_header)) {
+			/* 4920 */
+			ah->ah_capabilities.cap_range.range_5ghz_min = 5005;
+			ah->ah_capabilities.cap_range.range_5ghz_max = 6100;
+
+			/* Set supported modes */
+			ah->ah_capabilities.cap_mode |= AR5K_MODE_BIT_11A;
+			ah->ah_capabilities.cap_mode |= AR5K_MODE_BIT_11A_TURBO;
+			if (ah->ah_version == AR5K_AR5212)
+				ah->ah_capabilities.cap_mode |=
+					AR5K_MODE_BIT_11G_TURBO;
+		}
+
+		/* Enable  802.11b if a 2GHz capable radio (2111/5112) is
+		 * connected */
+		if (AR5K_EEPROM_HDR_11B(ee_header) ||
+		    (AR5K_EEPROM_HDR_11G(ee_header) &&
+		     ah->ah_version != AR5K_AR5211)) {
+			/* 2312 */
+			ah->ah_capabilities.cap_range.range_2ghz_min = 2412;
+			ah->ah_capabilities.cap_range.range_2ghz_max = 2732;
+
+			if (AR5K_EEPROM_HDR_11B(ee_header))
+				ah->ah_capabilities.cap_mode |=
+					AR5K_MODE_BIT_11B;
+
+			if (AR5K_EEPROM_HDR_11G(ee_header) &&
+			    ah->ah_version != AR5K_AR5211)
+				ah->ah_capabilities.cap_mode |=
+					AR5K_MODE_BIT_11G;
+		}
+	}
+
+	/* GPIO */
+	ah->ah_gpio_npins = AR5K_NUM_GPIO;
+
+	/* Set number of supported TX queues */
+	ah->ah_capabilities.cap_queues.q_tx_num = 1;
+
+	return 0;
+}
+
+/* Main function used by the driver part to check caps */
+int ath5k_hw_get_capability(struct ath5k_hw *ah,
+		enum ath5k_capability_type cap_type,
+		u32 capability __unused, u32 *result)
+{
+	switch (cap_type) {
+	case AR5K_CAP_NUM_TXQUEUES:
+		if (result) {
+			*result = 1;
+			goto yes;
+		}
+	case AR5K_CAP_VEOL:
+		goto yes;
+	case AR5K_CAP_COMPRESSION:
+		if (ah->ah_version == AR5K_AR5212)
+			goto yes;
+		else
+			goto no;
+	case AR5K_CAP_BURST:
+		goto yes;
+	case AR5K_CAP_TPC:
+		goto yes;
+	case AR5K_CAP_BSSIDMASK:
+		if (ah->ah_version == AR5K_AR5212)
+			goto yes;
+		else
+			goto no;
+	case AR5K_CAP_XR:
+		if (ah->ah_version == AR5K_AR5212)
+			goto yes;
+		else
+			goto no;
+	default:
+		goto no;
+	}
+
+no:
+	return -EINVAL;
+yes:
+	return 0;
+}
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_desc.c b/gpxe/src/drivers/net/ath5k/ath5k_desc.c
new file mode 100644
index 0000000..76d0c1e
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_desc.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ * Copyright (c) 2007-2008 Pavel Roskin <proski at gnu.org>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+/******************************\
+ Hardware Descriptor Functions
+\******************************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/*
+ * TX Descriptors
+ */
+
+#define FCS_LEN	4
+
+/*
+ * Initialize the 2-word tx control descriptor on 5210/5211
+ */
+static int
+ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
+	unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type,
+	unsigned int tx_power __unused, unsigned int tx_rate0, unsigned int tx_tries0,
+	unsigned int key_index __unused, unsigned int antenna_mode, unsigned int flags,
+	unsigned int rtscts_rate __unused, unsigned int rtscts_duration)
+{
+	u32 frame_type;
+	struct ath5k_hw_2w_tx_ctl *tx_ctl;
+	unsigned int frame_len;
+
+	tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
+
+	/*
+	 * Validate input
+	 * - Zero retries don't make sense.
+	 * - A zero rate will put the HW into a mode where it continously sends
+	 *   noise on the channel, so it is important to avoid this.
+	 */
+	if (tx_tries0 == 0) {
+		DBG("ath5k: zero retries\n");
+		return -EINVAL;
+	}
+	if (tx_rate0 == 0) {
+		DBG("ath5k: zero rate\n");
+		return -EINVAL;
+	}
+
+	/* Clear descriptor */
+	memset(&desc->ud.ds_tx5210, 0, sizeof(struct ath5k_hw_5210_tx_desc));
+
+	/* Setup control descriptor */
+
+	/* Verify and set frame length */
+
+	frame_len = pkt_len + FCS_LEN;
+
+	if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
+		return -EINVAL;
+
+	tx_ctl->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
+
+	/* Verify and set buffer length */
+
+	if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN)
+		return -EINVAL;
+
+	tx_ctl->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
+
+	/*
+	 * Verify and set header length
+	 * XXX: I only found that on 5210 code, does it work on 5211 ?
+	 */
+	if (ah->ah_version == AR5K_AR5210) {
+		if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN)
+			return -EINVAL;
+		tx_ctl->tx_control_0 |=
+			AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN);
+	}
+
+	/*Diferences between 5210-5211*/
+	if (ah->ah_version == AR5K_AR5210) {
+		switch (type) {
+		case AR5K_PKT_TYPE_BEACON:
+		case AR5K_PKT_TYPE_PROBE_RESP:
+			frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY;
+		case AR5K_PKT_TYPE_PIFS:
+			frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS;
+		default:
+			frame_type = type /*<< 2 ?*/;
+		}
+
+		tx_ctl->tx_control_0 |=
+		AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) |
+		AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
+
+	} else {
+		tx_ctl->tx_control_0 |=
+			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) |
+			AR5K_REG_SM(antenna_mode,
+				AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT);
+		tx_ctl->tx_control_1 |=
+			AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE);
+	}
+#define _TX_FLAGS(_c, _flag)					\
+	if (flags & AR5K_TXDESC_##_flag) {			\
+		tx_ctl->tx_control_##_c |=			\
+			AR5K_2W_TX_DESC_CTL##_c##_##_flag;	\
+	}
+
+	_TX_FLAGS(0, CLRDMASK);
+	_TX_FLAGS(0, VEOL);
+	_TX_FLAGS(0, INTREQ);
+	_TX_FLAGS(0, RTSENA);
+	_TX_FLAGS(1, NOACK);
+
+#undef _TX_FLAGS
+
+	/*
+	 * RTS/CTS Duration [5210 ?]
+	 */
+	if ((ah->ah_version == AR5K_AR5210) &&
+			(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
+		tx_ctl->tx_control_1 |= rtscts_duration &
+				AR5K_2W_TX_DESC_CTL1_RTS_DURATION;
+
+	return 0;
+}
+
+/*
+ * Initialize the 4-word tx control descriptor on 5212
+ */
+static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
+	struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len __unused,
+	enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0,
+	unsigned int tx_tries0, unsigned int key_index __unused,
+	unsigned int antenna_mode, unsigned int flags,
+	unsigned int rtscts_rate,
+	unsigned int rtscts_duration)
+{
+	struct ath5k_hw_4w_tx_ctl *tx_ctl;
+	unsigned int frame_len;
+
+	tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
+
+	/*
+	 * Validate input
+	 * - Zero retries don't make sense.
+	 * - A zero rate will put the HW into a mode where it continously sends
+	 *   noise on the channel, so it is important to avoid this.
+	 */
+	if (tx_tries0 == 0) {
+		DBG("ath5k: zero retries\n");
+		return -EINVAL;
+	}
+	if (tx_rate0 == 0) {
+		DBG("ath5k: zero rate\n");
+		return -EINVAL;
+	}
+
+	tx_power += ah->ah_txpower.txp_offset;
+	if (tx_power > AR5K_TUNE_MAX_TXPOWER)
+		tx_power = AR5K_TUNE_MAX_TXPOWER;
+
+	/* Clear descriptor */
+	memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
+
+	/* Setup control descriptor */
+
+	/* Verify and set frame length */
+
+	frame_len = pkt_len + FCS_LEN;
+
+	if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
+		return -EINVAL;
+
+	tx_ctl->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
+
+	/* Verify and set buffer length */
+
+	if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN)
+		return -EINVAL;
+
+	tx_ctl->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
+
+	tx_ctl->tx_control_0 |=
+		AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
+		AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
+	tx_ctl->tx_control_1 |= AR5K_REG_SM(type,
+					AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
+	tx_ctl->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
+					AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
+	tx_ctl->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
+
+#define _TX_FLAGS(_c, _flag)					\
+	if (flags & AR5K_TXDESC_##_flag) {			\
+		tx_ctl->tx_control_##_c |=			\
+			AR5K_4W_TX_DESC_CTL##_c##_##_flag;	\
+	}
+
+	_TX_FLAGS(0, CLRDMASK);
+	_TX_FLAGS(0, VEOL);
+	_TX_FLAGS(0, INTREQ);
+	_TX_FLAGS(0, RTSENA);
+	_TX_FLAGS(0, CTSENA);
+	_TX_FLAGS(1, NOACK);
+
+#undef _TX_FLAGS
+
+	/*
+	 * RTS/CTS
+	 */
+	if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) {
+		if ((flags & AR5K_TXDESC_RTSENA) &&
+				(flags & AR5K_TXDESC_CTSENA))
+			return -EINVAL;
+		tx_ctl->tx_control_2 |= rtscts_duration &
+				AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
+		tx_ctl->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
+				AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
+	}
+
+	return 0;
+}
+
+/*
+ * Proccess the tx status descriptor on 5210/5211
+ */
+static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah __unused,
+		struct ath5k_desc *desc, struct ath5k_tx_status *ts)
+{
+	struct ath5k_hw_2w_tx_ctl *tx_ctl;
+	struct ath5k_hw_tx_status *tx_status;
+
+	tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
+	tx_status = &desc->ud.ds_tx5210.tx_stat;
+
+	/* No frame has been send or error */
+	if ((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0)
+		return -EINPROGRESS;
+
+	/*
+	 * Get descriptor status
+	 */
+	ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
+	ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
+	ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
+	/*TODO: ts->ts_virtcol + test*/
+	ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
+		AR5K_DESC_TX_STATUS1_SEQ_NUM);
+	ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
+		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
+	ts->ts_antenna = 1;
+	ts->ts_status = 0;
+	ts->ts_rate[0] = AR5K_REG_MS(tx_ctl->tx_control_0,
+		AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
+	ts->ts_retry[0] = ts->ts_longretry;
+	ts->ts_final_idx = 0;
+
+	if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
+		if (tx_status->tx_status_0 &
+				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
+			ts->ts_status |= AR5K_TXERR_XRETRY;
+
+		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
+			ts->ts_status |= AR5K_TXERR_FIFO;
+
+		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
+			ts->ts_status |= AR5K_TXERR_FILT;
+	}
+
+	return 0;
+}
+
+/*
+ * Proccess a tx status descriptor on 5212
+ */
+static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah __unused,
+		struct ath5k_desc *desc, struct ath5k_tx_status *ts)
+{
+	struct ath5k_hw_4w_tx_ctl *tx_ctl;
+	struct ath5k_hw_tx_status *tx_status;
+
+	tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
+	tx_status = &desc->ud.ds_tx5212.tx_stat;
+
+	/* No frame has been send or error */
+	if (!(tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE))
+		return -EINPROGRESS;
+
+	/*
+	 * Get descriptor status
+	 */
+	ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
+	ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
+	ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
+		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
+	ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
+		AR5K_DESC_TX_STATUS1_SEQ_NUM);
+	ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
+		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
+	ts->ts_antenna = (tx_status->tx_status_1 &
+		AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
+	ts->ts_status = 0;
+
+	ts->ts_final_idx = AR5K_REG_MS(tx_status->tx_status_1,
+			AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX);
+
+	ts->ts_retry[0] = ts->ts_longretry;
+	ts->ts_rate[0] = tx_ctl->tx_control_3 &
+		AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
+
+	/* TX error */
+	if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
+		if (tx_status->tx_status_0 &
+				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
+			ts->ts_status |= AR5K_TXERR_XRETRY;
+
+		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
+			ts->ts_status |= AR5K_TXERR_FIFO;
+
+		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
+			ts->ts_status |= AR5K_TXERR_FILT;
+	}
+
+	return 0;
+}
+
+/*
+ * RX Descriptors
+ */
+
+/*
+ * Initialize an rx control descriptor
+ */
+static int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah __unused,
+				  struct ath5k_desc *desc,
+				  u32 size, unsigned int flags)
+{
+	struct ath5k_hw_rx_ctl *rx_ctl;
+
+	rx_ctl = &desc->ud.ds_rx.rx_ctl;
+
+	/*
+	 * Clear the descriptor
+	 * If we don't clean the status descriptor,
+	 * while scanning we get too many results,
+	 * most of them virtual, after some secs
+	 * of scanning system hangs. M.F.
+	*/
+	memset(&desc->ud.ds_rx, 0, sizeof(struct ath5k_hw_all_rx_desc));
+
+	/* Setup descriptor */
+	rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
+	if (rx_ctl->rx_control_1 != size)
+		return -EINVAL;
+
+	if (flags & AR5K_RXDESC_INTREQ)
+		rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
+
+	return 0;
+}
+
+/*
+ * Proccess the rx status descriptor on 5210/5211
+ */
+static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah __unused,
+		struct ath5k_desc *desc, struct ath5k_rx_status *rs)
+{
+	struct ath5k_hw_rx_status *rx_status;
+
+	rx_status = &desc->ud.ds_rx.u.rx_stat;
+
+	/* No frame received / not ready */
+	if (!(rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_DONE))
+		return -EINPROGRESS;
+
+	/*
+	 * Frame receive status
+	 */
+	rs->rs_datalen = rx_status->rx_status_0 &
+		AR5K_5210_RX_DESC_STATUS0_DATA_LEN;
+	rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL);
+	rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE);
+	rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA);
+	rs->rs_more = !!(rx_status->rx_status_0 &
+		AR5K_5210_RX_DESC_STATUS0_MORE);
+	/* TODO: this timestamp is 13 bit, later on we assume 15 bit */
+	rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
+		AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
+	rs->rs_status = 0;
+	rs->rs_phyerr = 0;
+	rs->rs_keyix = AR5K_RXKEYIX_INVALID;
+
+	/*
+	 * Receive/descriptor errors
+	 */
+	if (!(rx_status->rx_status_1 &
+	      AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
+		if (rx_status->rx_status_1 &
+				AR5K_5210_RX_DESC_STATUS1_CRC_ERROR)
+			rs->rs_status |= AR5K_RXERR_CRC;
+
+		if (rx_status->rx_status_1 &
+				AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN)
+			rs->rs_status |= AR5K_RXERR_FIFO;
+
+		if (rx_status->rx_status_1 &
+				AR5K_5210_RX_DESC_STATUS1_PHY_ERROR) {
+			rs->rs_status |= AR5K_RXERR_PHY;
+			rs->rs_phyerr |= AR5K_REG_MS(rx_status->rx_status_1,
+				AR5K_5210_RX_DESC_STATUS1_PHY_ERROR);
+		}
+
+		if (rx_status->rx_status_1 &
+				AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
+			rs->rs_status |= AR5K_RXERR_DECRYPT;
+	}
+
+	return 0;
+}
+
+/*
+ * Proccess the rx status descriptor on 5212
+ */
+static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah __unused,
+		struct ath5k_desc *desc, struct ath5k_rx_status *rs)
+{
+	struct ath5k_hw_rx_status *rx_status;
+	struct ath5k_hw_rx_error *rx_err;
+
+	rx_status = &desc->ud.ds_rx.u.rx_stat;
+
+	/* Overlay on error */
+	rx_err = &desc->ud.ds_rx.u.rx_err;
+
+	/* No frame received / not ready */
+	if (!(rx_status->rx_status_1 & AR5K_5212_RX_DESC_STATUS1_DONE))
+		return -EINPROGRESS;
+
+	/*
+	 * Frame receive status
+	 */
+	rs->rs_datalen = rx_status->rx_status_0 &
+		AR5K_5212_RX_DESC_STATUS0_DATA_LEN;
+	rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL);
+	rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE);
+	rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA);
+	rs->rs_more = !!(rx_status->rx_status_0 &
+		AR5K_5212_RX_DESC_STATUS0_MORE);
+	rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
+		AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
+	rs->rs_status = 0;
+	rs->rs_phyerr = 0;
+	rs->rs_keyix = AR5K_RXKEYIX_INVALID;
+
+	/*
+	 * Receive/descriptor errors
+	 */
+	if (!(rx_status->rx_status_1 &
+	      AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
+		if (rx_status->rx_status_1 &
+				AR5K_5212_RX_DESC_STATUS1_CRC_ERROR)
+			rs->rs_status |= AR5K_RXERR_CRC;
+
+		if (rx_status->rx_status_1 &
+				AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) {
+			rs->rs_status |= AR5K_RXERR_PHY;
+			rs->rs_phyerr |= AR5K_REG_MS(rx_err->rx_error_1,
+					   AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
+		}
+
+		if (rx_status->rx_status_1 &
+				AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
+			rs->rs_status |= AR5K_RXERR_DECRYPT;
+
+		if (rx_status->rx_status_1 &
+				AR5K_5212_RX_DESC_STATUS1_MIC_ERROR)
+			rs->rs_status |= AR5K_RXERR_MIC;
+	}
+
+	return 0;
+}
+
+/*
+ * Init function pointers inside ath5k_hw struct
+ */
+int ath5k_hw_init_desc_functions(struct ath5k_hw *ah)
+{
+
+	if (ah->ah_version != AR5K_AR5210 &&
+	    ah->ah_version != AR5K_AR5211 &&
+	    ah->ah_version != AR5K_AR5212)
+		return -ENOTSUP;
+
+	if (ah->ah_version == AR5K_AR5212) {
+		ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc;
+		ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc;
+		ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status;
+	} else {
+		ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc;
+		ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc;
+		ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status;
+	}
+
+	if (ah->ah_version == AR5K_AR5212)
+		ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status;
+	else if (ah->ah_version <= AR5K_AR5211)
+		ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status;
+
+	return 0;
+}
+
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_dma.c b/gpxe/src/drivers/net/ath5k/ath5k_dma.c
new file mode 100644
index 0000000..23c4cf9
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_dma.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+/*************************************\
+* DMA and interrupt masking functions *
+\*************************************/
+
+/*
+ * dma.c - DMA and interrupt masking functions
+ *
+ * Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and
+ * handle queue setup for 5210 chipset (rest are handled on qcu.c).
+ * Also we setup interrupt mask register (IMR) and read the various iterrupt
+ * status registers (ISR).
+ *
+ * TODO: Handle SISR on 5211+ and introduce a function to return the queue
+ * number that resulted the interrupt.
+ */
+
+#include <unistd.h>
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/*********\
+* Receive *
+\*********/
+
+/**
+ * ath5k_hw_start_rx_dma - Start DMA receive
+ *
+ * @ah:	The &struct ath5k_hw
+ */
+void ath5k_hw_start_rx_dma(struct ath5k_hw *ah)
+{
+	ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR);
+	ath5k_hw_reg_read(ah, AR5K_CR);
+}
+
+/**
+ * ath5k_hw_stop_rx_dma - Stop DMA receive
+ *
+ * @ah:	The &struct ath5k_hw
+ */
+int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
+{
+	unsigned int i;
+
+	ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR);
+
+	/*
+	 * It may take some time to disable the DMA receive unit
+	 */
+	for (i = 1000; i > 0 &&
+			(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
+			i--)
+		udelay(10);
+
+	return i ? 0 : -EBUSY;
+}
+
+/**
+ * ath5k_hw_get_rxdp - Get RX Descriptor's address
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * XXX: Is RXDP read and clear ?
+ */
+u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah)
+{
+	return ath5k_hw_reg_read(ah, AR5K_RXDP);
+}
+
+/**
+ * ath5k_hw_set_rxdp - Set RX Descriptor's address
+ *
+ * @ah: The &struct ath5k_hw
+ * @phys_addr: RX descriptor address
+ *
+ * XXX: Should we check if rx is enabled before setting rxdp ?
+ */
+void ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
+{
+	ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP);
+}
+
+
+/**********\
+* Transmit *
+\**********/
+
+/**
+ * ath5k_hw_start_tx_dma - Start DMA transmit for a specific queue
+ *
+ * @ah: The &struct ath5k_hw
+ * @queue: The hw queue number
+ *
+ * Start DMA transmit for a specific queue and since 5210 doesn't have
+ * QCU/DCU, set up queue parameters for 5210 here based on queue type (one
+ * queue for normal data and one queue for beacons). For queue setup
+ * on newer chips check out qcu.c. Returns -EINVAL if queue number is out
+ * of range or if queue is already disabled.
+ *
+ * NOTE: Must be called after setting up tx control descriptor for that
+ * queue (see below).
+ */
+int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue)
+{
+	u32 tx_queue;
+
+	/* Return if queue is declared inactive */
+	if (ah->ah_txq.tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return -EIO;
+
+	if (ah->ah_version == AR5K_AR5210) {
+		tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
+
+		/* Assume always a data queue */
+		tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0;
+
+		/* Start queue */
+		ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
+		ath5k_hw_reg_read(ah, AR5K_CR);
+	} else {
+		/* Return if queue is disabled */
+		if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue))
+			return -EIO;
+
+		/* Start queue */
+		AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue);
+	}
+
+	return 0;
+}
+
+/**
+ * ath5k_hw_stop_tx_dma - Stop DMA transmit on a specific queue
+ *
+ * @ah: The &struct ath5k_hw
+ * @queue: The hw queue number
+ *
+ * Stop DMA transmit on a specific hw queue and drain queue so we don't
+ * have any pending frames. Returns -EBUSY if we still have pending frames,
+ * -EINVAL if queue number is out of range.
+ *
+ */
+int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
+{
+	unsigned int i = 40;
+	u32 tx_queue, pending;
+
+	/* Return if queue is declared inactive */
+	if (ah->ah_txq.tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return -EIO;
+
+	if (ah->ah_version == AR5K_AR5210) {
+		tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
+
+		/* Assume a data queue */
+		tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0;
+
+		/* Stop queue */
+		ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
+		ath5k_hw_reg_read(ah, AR5K_CR);
+	} else {
+		/*
+		 * Schedule TX disable and wait until queue is empty
+		 */
+		AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue);
+
+		/*Check for pending frames*/
+		do {
+			pending = ath5k_hw_reg_read(ah,
+				AR5K_QUEUE_STATUS(queue)) &
+				AR5K_QCU_STS_FRMPENDCNT;
+			udelay(100);
+		} while (--i && pending);
+
+		/* For 2413+ order PCU to drop packets using
+		 * QUIET mechanism */
+		if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) && pending) {
+			/* Set periodicity and duration */
+			ath5k_hw_reg_write(ah,
+				AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)|
+				AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR),
+				AR5K_QUIET_CTL2);
+
+			/* Enable quiet period for current TSF */
+			ath5k_hw_reg_write(ah,
+				AR5K_QUIET_CTL1_QT_EN |
+				AR5K_REG_SM(ath5k_hw_reg_read(ah,
+						AR5K_TSF_L32_5211) >> 10,
+						AR5K_QUIET_CTL1_NEXT_QT_TSF),
+				AR5K_QUIET_CTL1);
+
+			/* Force channel idle high */
+			AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
+					AR5K_DIAG_SW_CHANEL_IDLE_HIGH);
+
+			/* Wait a while and disable mechanism */
+			udelay(200);
+			AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1,
+						AR5K_QUIET_CTL1_QT_EN);
+
+			/* Re-check for pending frames */
+			i = 40;
+			do {
+				pending = ath5k_hw_reg_read(ah,
+					AR5K_QUEUE_STATUS(queue)) &
+					AR5K_QCU_STS_FRMPENDCNT;
+				udelay(100);
+			} while (--i && pending);
+
+			AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211,
+					AR5K_DIAG_SW_CHANEL_IDLE_HIGH);
+		}
+
+		/* Clear register */
+		ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
+		if (pending)
+			return -EBUSY;
+	}
+
+	/* TODO: Check for success on 5210 else return error */
+	return 0;
+}
+
+/**
+ * ath5k_hw_get_txdp - Get TX Descriptor's address for a specific queue
+ *
+ * @ah: The &struct ath5k_hw
+ * @queue: The hw queue number
+ *
+ * Get TX descriptor's address for a specific queue. For 5210 we ignore
+ * the queue number and use tx queue type since we only have 2 queues.
+ * We use TXDP0 for normal data queue and TXDP1 for beacon queue.
+ * For newer chips with QCU/DCU we just read the corresponding TXDP register.
+ *
+ * XXX: Is TXDP read and clear ?
+ */
+u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue)
+{
+	u16 tx_reg;
+
+	/*
+	 * Get the transmit queue descriptor pointer from the selected queue
+	 */
+	/*5210 doesn't have QCU*/
+	if (ah->ah_version == AR5K_AR5210) {
+		/* Assume a data queue */
+		tx_reg = AR5K_NOQCU_TXDP0;
+	} else {
+		tx_reg = AR5K_QUEUE_TXDP(queue);
+	}
+
+	return ath5k_hw_reg_read(ah, tx_reg);
+}
+
+/**
+ * ath5k_hw_set_txdp - Set TX Descriptor's address for a specific queue
+ *
+ * @ah: The &struct ath5k_hw
+ * @queue: The hw queue number
+ *
+ * Set TX descriptor's address for a specific queue. For 5210 we ignore
+ * the queue number and we use tx queue type since we only have 2 queues
+ * so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue.
+ * For newer chips with QCU/DCU we just set the corresponding TXDP register.
+ * Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still
+ * active.
+ */
+int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr)
+{
+	u16 tx_reg;
+
+	/*
+	 * Set the transmit queue descriptor pointer register by type
+	 * on 5210
+	 */
+	if (ah->ah_version == AR5K_AR5210) {
+		/* Assume a data queue */
+		tx_reg = AR5K_NOQCU_TXDP0;
+	} else {
+		/*
+		 * Set the transmit queue descriptor pointer for
+		 * the selected queue on QCU for 5211+
+		 * (this won't work if the queue is still active)
+		 */
+		if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
+			return -EIO;
+
+		tx_reg = AR5K_QUEUE_TXDP(queue);
+	}
+
+	/* Set descriptor pointer */
+	ath5k_hw_reg_write(ah, phys_addr, tx_reg);
+
+	return 0;
+}
+
+/**
+ * ath5k_hw_update_tx_triglevel - Update tx trigger level
+ *
+ * @ah: The &struct ath5k_hw
+ * @increase: Flag to force increase of trigger level
+ *
+ * This function increases/decreases the tx trigger level for the tx fifo
+ * buffer (aka FIFO threshold) that is used to indicate when PCU flushes
+ * the buffer and transmits it's data. Lowering this results sending small
+ * frames more quickly but can lead to tx underruns, raising it a lot can
+ * result other problems (i think bmiss is related). Right now we start with
+ * the lowest possible (64Bytes) and if we get tx underrun we increase it using
+ * the increase flag. Returns -EIO if we have have reached maximum/minimum.
+ *
+ * XXX: Link this with tx DMA size ?
+ * XXX: Use it to save interrupts ?
+ * TODO: Needs testing, i think it's related to bmiss...
+ */
+int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, int increase)
+{
+	u32 trigger_level, imr;
+	int ret = -EIO;
+
+	/*
+	 * Disable interrupts by setting the mask
+	 */
+	imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL);
+
+	trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG),
+			AR5K_TXCFG_TXFULL);
+
+	if (!increase) {
+		if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES)
+			goto done;
+	} else
+		trigger_level +=
+			((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2);
+
+	/*
+	 * Update trigger level on success
+	 */
+	if (ah->ah_version == AR5K_AR5210)
+		ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL);
+	else
+		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
+				AR5K_TXCFG_TXFULL, trigger_level);
+
+	ret = 0;
+
+done:
+	/*
+	 * Restore interrupt mask
+	 */
+	ath5k_hw_set_imr(ah, imr);
+
+	return ret;
+}
+
+/*******************\
+* Interrupt masking *
+\*******************/
+
+/**
+ * ath5k_hw_is_intr_pending - Check if we have pending interrupts
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Check if we have pending interrupts to process. Returns 1 if we
+ * have pending interrupts and 0 if we haven't.
+ */
+int ath5k_hw_is_intr_pending(struct ath5k_hw *ah)
+{
+	return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0;
+}
+
+/**
+ * ath5k_hw_get_isr - Get interrupt status
+ *
+ * @ah: The @struct ath5k_hw
+ * @interrupt_mask: Driver's interrupt mask used to filter out
+ * interrupts in sw.
+ *
+ * This function is used inside our interrupt handler to determine the reason
+ * for the interrupt by reading Primary Interrupt Status Register. Returns an
+ * abstract interrupt status mask which is mostly ISR with some uncommon bits
+ * being mapped on some standard non hw-specific positions
+ * (check out &ath5k_int).
+ *
+ * NOTE: We use read-and-clear register, so after this function is called ISR
+ * is zeroed.
+ */
+int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
+{
+	u32 data;
+
+	/*
+	 * Read interrupt status from the Interrupt Status register
+	 * on 5210
+	 */
+	if (ah->ah_version == AR5K_AR5210) {
+		data = ath5k_hw_reg_read(ah, AR5K_ISR);
+		if (data == AR5K_INT_NOCARD) {
+			*interrupt_mask = data;
+			return -ENODEV;
+		}
+	} else {
+		/*
+		 * Read interrupt status from Interrupt
+		 * Status Register shadow copy (Read And Clear)
+		 *
+		 * Note: PISR/SISR Not available on 5210
+		 */
+		data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR);
+		if (data == AR5K_INT_NOCARD) {
+			*interrupt_mask = data;
+			return -ENODEV;
+		}
+	}
+
+	/*
+	 * Get abstract interrupt mask (driver-compatible)
+	 */
+	*interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr;
+
+	if (ah->ah_version != AR5K_AR5210) {
+		u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2);
+
+		/*HIU = Host Interface Unit (PCI etc)*/
+		if (data & (AR5K_ISR_HIUERR))
+			*interrupt_mask |= AR5K_INT_FATAL;
+
+		/*Beacon Not Ready*/
+		if (data & (AR5K_ISR_BNR))
+			*interrupt_mask |= AR5K_INT_BNR;
+
+		if (sisr2 & (AR5K_SISR2_SSERR | AR5K_SISR2_DPERR |
+			     AR5K_SISR2_MCABT))
+			*interrupt_mask |= AR5K_INT_FATAL;
+
+		if (data & AR5K_ISR_TIM)
+			*interrupt_mask |= AR5K_INT_TIM;
+
+		if (data & AR5K_ISR_BCNMISC) {
+			if (sisr2 & AR5K_SISR2_TIM)
+				*interrupt_mask |= AR5K_INT_TIM;
+			if (sisr2 & AR5K_SISR2_DTIM)
+				*interrupt_mask |= AR5K_INT_DTIM;
+			if (sisr2 & AR5K_SISR2_DTIM_SYNC)
+				*interrupt_mask |= AR5K_INT_DTIM_SYNC;
+			if (sisr2 & AR5K_SISR2_BCN_TIMEOUT)
+				*interrupt_mask |= AR5K_INT_BCN_TIMEOUT;
+			if (sisr2 & AR5K_SISR2_CAB_TIMEOUT)
+				*interrupt_mask |= AR5K_INT_CAB_TIMEOUT;
+		}
+
+		if (data & AR5K_ISR_RXDOPPLER)
+			*interrupt_mask |= AR5K_INT_RX_DOPPLER;
+		if (data & AR5K_ISR_QCBRORN) {
+			*interrupt_mask |= AR5K_INT_QCBRORN;
+			ah->ah_txq_isr |= AR5K_REG_MS(
+					ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
+					AR5K_SISR3_QCBRORN);
+		}
+		if (data & AR5K_ISR_QCBRURN) {
+			*interrupt_mask |= AR5K_INT_QCBRURN;
+			ah->ah_txq_isr |= AR5K_REG_MS(
+					ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
+					AR5K_SISR3_QCBRURN);
+		}
+		if (data & AR5K_ISR_QTRIG) {
+			*interrupt_mask |= AR5K_INT_QTRIG;
+			ah->ah_txq_isr |= AR5K_REG_MS(
+					ath5k_hw_reg_read(ah, AR5K_RAC_SISR4),
+					AR5K_SISR4_QTRIG);
+		}
+
+		if (data & AR5K_ISR_TXOK)
+			ah->ah_txq_isr |= AR5K_REG_MS(
+					ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
+					AR5K_SISR0_QCU_TXOK);
+
+		if (data & AR5K_ISR_TXDESC)
+			ah->ah_txq_isr |= AR5K_REG_MS(
+					ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
+					AR5K_SISR0_QCU_TXDESC);
+
+		if (data & AR5K_ISR_TXERR)
+			ah->ah_txq_isr |= AR5K_REG_MS(
+					ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
+					AR5K_SISR1_QCU_TXERR);
+
+		if (data & AR5K_ISR_TXEOL)
+			ah->ah_txq_isr |= AR5K_REG_MS(
+					ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
+					AR5K_SISR1_QCU_TXEOL);
+
+		if (data & AR5K_ISR_TXURN)
+			ah->ah_txq_isr |= AR5K_REG_MS(
+					ath5k_hw_reg_read(ah, AR5K_RAC_SISR2),
+					AR5K_SISR2_QCU_TXURN);
+	} else {
+		if (data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT |
+			    AR5K_ISR_HIUERR | AR5K_ISR_DPERR))
+			*interrupt_mask |= AR5K_INT_FATAL;
+
+		/*
+		 * XXX: BMISS interrupts may occur after association.
+		 * I found this on 5210 code but it needs testing. If this is
+		 * true we should disable them before assoc and re-enable them
+		 * after a successful assoc + some jiffies.
+			interrupt_mask &= ~AR5K_INT_BMISS;
+		 */
+	}
+
+	return 0;
+}
+
+/**
+ * ath5k_hw_set_imr - Set interrupt mask
+ *
+ * @ah: The &struct ath5k_hw
+ * @new_mask: The new interrupt mask to be set
+ *
+ * Set the interrupt mask in hw to save interrupts. We do that by mapping
+ * ath5k_int bits to hw-specific bits to remove abstraction and writing
+ * Interrupt Mask Register.
+ */
+enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
+{
+	enum ath5k_int old_mask, int_mask;
+
+	old_mask = ah->ah_imr;
+
+	/*
+	 * Disable card interrupts to prevent any race conditions
+	 * (they will be re-enabled afterwards if AR5K_INT GLOBAL
+	 * is set again on the new mask).
+	 */
+	if (old_mask & AR5K_INT_GLOBAL) {
+		ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER);
+		ath5k_hw_reg_read(ah, AR5K_IER);
+	}
+
+	/*
+	 * Add additional, chipset-dependent interrupt mask flags
+	 * and write them to the IMR (interrupt mask register).
+	 */
+	int_mask = new_mask & AR5K_INT_COMMON;
+
+	if (ah->ah_version != AR5K_AR5210) {
+		/* Preserve per queue TXURN interrupt mask */
+		u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2)
+				& AR5K_SIMR2_QCU_TXURN;
+
+		if (new_mask & AR5K_INT_FATAL) {
+			int_mask |= AR5K_IMR_HIUERR;
+			simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR
+				| AR5K_SIMR2_DPERR);
+		}
+
+		/*Beacon Not Ready*/
+		if (new_mask & AR5K_INT_BNR)
+			int_mask |= AR5K_INT_BNR;
+
+		if (new_mask & AR5K_INT_TIM)
+			int_mask |= AR5K_IMR_TIM;
+
+		if (new_mask & AR5K_INT_TIM)
+			simr2 |= AR5K_SISR2_TIM;
+		if (new_mask & AR5K_INT_DTIM)
+			simr2 |= AR5K_SISR2_DTIM;
+		if (new_mask & AR5K_INT_DTIM_SYNC)
+			simr2 |= AR5K_SISR2_DTIM_SYNC;
+		if (new_mask & AR5K_INT_BCN_TIMEOUT)
+			simr2 |= AR5K_SISR2_BCN_TIMEOUT;
+		if (new_mask & AR5K_INT_CAB_TIMEOUT)
+			simr2 |= AR5K_SISR2_CAB_TIMEOUT;
+
+		if (new_mask & AR5K_INT_RX_DOPPLER)
+			int_mask |= AR5K_IMR_RXDOPPLER;
+
+		/* Note: Per queue interrupt masks
+		 * are set via reset_tx_queue (qcu.c) */
+		ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
+		ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2);
+
+	} else {
+		if (new_mask & AR5K_INT_FATAL)
+			int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT
+				| AR5K_IMR_HIUERR | AR5K_IMR_DPERR);
+
+		ath5k_hw_reg_write(ah, int_mask, AR5K_IMR);
+	}
+
+	/* If RXNOFRM interrupt is masked disable it
+	 * by setting AR5K_RXNOFRM to zero */
+	if (!(new_mask & AR5K_INT_RXNOFRM))
+		ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM);
+
+	/* Store new interrupt mask */
+	ah->ah_imr = new_mask;
+
+	/* ..re-enable interrupts if AR5K_INT_GLOBAL is set */
+	if (new_mask & AR5K_INT_GLOBAL) {
+		ath5k_hw_reg_write(ah, ah->ah_ier, AR5K_IER);
+		ath5k_hw_reg_read(ah, AR5K_IER);
+	}
+
+	return old_mask;
+}
+
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_eeprom.c b/gpxe/src/drivers/net/ath5k/ath5k_eeprom.c
new file mode 100644
index 0000000..0f62c4c
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_eeprom.c
@@ -0,0 +1,1760 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm at gmail.com>
+ * Copyright (c) 2008-2009 Felix Fietkau <nbd at openwrt.org>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+/*************************************\
+* EEPROM access functions and helpers *
+\*************************************/
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/*
+ * Read from eeprom
+ */
+static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data)
+{
+	u32 status, timeout;
+
+	/*
+	 * Initialize EEPROM access
+	 */
+	if (ah->ah_version == AR5K_AR5210) {
+		AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE);
+		(void)ath5k_hw_reg_read(ah, AR5K_EEPROM_BASE + (4 * offset));
+	} else {
+		ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE);
+		AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD,
+				AR5K_EEPROM_CMD_READ);
+	}
+
+	for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
+		status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS);
+		if (status & AR5K_EEPROM_STAT_RDDONE) {
+			if (status & AR5K_EEPROM_STAT_RDERR)
+				return -EIO;
+			*data = (u16)(ath5k_hw_reg_read(ah, AR5K_EEPROM_DATA) &
+					0xffff);
+			return 0;
+		}
+		udelay(15);
+	}
+
+	return -ETIMEDOUT;
+}
+
+/*
+ * Translate binary channel representation in EEPROM to frequency
+ */
+static u16 ath5k_eeprom_bin2freq(struct ath5k_eeprom_info *ee, u16 bin,
+                                 unsigned int mode)
+{
+	u16 val;
+
+	if (bin == AR5K_EEPROM_CHANNEL_DIS)
+		return bin;
+
+	if (mode == AR5K_EEPROM_MODE_11A) {
+		if (ee->ee_version > AR5K_EEPROM_VERSION_3_2)
+			val = (5 * bin) + 4800;
+		else
+			val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 :
+				(bin * 10) + 5100;
+	} else {
+		if (ee->ee_version > AR5K_EEPROM_VERSION_3_2)
+			val = bin + 2300;
+		else
+			val = bin + 2400;
+	}
+
+	return val;
+}
+
+/*
+ * Initialize eeprom & capabilities structs
+ */
+static int
+ath5k_eeprom_init_header(struct ath5k_hw *ah)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	int ret;
+	u16 val;
+
+	/*
+	 * Read values from EEPROM and store them in the capability structure
+	 */
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic);
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect);
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain);
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version);
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header);
+
+	/* Return if we have an old EEPROM */
+	if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0)
+		return 0;
+
+	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version),
+	    ee_ant_gain);
+
+	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
+		AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0);
+		AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1);
+
+		/* XXX: Don't know which versions include these two */
+		AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC2, ee_misc2);
+
+		if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3)
+			AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC3, ee_misc3);
+
+		if (ee->ee_version >= AR5K_EEPROM_VERSION_5_0) {
+			AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC4, ee_misc4);
+			AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC5, ee_misc5);
+			AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC6, ee_misc6);
+		}
+	}
+
+	if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) {
+		AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val);
+		ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7;
+		ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7;
+
+		AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val);
+		ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7;
+		ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7;
+	}
+
+	AR5K_EEPROM_READ(AR5K_EEPROM_IS_HB63, val);
+
+	if ((ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4)) && val)
+		ee->ee_is_hb63 = 1;
+	else
+		ee->ee_is_hb63 = 0;
+
+	AR5K_EEPROM_READ(AR5K_EEPROM_RFKILL, val);
+	ee->ee_rfkill_pin = (u8) AR5K_REG_MS(val, AR5K_EEPROM_RFKILL_GPIO_SEL);
+	ee->ee_rfkill_pol = val & AR5K_EEPROM_RFKILL_POLARITY ? 1 : 0;
+
+	return 0;
+}
+
+
+/*
+ * Read antenna infos from eeprom
+ */
+static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset,
+		unsigned int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	u32 o = *offset;
+	u16 val;
+	int ret, i = 0;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_switch_settling[mode]	= (val >> 8) & 0x7f;
+	ee->ee_atn_tx_rx[mode]		= (val >> 2) & 0x3f;
+	ee->ee_ant_control[mode][i]	= (val << 4) & 0x3f;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_ant_control[mode][i++]	|= (val >> 12) & 0xf;
+	ee->ee_ant_control[mode][i++]	= (val >> 6) & 0x3f;
+	ee->ee_ant_control[mode][i++]	= val & 0x3f;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_ant_control[mode][i++]	= (val >> 10) & 0x3f;
+	ee->ee_ant_control[mode][i++]	= (val >> 4) & 0x3f;
+	ee->ee_ant_control[mode][i]	= (val << 2) & 0x3f;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_ant_control[mode][i++]	|= (val >> 14) & 0x3;
+	ee->ee_ant_control[mode][i++]	= (val >> 8) & 0x3f;
+	ee->ee_ant_control[mode][i++]	= (val >> 2) & 0x3f;
+	ee->ee_ant_control[mode][i]	= (val << 4) & 0x3f;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_ant_control[mode][i++]	|= (val >> 12) & 0xf;
+	ee->ee_ant_control[mode][i++]	= (val >> 6) & 0x3f;
+	ee->ee_ant_control[mode][i++]	= val & 0x3f;
+
+	/* Get antenna modes */
+	ah->ah_antenna[mode][0] =
+	    (ee->ee_ant_control[mode][0] << 4);
+	ah->ah_antenna[mode][AR5K_ANT_FIXED_A] =
+	     ee->ee_ant_control[mode][1] 	|
+	    (ee->ee_ant_control[mode][2] << 6) 	|
+	    (ee->ee_ant_control[mode][3] << 12) |
+	    (ee->ee_ant_control[mode][4] << 18) |
+	    (ee->ee_ant_control[mode][5] << 24);
+	ah->ah_antenna[mode][AR5K_ANT_FIXED_B] =
+	     ee->ee_ant_control[mode][6] 	|
+	    (ee->ee_ant_control[mode][7] << 6) 	|
+	    (ee->ee_ant_control[mode][8] << 12) |
+	    (ee->ee_ant_control[mode][9] << 18) |
+	    (ee->ee_ant_control[mode][10] << 24);
+
+	/* return new offset */
+	*offset = o;
+
+	return 0;
+}
+
+/*
+ * Read supported modes and some mode-specific calibration data
+ * from eeprom
+ */
+static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
+		unsigned int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	u32 o = *offset;
+	u16 val;
+	int ret;
+
+	ee->ee_n_piers[mode] = 0;
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_adc_desired_size[mode]	= (s8)((val >> 8) & 0xff);
+	switch(mode) {
+	case AR5K_EEPROM_MODE_11A:
+		ee->ee_ob[mode][3]	= (val >> 5) & 0x7;
+		ee->ee_db[mode][3]	= (val >> 2) & 0x7;
+		ee->ee_ob[mode][2]	= (val << 1) & 0x7;
+
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_ob[mode][2]	|= (val >> 15) & 0x1;
+		ee->ee_db[mode][2]	= (val >> 12) & 0x7;
+		ee->ee_ob[mode][1]	= (val >> 9) & 0x7;
+		ee->ee_db[mode][1]	= (val >> 6) & 0x7;
+		ee->ee_ob[mode][0]	= (val >> 3) & 0x7;
+		ee->ee_db[mode][0]	= val & 0x7;
+		break;
+	case AR5K_EEPROM_MODE_11G:
+	case AR5K_EEPROM_MODE_11B:
+		ee->ee_ob[mode][1]	= (val >> 4) & 0x7;
+		ee->ee_db[mode][1]	= val & 0x7;
+		break;
+	}
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_tx_end2xlna_enable[mode]	= (val >> 8) & 0xff;
+	ee->ee_thr_62[mode]		= val & 0xff;
+
+	if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2)
+		ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_tx_end2xpa_disable[mode]	= (val >> 8) & 0xff;
+	ee->ee_tx_frm2xpa_enable[mode]	= val & 0xff;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_pga_desired_size[mode]	= (val >> 8) & 0xff;
+
+	if ((val & 0xff) & 0x80)
+		ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1);
+	else
+		ee->ee_noise_floor_thr[mode] = val & 0xff;
+
+	if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2)
+		ee->ee_noise_floor_thr[mode] =
+		    mode == AR5K_EEPROM_MODE_11A ? -54 : -1;
+
+	AR5K_EEPROM_READ(o++, val);
+	ee->ee_xlna_gain[mode]		= (val >> 5) & 0xff;
+	ee->ee_x_gain[mode]		= (val >> 1) & 0xf;
+	ee->ee_xpd[mode]		= val & 0x1;
+
+	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0)
+		ee->ee_fixed_bias[mode] = (val >> 13) & 0x1;
+
+	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) {
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_false_detect[mode] = (val >> 6) & 0x7f;
+
+		if (mode == AR5K_EEPROM_MODE_11A)
+			ee->ee_xr_power[mode] = val & 0x3f;
+		else {
+			ee->ee_ob[mode][0] = val & 0x7;
+			ee->ee_db[mode][0] = (val >> 3) & 0x7;
+		}
+	}
+
+	if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_4) {
+		ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN;
+		ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA;
+	} else {
+		ee->ee_i_gain[mode] = (val >> 13) & 0x7;
+
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_i_gain[mode] |= (val << 3) & 0x38;
+
+		if (mode == AR5K_EEPROM_MODE_11G) {
+			ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff;
+			if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6)
+				ee->ee_scaled_cck_delta = (val >> 11) & 0x1f;
+		}
+	}
+
+	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 &&
+			mode == AR5K_EEPROM_MODE_11A) {
+		ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
+		ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
+	}
+
+	if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0)
+		goto done;
+
+	/* Note: >= v5 have bg freq piers on another location
+	 * so these freq piers are ignored for >= v5 (should be 0xff
+	 * anyway) */
+	switch(mode) {
+	case AR5K_EEPROM_MODE_11A:
+		if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1)
+			break;
+
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_margin_tx_rx[mode] = val & 0x3f;
+		break;
+	case AR5K_EEPROM_MODE_11B:
+		AR5K_EEPROM_READ(o++, val);
+
+		ee->ee_pwr_cal_b[0].freq =
+			ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+		if (ee->ee_pwr_cal_b[0].freq != AR5K_EEPROM_CHANNEL_DIS)
+			ee->ee_n_piers[mode]++;
+
+		ee->ee_pwr_cal_b[1].freq =
+			ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+		if (ee->ee_pwr_cal_b[1].freq != AR5K_EEPROM_CHANNEL_DIS)
+			ee->ee_n_piers[mode]++;
+
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_pwr_cal_b[2].freq =
+			ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+		if (ee->ee_pwr_cal_b[2].freq != AR5K_EEPROM_CHANNEL_DIS)
+			ee->ee_n_piers[mode]++;
+
+		if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+			ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+		break;
+	case AR5K_EEPROM_MODE_11G:
+		AR5K_EEPROM_READ(o++, val);
+
+		ee->ee_pwr_cal_g[0].freq =
+			ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+		if (ee->ee_pwr_cal_g[0].freq != AR5K_EEPROM_CHANNEL_DIS)
+			ee->ee_n_piers[mode]++;
+
+		ee->ee_pwr_cal_g[1].freq =
+			ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+		if (ee->ee_pwr_cal_g[1].freq != AR5K_EEPROM_CHANNEL_DIS)
+			ee->ee_n_piers[mode]++;
+
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_turbo_max_power[mode] = val & 0x7f;
+		ee->ee_xr_power[mode] = (val >> 7) & 0x3f;
+
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_pwr_cal_g[2].freq =
+			ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+		if (ee->ee_pwr_cal_g[2].freq != AR5K_EEPROM_CHANNEL_DIS)
+			ee->ee_n_piers[mode]++;
+
+		if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+			ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
+		ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
+
+		if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) {
+			AR5K_EEPROM_READ(o++, val);
+			ee->ee_cck_ofdm_gain_delta = val & 0xff;
+		}
+		break;
+	}
+
+done:
+	/* return new offset */
+	*offset = o;
+
+	return 0;
+}
+
+/*
+ * Read turbo mode information on newer EEPROM versions
+ */
+static int
+ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah,
+			      u32 *offset, unsigned int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	u32 o = *offset;
+	u16 val;
+	int ret;
+
+	if (ee->ee_version < AR5K_EEPROM_VERSION_5_0)
+		return 0;
+
+	switch (mode){
+	case AR5K_EEPROM_MODE_11A:
+		ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f;
+
+		ee->ee_atn_tx_rx_turbo[mode] = (val >> 13) & 0x7;
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x7) << 3;
+		ee->ee_margin_tx_rx_turbo[mode] = (val >> 3) & 0x3f;
+
+		ee->ee_adc_desired_size_turbo[mode] = (val >> 9) & 0x7f;
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_adc_desired_size_turbo[mode] |= (val & 0x1) << 7;
+		ee->ee_pga_desired_size_turbo[mode] = (val >> 1) & 0xff;
+
+		if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >=2)
+			ee->ee_pd_gain_overlap = (val >> 9) & 0xf;
+		break;
+	case AR5K_EEPROM_MODE_11G:
+		ee->ee_switch_settling_turbo[mode] = (val >> 8) & 0x7f;
+
+		ee->ee_atn_tx_rx_turbo[mode] = (val >> 15) & 0x7;
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x1f) << 1;
+		ee->ee_margin_tx_rx_turbo[mode] = (val >> 5) & 0x3f;
+
+		ee->ee_adc_desired_size_turbo[mode] = (val >> 11) & 0x7f;
+		AR5K_EEPROM_READ(o++, val);
+		ee->ee_adc_desired_size_turbo[mode] |= (val & 0x7) << 5;
+		ee->ee_pga_desired_size_turbo[mode] = (val >> 3) & 0xff;
+		break;
+	}
+
+	/* return new offset */
+	*offset = o;
+
+	return 0;
+}
+
+/* Read mode-specific data (except power calibration data) */
+static int
+ath5k_eeprom_init_modes(struct ath5k_hw *ah)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	u32 mode_offset[3];
+	unsigned int mode;
+	u32 offset;
+	int ret;
+
+	/*
+	 * Get values for all modes
+	 */
+	mode_offset[AR5K_EEPROM_MODE_11A] = AR5K_EEPROM_MODES_11A(ah->ah_ee_version);
+	mode_offset[AR5K_EEPROM_MODE_11B] = AR5K_EEPROM_MODES_11B(ah->ah_ee_version);
+	mode_offset[AR5K_EEPROM_MODE_11G] = AR5K_EEPROM_MODES_11G(ah->ah_ee_version);
+
+	ee->ee_turbo_max_power[AR5K_EEPROM_MODE_11A] =
+		AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header);
+
+	for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) {
+		offset = mode_offset[mode];
+
+		ret = ath5k_eeprom_read_ants(ah, &offset, mode);
+		if (ret)
+			return ret;
+
+		ret = ath5k_eeprom_read_modes(ah, &offset, mode);
+		if (ret)
+			return ret;
+
+		ret = ath5k_eeprom_read_turbo_modes(ah, &offset, mode);
+		if (ret)
+			return ret;
+	}
+
+	/* override for older eeprom versions for better performance */
+	if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) {
+		ee->ee_thr_62[AR5K_EEPROM_MODE_11A] = 15;
+		ee->ee_thr_62[AR5K_EEPROM_MODE_11B] = 28;
+		ee->ee_thr_62[AR5K_EEPROM_MODE_11G] = 28;
+	}
+
+	return 0;
+}
+
+/* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff
+ * frequency mask) */
+static inline int
+ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
+			struct ath5k_chan_pcal_info *pc, unsigned int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	int o = *offset;
+	int i = 0;
+	u8 freq1, freq2;
+	int ret;
+	u16 val;
+
+	ee->ee_n_piers[mode] = 0;
+	while(i < max) {
+		AR5K_EEPROM_READ(o++, val);
+
+		freq1 = val & 0xff;
+		if (!freq1)
+			break;
+
+		pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+				freq1, mode);
+		ee->ee_n_piers[mode]++;
+
+		freq2 = (val >> 8) & 0xff;
+		if (!freq2)
+			break;
+
+		pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+				freq2, mode);
+		ee->ee_n_piers[mode]++;
+	}
+
+	/* return new offset */
+	*offset = o;
+
+	return 0;
+}
+
+/* Read frequency piers for 802.11a */
+static int
+ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info *pcal = ee->ee_pwr_cal_a;
+	int i, ret;
+	u16 val;
+	u8 mask;
+
+	if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) {
+		ath5k_eeprom_read_freq_list(ah, &offset,
+			AR5K_EEPROM_N_5GHZ_CHAN, pcal,
+			AR5K_EEPROM_MODE_11A);
+	} else {
+		mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version);
+
+		AR5K_EEPROM_READ(offset++, val);
+		pcal[0].freq  = (val >> 9) & mask;
+		pcal[1].freq  = (val >> 2) & mask;
+		pcal[2].freq  = (val << 5) & mask;
+
+		AR5K_EEPROM_READ(offset++, val);
+		pcal[2].freq |= (val >> 11) & 0x1f;
+		pcal[3].freq  = (val >> 4) & mask;
+		pcal[4].freq  = (val << 3) & mask;
+
+		AR5K_EEPROM_READ(offset++, val);
+		pcal[4].freq |= (val >> 13) & 0x7;
+		pcal[5].freq  = (val >> 6) & mask;
+		pcal[6].freq  = (val << 1) & mask;
+
+		AR5K_EEPROM_READ(offset++, val);
+		pcal[6].freq |= (val >> 15) & 0x1;
+		pcal[7].freq  = (val >> 8) & mask;
+		pcal[8].freq  = (val >> 1) & mask;
+		pcal[9].freq  = (val << 6) & mask;
+
+		AR5K_EEPROM_READ(offset++, val);
+		pcal[9].freq |= (val >> 10) & 0x3f;
+
+		/* Fixed number of piers */
+		ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10;
+
+		for (i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i++) {
+			pcal[i].freq = ath5k_eeprom_bin2freq(ee,
+				pcal[i].freq, AR5K_EEPROM_MODE_11A);
+		}
+	}
+
+	return 0;
+}
+
+/* Read frequency piers for 802.11bg on eeprom versions >= 5 and eemap >= 2 */
+static inline int
+ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info *pcal;
+
+	switch(mode) {
+	case AR5K_EEPROM_MODE_11B:
+		pcal = ee->ee_pwr_cal_b;
+		break;
+	case AR5K_EEPROM_MODE_11G:
+		pcal = ee->ee_pwr_cal_g;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ath5k_eeprom_read_freq_list(ah, &offset,
+		AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal,
+		mode);
+
+	return 0;
+}
+
+/*
+ * Read power calibration for RF5111 chips
+ *
+ * For RF5111 we have an XPD -eXternal Power Detector- curve
+ * for each calibrated channel. Each curve has 0,5dB Power steps
+ * on x axis and PCDAC steps (offsets) on y axis and looks like an
+ * exponential function. To recreate the curve we read 11 points
+ * here and interpolate later.
+ */
+
+/* Used to match PCDAC steps with power values on RF5111 chips
+ * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC
+ * steps that match with the power values we read from eeprom. On
+ * older eeprom versions (< 3.2) these steps are equaly spaced at
+ * 10% of the pcdac curve -until the curve reaches it's maximum-
+ * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
+ * these 11 steps are spaced in a different way. This function returns
+ * the pcdac steps based on eeprom version and curve min/max so that we
+ * can have pcdac/pwr points.
+ */
+static inline void
+ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
+{
+	static const u16 intercepts3[] =
+		{ 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
+	static const u16 intercepts3_2[] =
+		{ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
+	const u16 *ip;
+	unsigned i;
+
+	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
+		ip = intercepts3_2;
+	else
+		ip = intercepts3;
+
+	for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
+		vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100;
+}
+
+/* Convert RF5111 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode,
+				struct ath5k_chan_pcal_info *chinfo)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info_rf5111 *pcinfo;
+	struct ath5k_pdgain_info *pd;
+	u8 pier, point, idx;
+	u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+
+	/* Fill raw data for each calibration pier */
+	for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+		pcinfo = &chinfo[pier].rf5111_info;
+
+		/* Allocate pd_curves for this cal pier */
+		chinfo[pier].pd_curves =
+			calloc(AR5K_EEPROM_N_PD_CURVES,
+			       sizeof(struct ath5k_pdgain_info));
+
+		if (!chinfo[pier].pd_curves)
+			return -ENOMEM;
+
+		/* Only one curve for RF5111
+		 * find out which one and place
+		 * in in pd_curves.
+		 * Note: ee_x_gain is reversed here */
+		for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) {
+
+			if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) {
+				pdgain_idx[0] = idx;
+				break;
+			}
+		}
+
+		ee->ee_pd_gains[mode] = 1;
+
+		pd = &chinfo[pier].pd_curves[idx];
+
+		pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111;
+
+		/* Allocate pd points for this curve */
+		pd->pd_step = calloc(AR5K_EEPROM_N_PWR_POINTS_5111, sizeof(u8));
+		if (!pd->pd_step)
+			return -ENOMEM;
+
+		pd->pd_pwr = calloc(AR5K_EEPROM_N_PWR_POINTS_5111, sizeof(s16));
+		if (!pd->pd_pwr)
+			return -ENOMEM;
+
+		/* Fill raw dataset
+		 * (convert power to 0.25dB units
+		 * for RF5112 combatibility) */
+		for (point = 0; point < pd->pd_points; point++) {
+
+			/* Absolute values */
+			pd->pd_pwr[point] = 2 * pcinfo->pwr[point];
+
+			/* Already sorted */
+			pd->pd_step[point] = pcinfo->pcdac[point];
+		}
+
+		/* Set min/max pwr */
+		chinfo[pier].min_pwr = pd->pd_pwr[0];
+		chinfo[pier].max_pwr = pd->pd_pwr[10];
+
+	}
+
+	return 0;
+}
+
+/* Parse EEPROM data */
+static int
+ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info *pcal;
+	int offset, ret;
+	int i;
+	u16 val;
+
+	offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+	switch(mode) {
+	case AR5K_EEPROM_MODE_11A:
+		if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+			return 0;
+
+		ret = ath5k_eeprom_init_11a_pcal_freq(ah,
+			offset + AR5K_EEPROM_GROUP1_OFFSET);
+		if (ret < 0)
+			return ret;
+
+		offset += AR5K_EEPROM_GROUP2_OFFSET;
+		pcal = ee->ee_pwr_cal_a;
+		break;
+	case AR5K_EEPROM_MODE_11B:
+		if (!AR5K_EEPROM_HDR_11B(ee->ee_header) &&
+		    !AR5K_EEPROM_HDR_11G(ee->ee_header))
+			return 0;
+
+		pcal = ee->ee_pwr_cal_b;
+		offset += AR5K_EEPROM_GROUP3_OFFSET;
+
+		/* fixed piers */
+		pcal[0].freq = 2412;
+		pcal[1].freq = 2447;
+		pcal[2].freq = 2484;
+		ee->ee_n_piers[mode] = 3;
+		break;
+	case AR5K_EEPROM_MODE_11G:
+		if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+			return 0;
+
+		pcal = ee->ee_pwr_cal_g;
+		offset += AR5K_EEPROM_GROUP4_OFFSET;
+
+		/* fixed piers */
+		pcal[0].freq = 2312;
+		pcal[1].freq = 2412;
+		pcal[2].freq = 2484;
+		ee->ee_n_piers[mode] = 3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+		struct ath5k_chan_pcal_info_rf5111 *cdata =
+			&pcal[i].rf5111_info;
+
+		AR5K_EEPROM_READ(offset++, val);
+		cdata->pcdac_max = ((val >> 10) & AR5K_EEPROM_PCDAC_M);
+		cdata->pcdac_min = ((val >> 4) & AR5K_EEPROM_PCDAC_M);
+		cdata->pwr[0] = ((val << 2) & AR5K_EEPROM_POWER_M);
+
+		AR5K_EEPROM_READ(offset++, val);
+		cdata->pwr[0] |= ((val >> 14) & 0x3);
+		cdata->pwr[1] = ((val >> 8) & AR5K_EEPROM_POWER_M);
+		cdata->pwr[2] = ((val >> 2) & AR5K_EEPROM_POWER_M);
+		cdata->pwr[3] = ((val << 4) & AR5K_EEPROM_POWER_M);
+
+		AR5K_EEPROM_READ(offset++, val);
+		cdata->pwr[3] |= ((val >> 12) & 0xf);
+		cdata->pwr[4] = ((val >> 6) & AR5K_EEPROM_POWER_M);
+		cdata->pwr[5] = (val  & AR5K_EEPROM_POWER_M);
+
+		AR5K_EEPROM_READ(offset++, val);
+		cdata->pwr[6] = ((val >> 10) & AR5K_EEPROM_POWER_M);
+		cdata->pwr[7] = ((val >> 4) & AR5K_EEPROM_POWER_M);
+		cdata->pwr[8] = ((val << 2) & AR5K_EEPROM_POWER_M);
+
+		AR5K_EEPROM_READ(offset++, val);
+		cdata->pwr[8] |= ((val >> 14) & 0x3);
+		cdata->pwr[9] = ((val >> 8) & AR5K_EEPROM_POWER_M);
+		cdata->pwr[10] = ((val >> 2) & AR5K_EEPROM_POWER_M);
+
+		ath5k_get_pcdac_intercepts(ah, cdata->pcdac_min,
+			cdata->pcdac_max, cdata->pcdac);
+	}
+
+	return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal);
+}
+
+
+/*
+ * Read power calibration for RF5112 chips
+ *
+ * For RF5112 we have 4 XPD -eXternal Power Detector- curves
+ * for each calibrated channel on 0, -6, -12 and -18dbm but we only
+ * use the higher (3) and the lower (0) curves. Each curve has 0.5dB
+ * power steps on x axis and PCDAC steps on y axis and looks like a
+ * linear function. To recreate the curve and pass the power values
+ * on hw, we read 4 points for xpd 0 (lower gain -> max power)
+ * and 3 points for xpd 3 (higher gain -> lower power) here and
+ * interpolate later.
+ *
+ * Note: Many vendors just use xpd 0 so xpd 3 is zeroed.
+ */
+
+/* Convert RF5112 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode,
+				struct ath5k_chan_pcal_info *chinfo)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info_rf5112 *pcinfo;
+	u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+	unsigned int pier, pdg, point;
+
+	/* Fill raw data for each calibration pier */
+	for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+		pcinfo = &chinfo[pier].rf5112_info;
+
+		/* Allocate pd_curves for this cal pier */
+		chinfo[pier].pd_curves =
+				calloc(AR5K_EEPROM_N_PD_CURVES,
+				       sizeof(struct ath5k_pdgain_info));
+
+		if (!chinfo[pier].pd_curves)
+			return -ENOMEM;
+
+		/* Fill pd_curves */
+		for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+
+			u8 idx = pdgain_idx[pdg];
+			struct ath5k_pdgain_info *pd =
+					&chinfo[pier].pd_curves[idx];
+
+			/* Lowest gain curve (max power) */
+			if (pdg == 0) {
+				/* One more point for better accuracy */
+				pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS;
+
+				/* Allocate pd points for this curve */
+				pd->pd_step = calloc(pd->pd_points, sizeof(u8));
+
+				if (!pd->pd_step)
+					return -ENOMEM;
+
+				pd->pd_pwr = calloc(pd->pd_points, sizeof(s16));
+
+				if (!pd->pd_pwr)
+					return -ENOMEM;
+
+
+				/* Fill raw dataset
+				 * (all power levels are in 0.25dB units) */
+				pd->pd_step[0] = pcinfo->pcdac_x0[0];
+				pd->pd_pwr[0] = pcinfo->pwr_x0[0];
+
+				for (point = 1; point < pd->pd_points;
+				point++) {
+					/* Absolute values */
+					pd->pd_pwr[point] =
+						pcinfo->pwr_x0[point];
+
+					/* Deltas */
+					pd->pd_step[point] =
+						pd->pd_step[point - 1] +
+						pcinfo->pcdac_x0[point];
+				}
+
+				/* Set min power for this frequency */
+				chinfo[pier].min_pwr = pd->pd_pwr[0];
+
+			/* Highest gain curve (min power) */
+			} else if (pdg == 1) {
+
+				pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS;
+
+				/* Allocate pd points for this curve */
+				pd->pd_step = calloc(pd->pd_points, sizeof(u8));
+
+				if (!pd->pd_step)
+					return -ENOMEM;
+
+				pd->pd_pwr = calloc(pd->pd_points, sizeof(s16));
+
+				if (!pd->pd_pwr)
+					return -ENOMEM;
+
+				/* Fill raw dataset
+				 * (all power levels are in 0.25dB units) */
+				for (point = 0; point < pd->pd_points;
+				point++) {
+					/* Absolute values */
+					pd->pd_pwr[point] =
+						pcinfo->pwr_x3[point];
+
+					/* Fixed points */
+					pd->pd_step[point] =
+						pcinfo->pcdac_x3[point];
+				}
+
+				/* Since we have a higher gain curve
+				 * override min power */
+				chinfo[pier].min_pwr = pd->pd_pwr[0];
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* Parse EEPROM data */
+static int
+ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info;
+	struct ath5k_chan_pcal_info *gen_chan_info;
+	u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+	u32 offset;
+	u8 i, c;
+	u16 val;
+	int ret;
+	u8 pd_gains = 0;
+
+	/* Count how many curves we have and
+	 * identify them (which one of the 4
+	 * available curves we have on each count).
+	 * Curves are stored from lower (x0) to
+	 * higher (x3) gain */
+	for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) {
+		/* ee_x_gain[mode] is x gain mask */
+		if ((ee->ee_x_gain[mode] >> i) & 0x1)
+			pdgain_idx[pd_gains++] = i;
+	}
+	ee->ee_pd_gains[mode] = pd_gains;
+
+	if (pd_gains == 0 || pd_gains > 2)
+		return -EINVAL;
+
+	switch (mode) {
+	case AR5K_EEPROM_MODE_11A:
+		/*
+		 * Read 5GHz EEPROM channels
+		 */
+		offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+		ath5k_eeprom_init_11a_pcal_freq(ah, offset);
+
+		offset += AR5K_EEPROM_GROUP2_OFFSET;
+		gen_chan_info = ee->ee_pwr_cal_a;
+		break;
+	case AR5K_EEPROM_MODE_11B:
+		offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+		if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+			offset += AR5K_EEPROM_GROUP3_OFFSET;
+
+		/* NB: frequency piers parsed during mode init */
+		gen_chan_info = ee->ee_pwr_cal_b;
+		break;
+	case AR5K_EEPROM_MODE_11G:
+		offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+		if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+			offset += AR5K_EEPROM_GROUP4_OFFSET;
+		else if (AR5K_EEPROM_HDR_11B(ee->ee_header))
+			offset += AR5K_EEPROM_GROUP2_OFFSET;
+
+		/* NB: frequency piers parsed during mode init */
+		gen_chan_info = ee->ee_pwr_cal_g;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+		chan_pcal_info = &gen_chan_info[i].rf5112_info;
+
+		/* Power values in quarter dB
+		 * for the lower xpd gain curve
+		 * (0 dBm -> higher output power) */
+		for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
+			AR5K_EEPROM_READ(offset++, val);
+			chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff);
+			chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff);
+		}
+
+		/* PCDAC steps
+		 * corresponding to the above power
+		 * measurements */
+		AR5K_EEPROM_READ(offset++, val);
+		chan_pcal_info->pcdac_x0[1] = (val & 0x1f);
+		chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f);
+		chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f);
+
+		/* Power values in quarter dB
+		 * for the higher xpd gain curve
+		 * (18 dBm -> lower output power) */
+		AR5K_EEPROM_READ(offset++, val);
+		chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff);
+		chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff);
+
+		AR5K_EEPROM_READ(offset++, val);
+		chan_pcal_info->pwr_x3[2] = (val & 0xff);
+
+		/* PCDAC steps
+		 * corresponding to the above power
+		 * measurements (fixed) */
+		chan_pcal_info->pcdac_x3[0] = 20;
+		chan_pcal_info->pcdac_x3[1] = 35;
+		chan_pcal_info->pcdac_x3[2] = 63;
+
+		if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) {
+			chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f);
+
+			/* Last xpd0 power level is also channel maximum */
+			gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3];
+		} else {
+			chan_pcal_info->pcdac_x0[0] = 1;
+			gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff);
+		}
+
+	}
+
+	return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info);
+}
+
+
+/*
+ * Read power calibration for RF2413 chips
+ *
+ * For RF2413 we have a Power to PDDAC table (Power Detector)
+ * instead of a PCDAC and 4 pd gain curves for each calibrated channel.
+ * Each curve has power on x axis in 0.5 db steps and PDDADC steps on y
+ * axis and looks like an exponential function like the RF5111 curve.
+ *
+ * To recreate the curves we read here the points and interpolate
+ * later. Note that in most cases only 2 (higher and lower) curves are
+ * used (like RF5112) but vendors have the oportunity to include all
+ * 4 curves on eeprom. The final curve (higher power) has an extra
+ * point for better accuracy like RF5112.
+ */
+
+/* For RF2413 power calibration data doesn't start on a fixed location and
+ * if a mode is not supported, it's section is missing -not zeroed-.
+ * So we need to calculate the starting offset for each section by using
+ * these two functions */
+
+/* Return the size of each section based on the mode and the number of pd
+ * gains available (maximum 4). */
+static inline unsigned int
+ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode)
+{
+	static const unsigned int pdgains_size[] = { 4, 6, 9, 12 };
+	unsigned int sz;
+
+	sz = pdgains_size[ee->ee_pd_gains[mode] - 1];
+	sz *= ee->ee_n_piers[mode];
+
+	return sz;
+}
+
+/* Return the starting offset for a section based on the modes supported
+ * and each section's size. */
+static unsigned int
+ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
+{
+	u32 offset = AR5K_EEPROM_CAL_DATA_START(ee->ee_misc4);
+
+	switch(mode) {
+	case AR5K_EEPROM_MODE_11G:
+		if (AR5K_EEPROM_HDR_11B(ee->ee_header))
+			offset += ath5k_pdgains_size_2413(ee,
+					AR5K_EEPROM_MODE_11B) +
+					AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+		/* fall through */
+	case AR5K_EEPROM_MODE_11B:
+		if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+			offset += ath5k_pdgains_size_2413(ee,
+					AR5K_EEPROM_MODE_11A) +
+					AR5K_EEPROM_N_5GHZ_CHAN / 2;
+		/* fall through */
+	case AR5K_EEPROM_MODE_11A:
+		break;
+	default:
+		break;
+	}
+
+	return offset;
+}
+
+/* Convert RF2413 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode,
+				struct ath5k_chan_pcal_info *chinfo)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+	u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+	unsigned int pier, point;
+	int pdg;
+
+	/* Fill raw data for each calibration pier */
+	for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+		pcinfo = &chinfo[pier].rf2413_info;
+
+		/* Allocate pd_curves for this cal pier */
+		chinfo[pier].pd_curves =
+				calloc(AR5K_EEPROM_N_PD_CURVES,
+				       sizeof(struct ath5k_pdgain_info));
+
+		if (!chinfo[pier].pd_curves)
+			return -ENOMEM;
+
+		/* Fill pd_curves */
+		for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+
+			u8 idx = pdgain_idx[pdg];
+			struct ath5k_pdgain_info *pd =
+					&chinfo[pier].pd_curves[idx];
+
+			/* One more point for the highest power
+			 * curve (lowest gain) */
+			if (pdg == ee->ee_pd_gains[mode] - 1)
+				pd->pd_points = AR5K_EEPROM_N_PD_POINTS;
+			else
+				pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1;
+
+			/* Allocate pd points for this curve */
+			pd->pd_step = calloc(pd->pd_points, sizeof(u8));
+
+			if (!pd->pd_step)
+				return -ENOMEM;
+
+			pd->pd_pwr = calloc(pd->pd_points, sizeof(s16));
+
+			if (!pd->pd_pwr)
+				return -ENOMEM;
+
+			/* Fill raw dataset
+			 * convert all pwr levels to
+			 * quarter dB for RF5112 combatibility */
+			pd->pd_step[0] = pcinfo->pddac_i[pdg];
+			pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg];
+
+			for (point = 1; point < pd->pd_points; point++) {
+
+				pd->pd_pwr[point] = pd->pd_pwr[point - 1] +
+					2 * pcinfo->pwr[pdg][point - 1];
+
+				pd->pd_step[point] = pd->pd_step[point - 1] +
+						pcinfo->pddac[pdg][point - 1];
+
+			}
+
+			/* Highest gain curve -> min power */
+			if (pdg == 0)
+				chinfo[pier].min_pwr = pd->pd_pwr[0];
+
+			/* Lowest gain curve -> max power */
+			if (pdg == ee->ee_pd_gains[mode] - 1)
+				chinfo[pier].max_pwr =
+					pd->pd_pwr[pd->pd_points - 1];
+		}
+	}
+
+	return 0;
+}
+
+/* Parse EEPROM data */
+static int
+ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+	struct ath5k_chan_pcal_info *chinfo;
+	u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+	u32 offset;
+	int idx, i, ret;
+	u16 val;
+	u8 pd_gains = 0;
+
+	/* Count how many curves we have and
+	 * identify them (which one of the 4
+	 * available curves we have on each count).
+	 * Curves are stored from higher to
+	 * lower gain so we go backwards */
+	for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) {
+		/* ee_x_gain[mode] is x gain mask */
+		if ((ee->ee_x_gain[mode] >> idx) & 0x1)
+			pdgain_idx[pd_gains++] = idx;
+
+	}
+	ee->ee_pd_gains[mode] = pd_gains;
+
+	if (pd_gains == 0)
+		return -EINVAL;
+
+	offset = ath5k_cal_data_offset_2413(ee, mode);
+	switch (mode) {
+	case AR5K_EEPROM_MODE_11A:
+		if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+			return 0;
+
+		ath5k_eeprom_init_11a_pcal_freq(ah, offset);
+		offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
+		chinfo = ee->ee_pwr_cal_a;
+		break;
+	case AR5K_EEPROM_MODE_11B:
+		if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
+			return 0;
+
+		ath5k_eeprom_init_11bg_2413(ah, mode, offset);
+		offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+		chinfo = ee->ee_pwr_cal_b;
+		break;
+	case AR5K_EEPROM_MODE_11G:
+		if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+			return 0;
+
+		ath5k_eeprom_init_11bg_2413(ah, mode, offset);
+		offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+		chinfo = ee->ee_pwr_cal_g;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+		pcinfo = &chinfo[i].rf2413_info;
+
+		/*
+		 * Read pwr_i, pddac_i and the first
+		 * 2 pd points (pwr, pddac)
+		 */
+		AR5K_EEPROM_READ(offset++, val);
+		pcinfo->pwr_i[0] = val & 0x1f;
+		pcinfo->pddac_i[0] = (val >> 5) & 0x7f;
+		pcinfo->pwr[0][0] = (val >> 12) & 0xf;
+
+		AR5K_EEPROM_READ(offset++, val);
+		pcinfo->pddac[0][0] = val & 0x3f;
+		pcinfo->pwr[0][1] = (val >> 6) & 0xf;
+		pcinfo->pddac[0][1] = (val >> 10) & 0x3f;
+
+		AR5K_EEPROM_READ(offset++, val);
+		pcinfo->pwr[0][2] = val & 0xf;
+		pcinfo->pddac[0][2] = (val >> 4) & 0x3f;
+
+		pcinfo->pwr[0][3] = 0;
+		pcinfo->pddac[0][3] = 0;
+
+		if (pd_gains > 1) {
+			/*
+			 * Pd gain 0 is not the last pd gain
+			 * so it only has 2 pd points.
+			 * Continue wih pd gain 1.
+			 */
+			pcinfo->pwr_i[1] = (val >> 10) & 0x1f;
+
+			pcinfo->pddac_i[1] = (val >> 15) & 0x1;
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pddac_i[1] |= (val & 0x3F) << 1;
+
+			pcinfo->pwr[1][0] = (val >> 6) & 0xf;
+			pcinfo->pddac[1][0] = (val >> 10) & 0x3f;
+
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pwr[1][1] = val & 0xf;
+			pcinfo->pddac[1][1] = (val >> 4) & 0x3f;
+			pcinfo->pwr[1][2] = (val >> 10) & 0xf;
+
+			pcinfo->pddac[1][2] = (val >> 14) & 0x3;
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pddac[1][2] |= (val & 0xF) << 2;
+
+			pcinfo->pwr[1][3] = 0;
+			pcinfo->pddac[1][3] = 0;
+		} else if (pd_gains == 1) {
+			/*
+			 * Pd gain 0 is the last one so
+			 * read the extra point.
+			 */
+			pcinfo->pwr[0][3] = (val >> 10) & 0xf;
+
+			pcinfo->pddac[0][3] = (val >> 14) & 0x3;
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pddac[0][3] |= (val & 0xF) << 2;
+		}
+
+		/*
+		 * Proceed with the other pd_gains
+		 * as above.
+		 */
+		if (pd_gains > 2) {
+			pcinfo->pwr_i[2] = (val >> 4) & 0x1f;
+			pcinfo->pddac_i[2] = (val >> 9) & 0x7f;
+
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pwr[2][0] = (val >> 0) & 0xf;
+			pcinfo->pddac[2][0] = (val >> 4) & 0x3f;
+			pcinfo->pwr[2][1] = (val >> 10) & 0xf;
+
+			pcinfo->pddac[2][1] = (val >> 14) & 0x3;
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pddac[2][1] |= (val & 0xF) << 2;
+
+			pcinfo->pwr[2][2] = (val >> 4) & 0xf;
+			pcinfo->pddac[2][2] = (val >> 8) & 0x3f;
+
+			pcinfo->pwr[2][3] = 0;
+			pcinfo->pddac[2][3] = 0;
+		} else if (pd_gains == 2) {
+			pcinfo->pwr[1][3] = (val >> 4) & 0xf;
+			pcinfo->pddac[1][3] = (val >> 8) & 0x3f;
+		}
+
+		if (pd_gains > 3) {
+			pcinfo->pwr_i[3] = (val >> 14) & 0x3;
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
+
+			pcinfo->pddac_i[3] = (val >> 3) & 0x7f;
+			pcinfo->pwr[3][0] = (val >> 10) & 0xf;
+			pcinfo->pddac[3][0] = (val >> 14) & 0x3;
+
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pddac[3][0] |= (val & 0xF) << 2;
+			pcinfo->pwr[3][1] = (val >> 4) & 0xf;
+			pcinfo->pddac[3][1] = (val >> 8) & 0x3f;
+
+			pcinfo->pwr[3][2] = (val >> 14) & 0x3;
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2;
+
+			pcinfo->pddac[3][2] = (val >> 2) & 0x3f;
+			pcinfo->pwr[3][3] = (val >> 8) & 0xf;
+
+			pcinfo->pddac[3][3] = (val >> 12) & 0xF;
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4;
+		} else if (pd_gains == 3) {
+			pcinfo->pwr[2][3] = (val >> 14) & 0x3;
+			AR5K_EEPROM_READ(offset++, val);
+			pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2;
+
+			pcinfo->pddac[2][3] = (val >> 2) & 0x3f;
+		}
+	}
+
+	return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo);
+}
+
+
+/*
+ * Read per rate target power (this is the maximum tx power
+ * supported by the card). This info is used when setting
+ * tx power, no matter the channel.
+ *
+ * This also works for v5 EEPROMs.
+ */
+static int
+ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_rate_pcal_info *rate_pcal_info;
+	u8 *rate_target_pwr_num;
+	u32 offset;
+	u16 val;
+	int ret, i;
+
+	offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1);
+	rate_target_pwr_num = &ee->ee_rate_target_pwr_num[mode];
+	switch (mode) {
+	case AR5K_EEPROM_MODE_11A:
+		offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version);
+		rate_pcal_info = ee->ee_rate_tpwr_a;
+		ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_CHAN;
+		break;
+	case AR5K_EEPROM_MODE_11B:
+		offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version);
+		rate_pcal_info = ee->ee_rate_tpwr_b;
+		ee->ee_rate_target_pwr_num[mode] = 2; /* 3rd is g mode's 1st */
+		break;
+	case AR5K_EEPROM_MODE_11G:
+		offset += AR5K_EEPROM_TARGET_PWR_OFF_11G(ee->ee_version);
+		rate_pcal_info = ee->ee_rate_tpwr_g;
+		ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_2GHZ_CHAN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Different freq mask for older eeproms (<= v3.2) */
+	if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2) {
+		for (i = 0; i < (*rate_target_pwr_num); i++) {
+			AR5K_EEPROM_READ(offset++, val);
+			rate_pcal_info[i].freq =
+			    ath5k_eeprom_bin2freq(ee, (val >> 9) & 0x7f, mode);
+
+			rate_pcal_info[i].target_power_6to24 = ((val >> 3) & 0x3f);
+			rate_pcal_info[i].target_power_36 = (val << 3) & 0x3f;
+
+			AR5K_EEPROM_READ(offset++, val);
+
+			if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS ||
+			    val == 0) {
+				(*rate_target_pwr_num) = i;
+				break;
+			}
+
+			rate_pcal_info[i].target_power_36 |= ((val >> 13) & 0x7);
+			rate_pcal_info[i].target_power_48 = ((val >> 7) & 0x3f);
+			rate_pcal_info[i].target_power_54 = ((val >> 1) & 0x3f);
+		}
+	} else {
+		for (i = 0; i < (*rate_target_pwr_num); i++) {
+			AR5K_EEPROM_READ(offset++, val);
+			rate_pcal_info[i].freq =
+			    ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+
+			rate_pcal_info[i].target_power_6to24 = ((val >> 2) & 0x3f);
+			rate_pcal_info[i].target_power_36 = (val << 4) & 0x3f;
+
+			AR5K_EEPROM_READ(offset++, val);
+
+			if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS ||
+			    val == 0) {
+				(*rate_target_pwr_num) = i;
+				break;
+			}
+
+			rate_pcal_info[i].target_power_36 |= (val >> 12) & 0xf;
+			rate_pcal_info[i].target_power_48 = ((val >> 6) & 0x3f);
+			rate_pcal_info[i].target_power_54 = (val & 0x3f);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Read per channel calibration info from EEPROM
+ *
+ * This info is used to calibrate the baseband power table. Imagine
+ * that for each channel there is a power curve that's hw specific
+ * (depends on amplifier etc) and we try to "correct" this curve using
+ * offests we pass on to phy chip (baseband -> before amplifier) so that
+ * it can use accurate power values when setting tx power (takes amplifier's
+ * performance on each channel into account).
+ *
+ * EEPROM provides us with the offsets for some pre-calibrated channels
+ * and we have to interpolate to create the full table for these channels and
+ * also the table for any channel.
+ */
+static int
+ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	int (*read_pcal)(struct ath5k_hw *hw, int mode);
+	int mode;
+	int err;
+
+	if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) &&
+			(AR5K_EEPROM_EEMAP(ee->ee_misc0) == 1))
+		read_pcal = ath5k_eeprom_read_pcal_info_5112;
+	else if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0) &&
+			(AR5K_EEPROM_EEMAP(ee->ee_misc0) == 2))
+		read_pcal = ath5k_eeprom_read_pcal_info_2413;
+	else
+		read_pcal = ath5k_eeprom_read_pcal_info_5111;
+
+
+	for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G;
+	mode++) {
+		err = read_pcal(ah, mode);
+		if (err)
+			return err;
+
+		err = ath5k_eeprom_read_target_rate_pwr_info(ah, mode);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int
+ath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info *chinfo;
+	u8 pier, pdg;
+
+	switch (mode) {
+	case AR5K_EEPROM_MODE_11A:
+		if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+			return 0;
+		chinfo = ee->ee_pwr_cal_a;
+		break;
+	case AR5K_EEPROM_MODE_11B:
+		if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
+			return 0;
+		chinfo = ee->ee_pwr_cal_b;
+		break;
+	case AR5K_EEPROM_MODE_11G:
+		if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+			return 0;
+		chinfo = ee->ee_pwr_cal_g;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+		if (!chinfo[pier].pd_curves)
+			continue;
+
+		for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+			struct ath5k_pdgain_info *pd =
+					&chinfo[pier].pd_curves[pdg];
+
+			if (pd != NULL) {
+				free(pd->pd_step);
+				free(pd->pd_pwr);
+			}
+		}
+
+		free(chinfo[pier].pd_curves);
+	}
+
+	return 0;
+}
+
+void
+ath5k_eeprom_detach(struct ath5k_hw *ah)
+{
+	u8 mode;
+
+	for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++)
+		ath5k_eeprom_free_pcal_info(ah, mode);
+}
+
+/* Read conformance test limits used for regulatory control */
+static int
+ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_edge_power *rep;
+	unsigned int fmask, pmask;
+	unsigned int ctl_mode;
+	int ret, i, j;
+	u32 offset;
+	u16 val;
+
+	pmask = AR5K_EEPROM_POWER_M;
+	fmask = AR5K_EEPROM_FREQ_M(ee->ee_version);
+	offset = AR5K_EEPROM_CTL(ee->ee_version);
+	ee->ee_ctls = AR5K_EEPROM_N_CTLS(ee->ee_version);
+	for (i = 0; i < ee->ee_ctls; i += 2) {
+		AR5K_EEPROM_READ(offset++, val);
+		ee->ee_ctl[i] = (val >> 8) & 0xff;
+		ee->ee_ctl[i + 1] = val & 0xff;
+	}
+
+	offset = AR5K_EEPROM_GROUP8_OFFSET;
+	if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0)
+		offset += AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) -
+			AR5K_EEPROM_GROUP5_OFFSET;
+	else
+		offset += AR5K_EEPROM_GROUPS_START(ee->ee_version);
+
+	rep = ee->ee_ctl_pwr;
+	for(i = 0; i < ee->ee_ctls; i++) {
+		switch(ee->ee_ctl[i] & AR5K_CTL_MODE_M) {
+		case AR5K_CTL_11A:
+		case AR5K_CTL_TURBO:
+			ctl_mode = AR5K_EEPROM_MODE_11A;
+			break;
+		default:
+			ctl_mode = AR5K_EEPROM_MODE_11G;
+			break;
+		}
+		if (ee->ee_ctl[i] == 0) {
+			if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3)
+				offset += 8;
+			else
+				offset += 7;
+			rep += AR5K_EEPROM_N_EDGES;
+			continue;
+		}
+		if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) {
+			for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) {
+				AR5K_EEPROM_READ(offset++, val);
+				rep[j].freq = (val >> 8) & fmask;
+				rep[j + 1].freq = val & fmask;
+			}
+			for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) {
+				AR5K_EEPROM_READ(offset++, val);
+				rep[j].edge = (val >> 8) & pmask;
+				rep[j].flag = (val >> 14) & 1;
+				rep[j + 1].edge = val & pmask;
+				rep[j + 1].flag = (val >> 6) & 1;
+			}
+		} else {
+			AR5K_EEPROM_READ(offset++, val);
+			rep[0].freq = (val >> 9) & fmask;
+			rep[1].freq = (val >> 2) & fmask;
+			rep[2].freq = (val << 5) & fmask;
+
+			AR5K_EEPROM_READ(offset++, val);
+			rep[2].freq |= (val >> 11) & 0x1f;
+			rep[3].freq = (val >> 4) & fmask;
+			rep[4].freq = (val << 3) & fmask;
+
+			AR5K_EEPROM_READ(offset++, val);
+			rep[4].freq |= (val >> 13) & 0x7;
+			rep[5].freq = (val >> 6) & fmask;
+			rep[6].freq = (val << 1) & fmask;
+
+			AR5K_EEPROM_READ(offset++, val);
+			rep[6].freq |= (val >> 15) & 0x1;
+			rep[7].freq = (val >> 8) & fmask;
+
+			rep[0].edge = (val >> 2) & pmask;
+			rep[1].edge = (val << 4) & pmask;
+
+			AR5K_EEPROM_READ(offset++, val);
+			rep[1].edge |= (val >> 12) & 0xf;
+			rep[2].edge = (val >> 6) & pmask;
+			rep[3].edge = val & pmask;
+
+			AR5K_EEPROM_READ(offset++, val);
+			rep[4].edge = (val >> 10) & pmask;
+			rep[5].edge = (val >> 4) & pmask;
+			rep[6].edge = (val << 2) & pmask;
+
+			AR5K_EEPROM_READ(offset++, val);
+			rep[6].edge |= (val >> 14) & 0x3;
+			rep[7].edge = (val >> 8) & pmask;
+		}
+		for (j = 0; j < AR5K_EEPROM_N_EDGES; j++) {
+			rep[j].freq = ath5k_eeprom_bin2freq(ee,
+				rep[j].freq, ctl_mode);
+		}
+		rep += AR5K_EEPROM_N_EDGES;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Initialize eeprom power tables
+ */
+int
+ath5k_eeprom_init(struct ath5k_hw *ah)
+{
+	int err;
+
+	err = ath5k_eeprom_init_header(ah);
+	if (err < 0)
+		return err;
+
+	err = ath5k_eeprom_init_modes(ah);
+	if (err < 0)
+		return err;
+
+	err = ath5k_eeprom_read_pcal_info(ah);
+	if (err < 0)
+		return err;
+
+	err = ath5k_eeprom_read_ctl_info(ah);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+/*
+ * Read the MAC address from eeprom
+ */
+int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
+{
+	u8 mac_d[ETH_ALEN] = {};
+	u32 total, offset;
+	u16 data;
+	int octet, ret;
+
+	ret = ath5k_hw_eeprom_read(ah, 0x20, &data);
+	if (ret)
+		return ret;
+
+	for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
+		ret = ath5k_hw_eeprom_read(ah, offset, &data);
+		if (ret)
+			return ret;
+
+		total += data;
+		mac_d[octet + 1] = data & 0xff;
+		mac_d[octet] = data >> 8;
+		octet += 2;
+	}
+
+	if (!total || total == 3 * 0xffff)
+		return -EINVAL;
+
+	memcpy(mac, mac_d, ETH_ALEN);
+
+	return 0;
+}
+
+int ath5k_eeprom_is_hb63(struct ath5k_hw *ah)
+{
+	u16 data;
+
+	ath5k_hw_eeprom_read(ah, AR5K_EEPROM_IS_HB63, &data);
+
+	if ((ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4)) && data)
+		return 1;
+	else
+		return 0;
+}
+
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_gpio.c b/gpxe/src/drivers/net/ath5k/ath5k_gpio.c
new file mode 100644
index 0000000..0e8a3e6
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_gpio.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+/****************\
+  GPIO Functions
+\****************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/*
+ * Set GPIO inputs
+ */
+int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio)
+{
+	if (gpio >= AR5K_NUM_GPIO)
+		return -EINVAL;
+
+	ath5k_hw_reg_write(ah,
+		(ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
+		| AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR);
+
+	return 0;
+}
+
+/*
+ * Set GPIO outputs
+ */
+int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio)
+{
+	if (gpio >= AR5K_NUM_GPIO)
+		return -EINVAL;
+
+	ath5k_hw_reg_write(ah,
+		(ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
+		| AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR);
+
+	return 0;
+}
+
+/*
+ * Get GPIO state
+ */
+u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio)
+{
+	if (gpio >= AR5K_NUM_GPIO)
+		return 0xffffffff;
+
+	/* GPIO input magic */
+	return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) &
+		0x1;
+}
+
+/*
+ * Set GPIO state
+ */
+int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val)
+{
+	u32 data;
+
+	if (gpio >= AR5K_NUM_GPIO)
+		return -EINVAL;
+
+	/* GPIO output magic */
+	data = ath5k_hw_reg_read(ah, AR5K_GPIODO);
+
+	data &= ~(1 << gpio);
+	data |= (val & 1) << gpio;
+
+	ath5k_hw_reg_write(ah, data, AR5K_GPIODO);
+
+	return 0;
+}
+
+/*
+ * Initialize the GPIO interrupt (RFKill switch)
+ */
+void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio,
+		u32 interrupt_level)
+{
+	u32 data;
+
+	if (gpio >= AR5K_NUM_GPIO)
+		return;
+
+	/*
+	 * Set the GPIO interrupt
+	 */
+	data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &
+		~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH |
+		AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) |
+		(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA);
+
+	ath5k_hw_reg_write(ah, interrupt_level ? data :
+		(data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR);
+
+	ah->ah_imr |= AR5K_IMR_GPIO;
+
+	/* Enable GPIO interrupts */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO);
+}
+
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_initvals.c b/gpxe/src/drivers/net/ath5k/ath5k_initvals.c
new file mode 100644
index 0000000..92011c8
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_initvals.c
@@ -0,0 +1,1560 @@
+/*
+ * Initial register settings functions
+ *
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm at gmail.com>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby at gmail.com>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+#include <unistd.h>
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/*
+ * Mode-independent initial register writes
+ */
+
+struct ath5k_ini {
+	u16	ini_register;
+	u32	ini_value;
+
+	enum {
+		AR5K_INI_WRITE = 0,	/* Default */
+		AR5K_INI_READ = 1,	/* Cleared on read */
+	} ini_mode;
+};
+
+/*
+ * Mode specific initial register values
+ */
+
+struct ath5k_ini_mode {
+	u16	mode_register;
+	u32	mode_value[5];
+};
+
+/* Initial register settings for AR5210 */
+static const struct ath5k_ini ar5210_ini[] = {
+	/* PCU and MAC registers */
+	{ AR5K_NOQCU_TXDP0,	0, AR5K_INI_WRITE },
+	{ AR5K_NOQCU_TXDP1,	0, AR5K_INI_WRITE },
+	{ AR5K_RXDP,		0, AR5K_INI_WRITE },
+	{ AR5K_CR,		0, AR5K_INI_WRITE },
+	{ AR5K_ISR,		0, AR5K_INI_READ },
+	{ AR5K_IMR,		0, AR5K_INI_WRITE },
+	{ AR5K_IER,		AR5K_IER_DISABLE, AR5K_INI_WRITE },
+	{ AR5K_BSR,		0, AR5K_INI_READ },
+	{ AR5K_TXCFG,		AR5K_DMASIZE_128B, AR5K_INI_WRITE },
+	{ AR5K_RXCFG,		AR5K_DMASIZE_128B, AR5K_INI_WRITE },
+	{ AR5K_CFG,		AR5K_INIT_CFG, AR5K_INI_WRITE },
+	{ AR5K_TOPS,		8, AR5K_INI_WRITE },
+	{ AR5K_RXNOFRM,		8, AR5K_INI_WRITE },
+	{ AR5K_RPGTO,		0, AR5K_INI_WRITE },
+	{ AR5K_TXNOFRM,		0, AR5K_INI_WRITE },
+	{ AR5K_SFR,		0, AR5K_INI_WRITE },
+	{ AR5K_MIBC,		0, AR5K_INI_WRITE },
+	{ AR5K_MISC,		0, AR5K_INI_WRITE },
+	{ AR5K_RX_FILTER_5210,	0, AR5K_INI_WRITE },
+	{ AR5K_MCAST_FILTER0_5210, 0, AR5K_INI_WRITE },
+	{ AR5K_MCAST_FILTER1_5210, 0, AR5K_INI_WRITE },
+	{ AR5K_TX_MASK0,	0, AR5K_INI_WRITE },
+	{ AR5K_TX_MASK1,	0, AR5K_INI_WRITE },
+	{ AR5K_CLR_TMASK,	0, AR5K_INI_WRITE },
+	{ AR5K_TRIG_LVL,	AR5K_TUNE_MIN_TX_FIFO_THRES, AR5K_INI_WRITE },
+	{ AR5K_DIAG_SW_5210,	0, AR5K_INI_WRITE },
+	{ AR5K_RSSI_THR,	AR5K_TUNE_RSSI_THRES, AR5K_INI_WRITE },
+	{ AR5K_TSF_L32_5210,	0, AR5K_INI_WRITE },
+	{ AR5K_TIMER0_5210,	0, AR5K_INI_WRITE },
+	{ AR5K_TIMER1_5210,	0xffffffff, AR5K_INI_WRITE },
+	{ AR5K_TIMER2_5210,	0xffffffff, AR5K_INI_WRITE },
+	{ AR5K_TIMER3_5210,	1, AR5K_INI_WRITE },
+	{ AR5K_CFP_DUR_5210,	0, AR5K_INI_WRITE },
+	{ AR5K_CFP_PERIOD_5210,	0, AR5K_INI_WRITE },
+	/* PHY registers */
+	{ AR5K_PHY(0),	0x00000047, AR5K_INI_WRITE },
+	{ AR5K_PHY_AGC,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(3),	0x09848ea6, AR5K_INI_WRITE },
+	{ AR5K_PHY(4),	0x3d32e000, AR5K_INI_WRITE },
+	{ AR5K_PHY(5),	0x0000076b, AR5K_INI_WRITE },
+	{ AR5K_PHY_ACT,	AR5K_PHY_ACT_DISABLE, AR5K_INI_WRITE },
+	{ AR5K_PHY(8),	0x02020200, AR5K_INI_WRITE },
+	{ AR5K_PHY(9),	0x00000e0e, AR5K_INI_WRITE },
+	{ AR5K_PHY(10),	0x0a020201, AR5K_INI_WRITE },
+	{ AR5K_PHY(11),	0x00036ffc, AR5K_INI_WRITE },
+	{ AR5K_PHY(12),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(13),	0x00000e0e, AR5K_INI_WRITE },
+	{ AR5K_PHY(14),	0x00000007, AR5K_INI_WRITE },
+	{ AR5K_PHY(15),	0x00020100, AR5K_INI_WRITE },
+	{ AR5K_PHY(16),	0x89630000, AR5K_INI_WRITE },
+	{ AR5K_PHY(17),	0x1372169c, AR5K_INI_WRITE },
+	{ AR5K_PHY(18),	0x0018b633, AR5K_INI_WRITE },
+	{ AR5K_PHY(19),	0x1284613c, AR5K_INI_WRITE },
+	{ AR5K_PHY(20),	0x0de8b8e0, AR5K_INI_WRITE },
+	{ AR5K_PHY(21),	0x00074859, AR5K_INI_WRITE },
+	{ AR5K_PHY(22),	0x7e80beba, AR5K_INI_WRITE },
+	{ AR5K_PHY(23),	0x313a665e, AR5K_INI_WRITE },
+	{ AR5K_PHY_AGCCTL, 0x00001d08, AR5K_INI_WRITE },
+	{ AR5K_PHY(25),	0x0001ce00, AR5K_INI_WRITE },
+	{ AR5K_PHY(26),	0x409a4190, AR5K_INI_WRITE },
+	{ AR5K_PHY(28),	0x0000000f, AR5K_INI_WRITE },
+	{ AR5K_PHY(29),	0x00000080, AR5K_INI_WRITE },
+	{ AR5K_PHY(30),	0x00000004, AR5K_INI_WRITE },
+	{ AR5K_PHY(31),	0x00000018, AR5K_INI_WRITE }, 	/* 0x987c */
+	{ AR5K_PHY(64),	0x00000000, AR5K_INI_WRITE }, 	/* 0x9900 */
+	{ AR5K_PHY(65),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(66),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(67),	0x00800000, AR5K_INI_WRITE },
+	{ AR5K_PHY(68),	0x00000003, AR5K_INI_WRITE },
+	/* BB gain table (64bytes) */
+	{ AR5K_BB_GAIN(0), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(1), 0x00000020, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(2), 0x00000010, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(3), 0x00000030, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(4), 0x00000008, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(5), 0x00000028, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(6), 0x00000028, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(7), 0x00000004, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(8), 0x00000024, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(9), 0x00000014, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(10), 0x00000034, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(11), 0x0000000c, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(12), 0x0000002c, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(13), 0x00000002, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(14), 0x00000022, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(15), 0x00000012, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(16), 0x00000032, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(17), 0x0000000a, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(18), 0x0000002a, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(19), 0x00000001, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(20), 0x00000021, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(21), 0x00000011, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(22), 0x00000031, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(23), 0x00000009, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(24), 0x00000029, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(25), 0x00000005, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(26), 0x00000025, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(27), 0x00000015, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(28), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(29), 0x0000000d, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(30), 0x0000002d, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(31), 0x00000003, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(32), 0x00000023, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(33), 0x00000013, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(34), 0x00000033, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(35), 0x0000000b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(36), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(37), 0x00000007, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(38), 0x00000027, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(39), 0x00000017, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(40), 0x00000037, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(41), 0x0000000f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(42), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(43), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(44), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(45), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(46), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(47), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(48), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(49), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(50), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(51), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(52), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(53), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(54), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(55), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(56), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(57), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(58), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(59), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(60), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(61), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(62), 0x0000002f, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(63), 0x0000002f, AR5K_INI_WRITE },
+	/* 5110 RF gain table (64btes) */
+	{ AR5K_RF_GAIN(0), 0x0000001d, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(1), 0x0000005d, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(2), 0x0000009d, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(3), 0x000000dd, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(4), 0x0000011d, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(5), 0x00000021, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(6), 0x00000061, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(7), 0x000000a1, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(8), 0x000000e1, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(9), 0x00000031, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(10), 0x00000071, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(11), 0x000000b1, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(12), 0x0000001c, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(13), 0x0000005c, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(14), 0x00000029, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(15), 0x00000069, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(16), 0x000000a9, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(17), 0x00000020, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(18), 0x00000019, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(19), 0x00000059, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(20), 0x00000099, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(21), 0x00000030, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(22), 0x00000005, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(23), 0x00000025, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(24), 0x00000065, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(25), 0x000000a5, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(26), 0x00000028, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(27), 0x00000068, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(28), 0x0000001f, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(29), 0x0000001e, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(30), 0x00000018, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(31), 0x00000058, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(32), 0x00000098, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(33), 0x00000003, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(34), 0x00000004, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(35), 0x00000044, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(36), 0x00000084, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(37), 0x00000013, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(38), 0x00000012, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(39), 0x00000052, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(40), 0x00000092, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(41), 0x000000d2, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(42), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(43), 0x0000002a, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(44), 0x0000006a, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(45), 0x000000aa, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(46), 0x0000001b, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(47), 0x0000001a, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(48), 0x0000005a, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(49), 0x0000009a, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(50), 0x000000da, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(51), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(52), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(53), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(54), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(55), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(56), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(57), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(58), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(59), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(60), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(61), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(62), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_RF_GAIN(63), 0x00000006, AR5K_INI_WRITE },
+	/* PHY activation */
+	{ AR5K_PHY(53), 0x00000020, AR5K_INI_WRITE },
+	{ AR5K_PHY(51), 0x00000004, AR5K_INI_WRITE },
+	{ AR5K_PHY(50), 0x00060106, AR5K_INI_WRITE },
+	{ AR5K_PHY(39), 0x0000006d, AR5K_INI_WRITE },
+	{ AR5K_PHY(48), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(52), 0x00000014, AR5K_INI_WRITE },
+	{ AR5K_PHY_ACT, AR5K_PHY_ACT_ENABLE, AR5K_INI_WRITE },
+};
+
+/* Initial register settings for AR5211 */
+static const struct ath5k_ini ar5211_ini[] = {
+	{ AR5K_RXDP,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RTSD0,		0x84849c9c, AR5K_INI_WRITE },
+	{ AR5K_RTSD1,		0x7c7c7c7c, AR5K_INI_WRITE },
+	{ AR5K_RXCFG,		0x00000005, AR5K_INI_WRITE },
+	{ AR5K_MIBC,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_TOPS,		0x00000008, AR5K_INI_WRITE },
+	{ AR5K_RXNOFRM,		0x00000008, AR5K_INI_WRITE },
+	{ AR5K_TXNOFRM,		0x00000010, AR5K_INI_WRITE },
+	{ AR5K_RPGTO,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RFCNT,		0x0000001f, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(0),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(1),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(2),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(3),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(4),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(5),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(6),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(7),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(8),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(9),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_FP,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_STA_ID1,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BSS_ID0,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BSS_ID1,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RSSI_THR,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_CFP_PERIOD_5211,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_TIMER0_5211,	0x00000030, AR5K_INI_WRITE },
+	{ AR5K_TIMER1_5211,	0x0007ffff, AR5K_INI_WRITE },
+	{ AR5K_TIMER2_5211,	0x01ffffff, AR5K_INI_WRITE },
+	{ AR5K_TIMER3_5211,	0x00000031, AR5K_INI_WRITE },
+	{ AR5K_CFP_DUR_5211,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RX_FILTER_5211,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MCAST_FILTER0_5211, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MCAST_FILTER1_5211, 0x00000002, AR5K_INI_WRITE },
+	{ AR5K_DIAG_SW_5211,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_ADDAC_TEST,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DEFAULT_ANTENNA,	0x00000000, AR5K_INI_WRITE },
+	/* PHY registers */
+	{ AR5K_PHY_AGC,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(3),	0x2d849093, AR5K_INI_WRITE },
+	{ AR5K_PHY(4),	0x7d32e000, AR5K_INI_WRITE },
+	{ AR5K_PHY(5),	0x00000f6b, AR5K_INI_WRITE },
+	{ AR5K_PHY_ACT,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(11),	0x00026ffe, AR5K_INI_WRITE },
+	{ AR5K_PHY(12),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(15),	0x00020100, AR5K_INI_WRITE },
+	{ AR5K_PHY(16),	0x206a017a, AR5K_INI_WRITE },
+	{ AR5K_PHY(19),	0x1284613c, AR5K_INI_WRITE },
+	{ AR5K_PHY(21),	0x00000859, AR5K_INI_WRITE },
+	{ AR5K_PHY(26),	0x409a4190, AR5K_INI_WRITE },	/* 0x9868 */
+	{ AR5K_PHY(27),	0x050cb081, AR5K_INI_WRITE },
+	{ AR5K_PHY(28),	0x0000000f, AR5K_INI_WRITE },
+	{ AR5K_PHY(29),	0x00000080, AR5K_INI_WRITE },
+	{ AR5K_PHY(30),	0x0000000c, AR5K_INI_WRITE },
+	{ AR5K_PHY(64),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(65),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(66),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(67),	0x00800000, AR5K_INI_WRITE },
+	{ AR5K_PHY(68),	0x00000001, AR5K_INI_WRITE },
+	{ AR5K_PHY(71),	0x0000092a, AR5K_INI_WRITE },
+	{ AR5K_PHY_IQ,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(73),	0x00058a05, AR5K_INI_WRITE },
+	{ AR5K_PHY(74),	0x00000001, AR5K_INI_WRITE },
+	{ AR5K_PHY(75),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_PAPD_PROBE, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(77),	0x00000000, AR5K_INI_WRITE },	/* 0x9934 */
+	{ AR5K_PHY(78),	0x00000000, AR5K_INI_WRITE },	/* 0x9938 */
+	{ AR5K_PHY(79),	0x0000003f, AR5K_INI_WRITE },	/* 0x993c */
+	{ AR5K_PHY(80),	0x00000004, AR5K_INI_WRITE },
+	{ AR5K_PHY(82),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(83),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(84),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_RADAR, 0x5d50f14c, AR5K_INI_WRITE },
+	{ AR5K_PHY(86),	0x00000018, AR5K_INI_WRITE },
+	{ AR5K_PHY(87),	0x004b6a8e, AR5K_INI_WRITE },
+	/* Initial Power table (32bytes)
+	 * common on all cards/modes.
+	 * Note: Table is rewritten during
+	 * txpower setup later using calibration
+	 * data etc. so next write is non-common */
+	{ AR5K_PHY_PCDAC_TXPOWER(1), 0x06ff05ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(2), 0x07ff07ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(3), 0x08ff08ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(4), 0x09ff09ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(5), 0x0aff0aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(6), 0x0bff0bff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(7), 0x0cff0cff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(8), 0x0dff0dff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(9), 0x0fff0eff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(10), 0x12ff12ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(11), 0x14ff13ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(12), 0x16ff15ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(13), 0x19ff17ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(14), 0x1bff1aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(15), 0x1eff1dff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(16), 0x23ff20ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(17), 0x27ff25ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(18), 0x2cff29ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(19), 0x31ff2fff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(20), 0x37ff34ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(21), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(22), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(23), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(24), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(25), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(26), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(27), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(28), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(29), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(30), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_PCDAC_TXPOWER(31), 0x3aff3aff, AR5K_INI_WRITE },
+	{ AR5K_PHY_CCKTXCTL, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(642), 0x503e4646, AR5K_INI_WRITE },
+	{ AR5K_PHY_GAIN_2GHZ, 0x6480416c, AR5K_INI_WRITE },
+	{ AR5K_PHY(644), 0x0199a003, AR5K_INI_WRITE },
+	{ AR5K_PHY(645), 0x044cd610, AR5K_INI_WRITE },
+	{ AR5K_PHY(646), 0x13800040, AR5K_INI_WRITE },
+	{ AR5K_PHY(647), 0x1be00060, AR5K_INI_WRITE },
+	{ AR5K_PHY(648), 0x0c53800a, AR5K_INI_WRITE },
+	{ AR5K_PHY(649), 0x0014df3b, AR5K_INI_WRITE },
+	{ AR5K_PHY(650), 0x000001b5, AR5K_INI_WRITE },
+	{ AR5K_PHY(651), 0x00000020, AR5K_INI_WRITE },
+};
+
+/* Initial mode-specific settings for AR5211
+ * 5211 supports OFDM-only g (draft g) but we
+ * need to test it !
+ */
+static const struct ath5k_ini_mode ar5211_ini_mode[] = {
+	{ AR5K_TXCFG,
+	/*	  a	    aTurbo	  b	  g (OFDM)    */
+	   { 0x00000015, 0x00000015, 0x0000001d, 0x00000015 } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(0),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(1),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(2),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(3),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(4),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(5),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(6),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(7),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(8),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(9),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f } },
+	{ AR5K_DCU_GBL_IFS_SLOT,
+	   { 0x00000168, 0x000001e0, 0x000001b8, 0x00000168 } },
+	{ AR5K_DCU_GBL_IFS_SIFS,
+	   { 0x00000230, 0x000001e0, 0x000000b0, 0x00000230 } },
+	{ AR5K_DCU_GBL_IFS_EIFS,
+	   { 0x00000d98, 0x00001180, 0x00001f48, 0x00000d98 } },
+	{ AR5K_DCU_GBL_IFS_MISC,
+	   { 0x0000a0e0, 0x00014068, 0x00005880, 0x0000a0e0 } },
+	{ AR5K_TIME_OUT,
+	   { 0x04000400, 0x08000800, 0x20003000, 0x04000400 } },
+	{ AR5K_USEC_5211,
+	   { 0x0e8d8fa7, 0x0e8d8fcf, 0x01608f95, 0x0e8d8fa7 } },
+	{ AR5K_PHY_TURBO,
+	   { 0x00000000, 0x00000003, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY(8),
+	   { 0x02020200, 0x02020200, 0x02010200, 0x02020200 } },
+	{ AR5K_PHY(9),
+	   { 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e } },
+	{ AR5K_PHY(10),
+	   { 0x0a020001, 0x0a020001, 0x05010000, 0x0a020001 } },
+	{ AR5K_PHY(13),
+	   { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ AR5K_PHY(14),
+	   { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b } },
+	{ AR5K_PHY(17),
+	   { 0x1372169c, 0x137216a5, 0x137216a8, 0x1372169c } },
+	{ AR5K_PHY(18),
+	   { 0x0018ba67, 0x0018ba67, 0x0018ba69, 0x0018ba69 } },
+	{ AR5K_PHY(20),
+	   { 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0 } },
+	{ AR5K_PHY_SIG,
+	   { 0x7e800d2e, 0x7e800d2e, 0x7ec00d2e, 0x7e800d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+	   { 0x31375d5e, 0x31375d5e, 0x313a5d5e, 0x31375d5e } },
+	{ AR5K_PHY_AGCCTL,
+	   { 0x0000bd10, 0x0000bd10, 0x0000bd38, 0x0000bd10 } },
+	{ AR5K_PHY_NF,
+	   { 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } },
+	{ AR5K_PHY_RX_DELAY,
+	   { 0x00002710, 0x00002710, 0x0000157c, 0x00002710 } },
+	{ AR5K_PHY(70),
+	   { 0x00000190, 0x00000190, 0x00000084, 0x00000190 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+	   { 0x6fe01020, 0x6fe01020, 0x6fe00920, 0x6fe01020 } },
+	{ AR5K_PHY_PCDAC_TXPOWER_BASE,
+	   { 0x05ff14ff, 0x05ff14ff, 0x05ff14ff, 0x05ff19ff } },
+	{ AR5K_RF_BUFFER_CONTROL_4,
+	   { 0x00000010, 0x00000014, 0x00000010, 0x00000010 } },
+};
+
+/* Initial register settings for AR5212 */
+static const struct ath5k_ini ar5212_ini_common_start[] = {
+	{ AR5K_RXDP,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RXCFG,		0x00000005, AR5K_INI_WRITE },
+	{ AR5K_MIBC,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_TOPS,		0x00000008, AR5K_INI_WRITE },
+	{ AR5K_RXNOFRM,		0x00000008, AR5K_INI_WRITE },
+	{ AR5K_TXNOFRM,		0x00000010, AR5K_INI_WRITE },
+	{ AR5K_RPGTO,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RFCNT,		0x0000001f, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(0),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(1),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(2),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(3),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(4),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(5),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(6),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(7),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(8),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUEUE_TXDP(9),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_FP,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TXP,		0x00000000, AR5K_INI_WRITE },
+	/* Tx filter table 0 (32 entries) */
+	{ AR5K_DCU_TX_FILTER_0(0),  0x00000000, AR5K_INI_WRITE }, /* DCU 0 */
+	{ AR5K_DCU_TX_FILTER_0(1),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(2),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(3),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(4),  0x00000000, AR5K_INI_WRITE }, /* DCU 1 */
+	{ AR5K_DCU_TX_FILTER_0(5),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(6),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(7),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(8),  0x00000000, AR5K_INI_WRITE }, /* DCU 2 */
+	{ AR5K_DCU_TX_FILTER_0(9),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(10), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(11), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(12), 0x00000000, AR5K_INI_WRITE }, /* DCU 3 */
+	{ AR5K_DCU_TX_FILTER_0(13), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(14), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(15), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(16), 0x00000000, AR5K_INI_WRITE }, /* DCU 4 */
+	{ AR5K_DCU_TX_FILTER_0(17), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(18), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(19), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(20), 0x00000000, AR5K_INI_WRITE }, /* DCU 5 */
+	{ AR5K_DCU_TX_FILTER_0(21), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(22), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(23), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(24), 0x00000000, AR5K_INI_WRITE }, /* DCU 6 */
+	{ AR5K_DCU_TX_FILTER_0(25), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(26), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(27), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(28), 0x00000000, AR5K_INI_WRITE }, /* DCU 7 */
+	{ AR5K_DCU_TX_FILTER_0(29), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(30), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_0(31), 0x00000000, AR5K_INI_WRITE },
+	/* Tx filter table 1 (16 entries) */
+	{ AR5K_DCU_TX_FILTER_1(0),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(1),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(2),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(3),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(4),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(5),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(6),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(7),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(8),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(9),  0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(10), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(11), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(12), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(13), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(14), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_1(15), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_CLR, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_SET, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_CLR, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DCU_TX_FILTER_SET, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_STA_ID1,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BSS_ID0,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BSS_ID1,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BEACON_5211,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_CFP_PERIOD_5211, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_TIMER0_5211,	0x00000030, AR5K_INI_WRITE },
+	{ AR5K_TIMER1_5211,	0x0007ffff, AR5K_INI_WRITE },
+	{ AR5K_TIMER2_5211,	0x01ffffff, AR5K_INI_WRITE },
+	{ AR5K_TIMER3_5211,	0x00000031, AR5K_INI_WRITE },
+	{ AR5K_CFP_DUR_5211,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RX_FILTER_5211,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DIAG_SW_5211,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_ADDAC_TEST,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_DEFAULT_ANTENNA,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_FRAME_CTL_QOSM, 	0x000fc78f, AR5K_INI_WRITE },
+	{ AR5K_XRMODE,		0x2a82301a, AR5K_INI_WRITE },
+	{ AR5K_XRDELAY,		0x05dc01e0, AR5K_INI_WRITE },
+	{ AR5K_XRTIMEOUT,	0x1f402710, AR5K_INI_WRITE },
+	{ AR5K_XRCHIRP,		0x01f40000, AR5K_INI_WRITE },
+	{ AR5K_XRSTOMP,		0x00001e1c, AR5K_INI_WRITE },
+	{ AR5K_SLEEP0,		0x0002aaaa, AR5K_INI_WRITE },
+	{ AR5K_SLEEP1,		0x02005555, AR5K_INI_WRITE },
+	{ AR5K_SLEEP2,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BSS_IDM0,	0xffffffff, AR5K_INI_WRITE },
+	{ AR5K_BSS_IDM1,	0x0000ffff, AR5K_INI_WRITE },
+	{ AR5K_TXPC,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PROFCNT_TX,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PROFCNT_RX,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PROFCNT_RXCLR,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PROFCNT_CYCLE,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUIET_CTL1,	0x00000088, AR5K_INI_WRITE },
+	/* Initial rate duration table (32 entries )*/
+	{ AR5K_RATE_DUR(0),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(1),	0x0000008c, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(2),	0x000000e4, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(3),	0x000002d5, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(4),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(5),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(6),	0x000000a0, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(7),	0x000001c9, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(8),	0x0000002c, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(9),	0x0000002c, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(10),	0x00000030, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(11),	0x0000003c, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(12),	0x0000002c, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(13),	0x0000002c, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(14),	0x00000030, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(15),	0x0000003c, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(16),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(17),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(18),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(19),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(20),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(21),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(22),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(23),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(24),	0x000000d5, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(25),	0x000000df, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(26),	0x00000102, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(27),	0x0000013a, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(28),	0x00000075, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(29),	0x0000007f, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(30),	0x000000a2, AR5K_INI_WRITE },
+	{ AR5K_RATE_DUR(31),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_QUIET_CTL2,	0x00010002, AR5K_INI_WRITE },
+	{ AR5K_TSF_PARM,	0x00000001, AR5K_INI_WRITE },
+	{ AR5K_QOS_NOACK,	0x000000c0, AR5K_INI_WRITE },
+	{ AR5K_PHY_ERR_FIL,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_XRLAT_TX,	0x00000168, AR5K_INI_WRITE },
+	{ AR5K_ACKSIFS,		0x00000000, AR5K_INI_WRITE },
+	/* Rate -> db table
+	 * notice ...03<-02<-01<-00 ! */
+	{ AR5K_RATE2DB(0),	0x03020100, AR5K_INI_WRITE },
+	{ AR5K_RATE2DB(1),	0x07060504, AR5K_INI_WRITE },
+	{ AR5K_RATE2DB(2),	0x0b0a0908, AR5K_INI_WRITE },
+	{ AR5K_RATE2DB(3),	0x0f0e0d0c, AR5K_INI_WRITE },
+	{ AR5K_RATE2DB(4),	0x13121110, AR5K_INI_WRITE },
+	{ AR5K_RATE2DB(5),	0x17161514, AR5K_INI_WRITE },
+	{ AR5K_RATE2DB(6),	0x1b1a1918, AR5K_INI_WRITE },
+	{ AR5K_RATE2DB(7),	0x1f1e1d1c, AR5K_INI_WRITE },
+	/* Db -> Rate table */
+	{ AR5K_DB2RATE(0),	0x03020100, AR5K_INI_WRITE },
+	{ AR5K_DB2RATE(1),	0x07060504, AR5K_INI_WRITE },
+	{ AR5K_DB2RATE(2),	0x0b0a0908, AR5K_INI_WRITE },
+	{ AR5K_DB2RATE(3),	0x0f0e0d0c, AR5K_INI_WRITE },
+	{ AR5K_DB2RATE(4),	0x13121110, AR5K_INI_WRITE },
+	{ AR5K_DB2RATE(5),	0x17161514, AR5K_INI_WRITE },
+	{ AR5K_DB2RATE(6),	0x1b1a1918, AR5K_INI_WRITE },
+	{ AR5K_DB2RATE(7),	0x1f1e1d1c, AR5K_INI_WRITE },
+	/* PHY registers (Common settings
+	 * for all chips/modes) */
+	{ AR5K_PHY(3),		0xad848e19, AR5K_INI_WRITE },
+	{ AR5K_PHY(4),		0x7d28e000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_3,	0x9c0a9f6b, AR5K_INI_WRITE },
+	{ AR5K_PHY_ACT,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY(16),		0x206a017a, AR5K_INI_WRITE },
+	{ AR5K_PHY(21),		0x00000859, AR5K_INI_WRITE },
+	{ AR5K_PHY_BIN_MASK_1,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_BIN_MASK_2,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_BIN_MASK_3,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_BIN_MASK_CTL, 0x00800000, AR5K_INI_WRITE },
+	{ AR5K_PHY_ANT_CTL,	0x00000001, AR5K_INI_WRITE },
+	/*{ AR5K_PHY(71), 0x0000092a, AR5K_INI_WRITE },*/ /* Old value */
+	{ AR5K_PHY_MAX_RX_LEN,	0x00000c80, AR5K_INI_WRITE },
+	{ AR5K_PHY_IQ,		0x05100000, AR5K_INI_WRITE },
+	{ AR5K_PHY_WARM_RESET,	0x00000001, AR5K_INI_WRITE },
+	{ AR5K_PHY_CTL,		0x00000004, AR5K_INI_WRITE },
+	{ AR5K_PHY_TXPOWER_RATE1, 0x1e1f2022, AR5K_INI_WRITE },
+	{ AR5K_PHY_TXPOWER_RATE2, 0x0a0b0c0d, AR5K_INI_WRITE },
+	{ AR5K_PHY_TXPOWER_RATE_MAX, 0x0000003f, AR5K_INI_WRITE },
+	{ AR5K_PHY(82),		0x9280b212, AR5K_INI_WRITE },
+	{ AR5K_PHY_RADAR,	0x5d50e188, AR5K_INI_WRITE },
+	/*{ AR5K_PHY(86), 0x000000ff, AR5K_INI_WRITE },*/
+	{ AR5K_PHY(87),		0x004b6a8e, AR5K_INI_WRITE },
+	{ AR5K_PHY_NFTHRES,	0x000003ce, AR5K_INI_WRITE },
+	{ AR5K_PHY_RESTART,	0x192fb515, AR5K_INI_WRITE },
+	{ AR5K_PHY(94),		0x00000001, AR5K_INI_WRITE },
+	{ AR5K_PHY_RFBUS_REQ,	0x00000000, AR5K_INI_WRITE },
+	/*{ AR5K_PHY(644), 0x0080a333, AR5K_INI_WRITE },*/ /* Old value */
+	/*{ AR5K_PHY(645), 0x00206c10, AR5K_INI_WRITE },*/ /* Old value */
+	{ AR5K_PHY(644),	0x00806333, AR5K_INI_WRITE },
+	{ AR5K_PHY(645),	0x00106c10, AR5K_INI_WRITE },
+	{ AR5K_PHY(646),	0x009c4060, AR5K_INI_WRITE },
+	/* { AR5K_PHY(647), 0x1483800a, AR5K_INI_WRITE }, */
+	/* { AR5K_PHY(648), 0x01831061, AR5K_INI_WRITE }, */ /* Old value */
+	{ AR5K_PHY(648),	0x018830c6, AR5K_INI_WRITE },
+	{ AR5K_PHY(649),	0x00000400, AR5K_INI_WRITE },
+	/*{ AR5K_PHY(650), 0x000001b5, AR5K_INI_WRITE },*/
+	{ AR5K_PHY(651),	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TXPOWER_RATE3, 0x20202020, AR5K_INI_WRITE },
+	{ AR5K_PHY_TXPOWER_RATE2, 0x20202020, AR5K_INI_WRITE },
+	/*{ AR5K_PHY(655), 0x13c889af, AR5K_INI_WRITE },*/
+	{ AR5K_PHY(656),	0x38490a20, AR5K_INI_WRITE },
+	{ AR5K_PHY(657),	0x00007bb6, AR5K_INI_WRITE },
+	{ AR5K_PHY(658),	0x0fff3ffc, AR5K_INI_WRITE },
+};
+
+/* Initial mode-specific settings for AR5212 (Written before ar5212_ini) */
+static const struct ath5k_ini_mode ar5212_ini_mode_start[] = {
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(0),
+	/*	a/XR	   aTurbo	  b	   g (DYN)     gTurbo     */
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(1),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(2),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(3),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(4),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(5),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(6),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(7),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(8),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_QUEUE_DFS_LOCAL_IFS(9),
+	   { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } },
+	{ AR5K_DCU_GBL_IFS_SIFS,
+	   { 0x00000230, 0x000001e0, 0x000000b0, 0x00000160, 0x000001e0 } },
+	{ AR5K_DCU_GBL_IFS_SLOT,
+	   { 0x00000168, 0x000001e0, 0x000001b8, 0x0000018c, 0x000001e0 } },
+	{ AR5K_DCU_GBL_IFS_EIFS,
+	   { 0x00000e60, 0x00001180, 0x00001f1c, 0x00003e38, 0x00001180 } },
+	{ AR5K_DCU_GBL_IFS_MISC,
+	   { 0x0000a0e0, 0x00014068, 0x00005880, 0x0000b0e0, 0x00014068 } },
+	{ AR5K_TIME_OUT,
+	   { 0x03e803e8, 0x06e006e0, 0x04200420, 0x08400840, 0x06e006e0 } },
+	{ AR5K_PHY_TURBO,
+	   { 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000003 } },
+	{ AR5K_PHY(8),
+	   { 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } },
+	{ AR5K_PHY_RF_CTL2,
+	   { 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e, 0x00000e0e } },
+	{ AR5K_PHY_SETTLING,
+	   { 0x1372161c, 0x13721c25, 0x13721722, 0x137216a2, 0x13721c25 } },
+	{ AR5K_PHY_AGCCTL,
+	   { 0x00009d10, 0x00009d10, 0x00009d18, 0x00009d18, 0x00009d10 } },
+	{ AR5K_PHY_NF,
+	   { 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } },
+	{ AR5K_PHY_WEAK_OFDM_HIGH_THR,
+	   { 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 } },
+	{ AR5K_PHY(70),
+	   { 0x000001b8, 0x000001b8, 0x00000084, 0x00000108, 0x000001b8 } },
+	{ AR5K_PHY_OFDM_SELFCORR,
+	   { 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05 } },
+	{ 0xa230,
+	   { 0x00000000, 0x00000000, 0x00000000, 0x00000108, 0x00000000 } },
+};
+
+/* Initial mode-specific settings for AR5212 + RF5111 (Written after ar5212_ini) */
+static const struct ath5k_ini_mode rf5111_ini_mode_end[] = {
+	{ AR5K_TXCFG,
+	/*	a/XR	   aTurbo	  b	   g (DYN)     gTurbo     */
+	   { 0x00008015, 0x00008015, 0x00008015, 0x00008015, 0x00008015 } },
+	{ AR5K_USEC_5211,
+	   { 0x128d8fa7, 0x09880fcf, 0x04e00f95, 0x12e00fab, 0x09880fcf } },
+	{ AR5K_PHY_RF_CTL3,
+	   { 0x0a020001, 0x0a020001, 0x05010100, 0x0a020001, 0x0a020001 } },
+	{ AR5K_PHY_RF_CTL4,
+	   { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ AR5K_PHY_PA_CTL,
+	   { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } },
+	{ AR5K_PHY_GAIN,
+	   { 0x0018da5a, 0x0018da5a, 0x0018ca69, 0x0018ca69, 0x0018ca69 } },
+	{ AR5K_PHY_DESIRED_SIZE,
+	   { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } },
+	{ AR5K_PHY_SIG,
+	   { 0x7e800d2e, 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e, 0x7e800d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+	   { 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137615e } },
+	{ AR5K_PHY_WEAK_OFDM_LOW_THR,
+	   { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb080, 0x050cb080 } },
+	{ AR5K_PHY_RX_DELAY,
+	   { 0x00002710, 0x00002710, 0x0000157c, 0x00002af8, 0x00002710 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+	   { 0xf7b81020, 0xf7b81020, 0xf7b80d20, 0xf7b81020, 0xf7b81020 } },
+	{ AR5K_PHY_GAIN_2GHZ,
+	   { 0x642c416a, 0x642c416a, 0x6440416a, 0x6440416a, 0x6440416a } },
+	{ AR5K_PHY_CCK_RX_CTL_4,
+	   { 0x1883800a, 0x1883800a, 0x1873800a, 0x1883800a, 0x1883800a } },
+};
+
+static const struct ath5k_ini rf5111_ini_common_end[] = {
+	{ AR5K_DCU_FP,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_AGC, 	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_ADC_CTL, 	0x00022ffe, AR5K_INI_WRITE },
+	{ 0x983c, 		0x00020100, AR5K_INI_WRITE },
+	{ AR5K_PHY_GAIN_OFFSET,	0x1284613c, AR5K_INI_WRITE },
+	{ AR5K_PHY_PAPD_PROBE,	0x00004883, AR5K_INI_WRITE },
+	{ 0x9940,		0x00000004, AR5K_INI_WRITE },
+	{ 0x9958,		0x000000ff, AR5K_INI_WRITE },
+	{ 0x9974,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_SPENDING,	0x00000018, AR5K_INI_WRITE },
+	{ AR5K_PHY_CCKTXCTL,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_CCK_CROSSCORR, 0xd03e6788, AR5K_INI_WRITE },
+	{ AR5K_PHY_DAG_CCK_CTL,	0x000001b5, AR5K_INI_WRITE },
+	{ 0xa23c,		0x13c889af, AR5K_INI_WRITE },
+};
+
+/* Initial mode-specific settings for AR5212 + RF5112 (Written after ar5212_ini) */
+static const struct ath5k_ini_mode rf5112_ini_mode_end[] = {
+	{ AR5K_TXCFG,
+	/*	a/XR	   aTurbo	  b	   g (DYN)     gTurbo     */
+	   { 0x00008015, 0x00008015, 0x00008015, 0x00008015, 0x00008015 } },
+	{ AR5K_USEC_5211,
+	   { 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } },
+	{ AR5K_PHY_RF_CTL3,
+	   { 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } },
+	{ AR5K_PHY_RF_CTL4,
+	   { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ AR5K_PHY_PA_CTL,
+	   { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } },
+	{ AR5K_PHY_GAIN,
+	   { 0x0018da6d, 0x0018da6d, 0x0018ca75, 0x0018ca75, 0x0018ca75 } },
+	{ AR5K_PHY_DESIRED_SIZE,
+	   { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } },
+	{ AR5K_PHY_SIG,
+	   { 0x7e800d2e, 0x7e800d2e, 0x7ee80d2e, 0x7ee80d2e, 0x7e800d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+	   { 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e } },
+	{ AR5K_PHY_WEAK_OFDM_LOW_THR,
+	   { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } },
+	{ AR5K_PHY_RX_DELAY,
+	   { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+	   { 0xf7b81020, 0xf7b81020, 0xf7b80d10, 0xf7b81010, 0xf7b81010 } },
+	{ AR5K_PHY_CCKTXCTL,
+	   { 0x00000000, 0x00000000, 0x00000008, 0x00000008, 0x00000008 } },
+	{ AR5K_PHY_CCK_CROSSCORR,
+	   { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } },
+	{ AR5K_PHY_GAIN_2GHZ,
+	   { 0x642c0140, 0x642c0140, 0x6442c160, 0x6442c160, 0x6442c160 } },
+	{ AR5K_PHY_CCK_RX_CTL_4,
+	   { 0x1883800a, 0x1883800a, 0x1873800a, 0x1883800a, 0x1883800a } },
+};
+
+static const struct ath5k_ini rf5112_ini_common_end[] = {
+	{ AR5K_DCU_FP,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_AGC,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_ADC_CTL,	0x00022ffe, AR5K_INI_WRITE },
+	{ 0x983c,		0x00020100, AR5K_INI_WRITE },
+	{ AR5K_PHY_GAIN_OFFSET,	0x1284613c, AR5K_INI_WRITE },
+	{ AR5K_PHY_PAPD_PROBE,	0x00004882, AR5K_INI_WRITE },
+	{ 0x9940,		0x00000004, AR5K_INI_WRITE },
+	{ 0x9958,		0x000000ff, AR5K_INI_WRITE },
+	{ 0x9974,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_DAG_CCK_CTL,	0x000001b5, AR5K_INI_WRITE },
+	{ 0xa23c,		0x13c889af, AR5K_INI_WRITE },
+};
+
+/* Initial mode-specific settings for RF5413/5414 (Written after ar5212_ini) */
+static const struct ath5k_ini_mode rf5413_ini_mode_end[] = {
+	{ AR5K_TXCFG,
+	/*	a/XR	   aTurbo	  b	   g (DYN)     gTurbo     */
+	   { 0x00000015, 0x00000015, 0x00000015, 0x00000015, 0x00000015 } },
+	{ AR5K_USEC_5211,
+	   { 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } },
+	{ AR5K_PHY_RF_CTL3,
+	   { 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } },
+	{ AR5K_PHY_RF_CTL4,
+	   { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ AR5K_PHY_PA_CTL,
+	   { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } },
+	{ AR5K_PHY_GAIN,
+	   { 0x0018fa61, 0x0018fa61, 0x001a1a63, 0x001a1a63, 0x001a1a63 } },
+	{ AR5K_PHY_DESIRED_SIZE,
+	   { 0x0c98b4e0, 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da, 0x0c98b0da } },
+	{ AR5K_PHY_SIG,
+	   { 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+	   { 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e } },
+	{ AR5K_PHY_WEAK_OFDM_LOW_THR,
+	   { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } },
+	{ AR5K_PHY_RX_DELAY,
+	   { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+	   { 0xf7b81000, 0xf7b81000, 0xf7b80d00, 0xf7b81000, 0xf7b81000 } },
+	{ AR5K_PHY_CCKTXCTL,
+	   { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY_CCK_CROSSCORR,
+	   { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } },
+	{ AR5K_PHY_GAIN_2GHZ,
+	   { 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 } },
+	{ AR5K_PHY_CCK_RX_CTL_4,
+	   { 0x1883800a, 0x1883800a, 0x1863800a, 0x1883800a, 0x1883800a } },
+	{ 0xa300,
+	   { 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 } },
+	{ 0xa304,
+	   { 0x30032602, 0x30032602, 0x30032602, 0x30032602, 0x30032602 } },
+	{ 0xa308,
+	   { 0x48073e06, 0x48073e06, 0x48073e06, 0x48073e06, 0x48073e06 } },
+	{ 0xa30c,
+	   { 0x560b4c0a, 0x560b4c0a, 0x560b4c0a, 0x560b4c0a, 0x560b4c0a } },
+	{ 0xa310,
+	   { 0x641a600f, 0x641a600f, 0x641a600f, 0x641a600f, 0x641a600f } },
+	{ 0xa314,
+	   { 0x784f6e1b, 0x784f6e1b, 0x784f6e1b, 0x784f6e1b, 0x784f6e1b } },
+	{ 0xa318,
+	   { 0x868f7c5a, 0x868f7c5a, 0x868f7c5a, 0x868f7c5a, 0x868f7c5a } },
+	{ 0xa31c,
+	   { 0x90cf865b, 0x90cf865b, 0x8ecf865b, 0x8ecf865b, 0x8ecf865b } },
+	{ 0xa320,
+	   { 0x9d4f970f, 0x9d4f970f, 0x9b4f970f, 0x9b4f970f, 0x9b4f970f } },
+	{ 0xa324,
+	   { 0xa7cfa38f, 0xa7cfa38f, 0xa3cf9f8f, 0xa3cf9f8f, 0xa3cf9f8f } },
+	{ 0xa328,
+	   { 0xb55faf1f, 0xb55faf1f, 0xb35faf1f, 0xb35faf1f, 0xb35faf1f } },
+	{ 0xa32c,
+	   { 0xbddfb99f, 0xbddfb99f, 0xbbdfb99f, 0xbbdfb99f, 0xbbdfb99f } },
+	{ 0xa330,
+	   { 0xcb7fc53f, 0xcb7fc53f, 0xcb7fc73f, 0xcb7fc73f, 0xcb7fc73f } },
+	{ 0xa334,
+	   { 0xd5ffd1bf, 0xd5ffd1bf, 0xd3ffd1bf, 0xd3ffd1bf, 0xd3ffd1bf } },
+};
+
+static const struct ath5k_ini rf5413_ini_common_end[] = {
+	{ AR5K_DCU_FP,		0x000003e0, AR5K_INI_WRITE },
+	{ AR5K_5414_CBCFG,	0x00000010, AR5K_INI_WRITE },
+	{ AR5K_SEQ_MASK,	0x0000000f, AR5K_INI_WRITE },
+	{ 0x809c,		0x00000000, AR5K_INI_WRITE },
+	{ 0x80a0,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MIC_QOS_CTL,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MIC_QOS_SEL,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MISC_MODE,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_OFDM_FIL_CNT,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_CCK_FIL_CNT,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT1,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT1_MASK, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT2,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT2_MASK, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_TSF_THRES,	0x00000000, AR5K_INI_WRITE },
+	{ 0x8140,		0x800003f9, AR5K_INI_WRITE },
+	{ 0x8144,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_AGC,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_ADC_CTL,	0x0000a000, AR5K_INI_WRITE },
+	{ 0x983c,		0x00200400, AR5K_INI_WRITE },
+	{ AR5K_PHY_GAIN_OFFSET, 0x1284233c, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCR,		0x0000001f, AR5K_INI_WRITE },
+	{ AR5K_PHY_SLMT,	0x00000080, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCAL,	0x0000000e, AR5K_INI_WRITE },
+	{ 0x9958,		0x00081fff, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_7,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_8,	0x02800000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_11,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000, AR5K_INI_WRITE },
+	{ 0x99e4,		0xaaaaaaaa, AR5K_INI_WRITE },
+	{ 0x99e8,		0x3c466478, AR5K_INI_WRITE },
+	{ 0x99ec,		0x000000aa, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCLOCK,	0x0000000c, AR5K_INI_WRITE },
+	{ AR5K_PHY_SDELAY,	0x000000ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_SPENDING,	0x00000014, AR5K_INI_WRITE },
+	{ AR5K_PHY_DAG_CCK_CTL, 0x000009b5, AR5K_INI_WRITE },
+	{ 0xa23c,		0x93c889af, AR5K_INI_WRITE },
+	{ AR5K_PHY_FAST_ADC,	0x00000001, AR5K_INI_WRITE },
+	{ 0xa250,		0x0000a000, AR5K_INI_WRITE },
+	{ AR5K_PHY_BLUETOOTH,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TPC_RG1,	0x0cc75380, AR5K_INI_WRITE },
+	{ 0xa25c,		0x0f0f0f01, AR5K_INI_WRITE },
+	{ 0xa260,		0x5f690f01, AR5K_INI_WRITE },
+	{ 0xa264,		0x00418a11, AR5K_INI_WRITE },
+	{ 0xa268,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TPC_RG5,	0x0c30c16a, AR5K_INI_WRITE },
+	{ 0xa270, 0x00820820, AR5K_INI_WRITE },
+	{ 0xa274, 0x081b7caa, AR5K_INI_WRITE },
+	{ 0xa278, 0x1ce739ce, AR5K_INI_WRITE },
+	{ 0xa27c, 0x051701ce, AR5K_INI_WRITE },
+	{ 0xa338, 0x00000000, AR5K_INI_WRITE },
+	{ 0xa33c, 0x00000000, AR5K_INI_WRITE },
+	{ 0xa340, 0x00000000, AR5K_INI_WRITE },
+	{ 0xa344, 0x00000000, AR5K_INI_WRITE },
+	{ 0xa348, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa34c, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa350, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa354, 0x0003ffff, AR5K_INI_WRITE },
+	{ 0xa358, 0x79a8aa1f, AR5K_INI_WRITE },
+	{ 0xa35c, 0x066c420f, AR5K_INI_WRITE },
+	{ 0xa360, 0x0f282207, AR5K_INI_WRITE },
+	{ 0xa364, 0x17601685, AR5K_INI_WRITE },
+	{ 0xa368, 0x1f801104, AR5K_INI_WRITE },
+	{ 0xa36c, 0x37a00c03, AR5K_INI_WRITE },
+	{ 0xa370, 0x3fc40883, AR5K_INI_WRITE },
+	{ 0xa374, 0x57c00803, AR5K_INI_WRITE },
+	{ 0xa378, 0x5fd80682, AR5K_INI_WRITE },
+	{ 0xa37c, 0x7fe00482, AR5K_INI_WRITE },
+	{ 0xa380, 0x7f3c7bba, AR5K_INI_WRITE },
+	{ 0xa384, 0xf3307ff0, AR5K_INI_WRITE },
+};
+
+/* Initial mode-specific settings for RF2413/2414 (Written after ar5212_ini) */
+/* XXX: a mode ? */
+static const struct ath5k_ini_mode rf2413_ini_mode_end[] = {
+	{ AR5K_TXCFG,
+	/*	a/XR	   aTurbo	  b	   g (DYN)     gTurbo     */
+	   { 0x00000015, 0x00000015, 0x00000015, 0x00000015, 0x00000015 } },
+	{ AR5K_USEC_5211,
+	   { 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } },
+	{ AR5K_PHY_RF_CTL3,
+	   { 0x0a020001, 0x0a020001, 0x05020000, 0x0a020001, 0x0a020001 } },
+	{ AR5K_PHY_RF_CTL4,
+	   { 0x00000e00, 0x00000e00, 0x00000e00, 0x00000e00, 0x00000e00 } },
+	{ AR5K_PHY_PA_CTL,
+	   { 0x00000002, 0x00000002, 0x0000000a, 0x0000000a, 0x0000000a } },
+	{ AR5K_PHY_GAIN,
+	   { 0x0018da6d, 0x0018da6d, 0x001a6a64, 0x001a6a64, 0x001a6a64 } },
+	{ AR5K_PHY_DESIRED_SIZE,
+	   { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b0da, 0x0c98b0da, 0x0de8b0da } },
+	{ AR5K_PHY_SIG,
+	   { 0x7e800d2e, 0x7e800d2e, 0x7ee80d2e, 0x7ec80d2e, 0x7e800d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+	   { 0x3137665e, 0x3137665e, 0x3137665e, 0x3139605e, 0x3137665e } },
+	{ AR5K_PHY_WEAK_OFDM_LOW_THR,
+	   { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } },
+	{ AR5K_PHY_RX_DELAY,
+	   { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+	   { 0xf7b81000, 0xf7b81000, 0xf7b80d00, 0xf7b81000, 0xf7b81000 } },
+	{ AR5K_PHY_CCKTXCTL,
+	   { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY_CCK_CROSSCORR,
+	   { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } },
+	{ AR5K_PHY_GAIN_2GHZ,
+	   { 0x002c0140, 0x002c0140, 0x0042c140, 0x0042c140, 0x0042c140 } },
+	{ AR5K_PHY_CCK_RX_CTL_4,
+	   { 0x1883800a, 0x1883800a, 0x1863800a, 0x1883800a, 0x1883800a } },
+};
+
+static const struct ath5k_ini rf2413_ini_common_end[] = {
+	{ AR5K_DCU_FP,		0x000003e0, AR5K_INI_WRITE },
+	{ AR5K_SEQ_MASK,	0x0000000f, AR5K_INI_WRITE },
+	{ AR5K_MIC_QOS_CTL,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MIC_QOS_SEL,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MISC_MODE,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_OFDM_FIL_CNT,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_CCK_FIL_CNT,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT1,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT1_MASK, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT2,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT2_MASK, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_TSF_THRES,	0x00000000, AR5K_INI_WRITE },
+	{ 0x8140,		0x800000a8, AR5K_INI_WRITE },
+	{ 0x8144,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_AGC,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_ADC_CTL,	0x0000a000, AR5K_INI_WRITE },
+	{ 0x983c,		0x00200400, AR5K_INI_WRITE },
+	{ AR5K_PHY_GAIN_OFFSET,	0x1284233c, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCR,		0x0000001f, AR5K_INI_WRITE },
+	{ AR5K_PHY_SLMT,	0x00000080, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCAL,	0x0000000e, AR5K_INI_WRITE },
+	{ 0x9958,		0x000000ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_7,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_8,	0x02800000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_11,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000, AR5K_INI_WRITE },
+	{ 0x99e4,		0xaaaaaaaa, AR5K_INI_WRITE },
+	{ 0x99e8,		0x3c466478, AR5K_INI_WRITE },
+	{ 0x99ec,		0x000000aa, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCLOCK,	0x0000000c, AR5K_INI_WRITE },
+	{ AR5K_PHY_SDELAY,	0x000000ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_SPENDING,	0x00000014, AR5K_INI_WRITE },
+	{ AR5K_PHY_DAG_CCK_CTL,	0x000009b5, AR5K_INI_WRITE },
+	{ 0xa23c,		0x93c889af, AR5K_INI_WRITE },
+	{ AR5K_PHY_FAST_ADC,	0x00000001, AR5K_INI_WRITE },
+	{ 0xa250,		0x0000a000, AR5K_INI_WRITE },
+	{ AR5K_PHY_BLUETOOTH,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TPC_RG1,	0x0cc75380, AR5K_INI_WRITE },
+	{ 0xa25c,		0x0f0f0f01, AR5K_INI_WRITE },
+	{ 0xa260,		0x5f690f01, AR5K_INI_WRITE },
+	{ 0xa264,		0x00418a11, AR5K_INI_WRITE },
+	{ 0xa268,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TPC_RG5,	0x0c30c16a, AR5K_INI_WRITE },
+	{ 0xa270, 0x00820820, AR5K_INI_WRITE },
+	{ 0xa274, 0x001b7caa, AR5K_INI_WRITE },
+	{ 0xa278, 0x1ce739ce, AR5K_INI_WRITE },
+	{ 0xa27c, 0x051701ce, AR5K_INI_WRITE },
+	{ 0xa300, 0x18010000, AR5K_INI_WRITE },
+	{ 0xa304, 0x30032602, AR5K_INI_WRITE },
+	{ 0xa308, 0x48073e06, AR5K_INI_WRITE },
+	{ 0xa30c, 0x560b4c0a, AR5K_INI_WRITE },
+	{ 0xa310, 0x641a600f, AR5K_INI_WRITE },
+	{ 0xa314, 0x784f6e1b, AR5K_INI_WRITE },
+	{ 0xa318, 0x868f7c5a, AR5K_INI_WRITE },
+	{ 0xa31c, 0x8ecf865b, AR5K_INI_WRITE },
+	{ 0xa320, 0x9d4f970f, AR5K_INI_WRITE },
+	{ 0xa324, 0xa5cfa18f, AR5K_INI_WRITE },
+	{ 0xa328, 0xb55faf1f, AR5K_INI_WRITE },
+	{ 0xa32c, 0xbddfb99f, AR5K_INI_WRITE },
+	{ 0xa330, 0xcd7fc73f, AR5K_INI_WRITE },
+	{ 0xa334, 0xd5ffd1bf, AR5K_INI_WRITE },
+	{ 0xa338, 0x00000000, AR5K_INI_WRITE },
+	{ 0xa33c, 0x00000000, AR5K_INI_WRITE },
+	{ 0xa340, 0x00000000, AR5K_INI_WRITE },
+	{ 0xa344, 0x00000000, AR5K_INI_WRITE },
+	{ 0xa348, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa34c, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa350, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa354, 0x0003ffff, AR5K_INI_WRITE },
+	{ 0xa358, 0x79a8aa1f, AR5K_INI_WRITE },
+	{ 0xa35c, 0x066c420f, AR5K_INI_WRITE },
+	{ 0xa360, 0x0f282207, AR5K_INI_WRITE },
+	{ 0xa364, 0x17601685, AR5K_INI_WRITE },
+	{ 0xa368, 0x1f801104, AR5K_INI_WRITE },
+	{ 0xa36c, 0x37a00c03, AR5K_INI_WRITE },
+	{ 0xa370, 0x3fc40883, AR5K_INI_WRITE },
+	{ 0xa374, 0x57c00803, AR5K_INI_WRITE },
+	{ 0xa378, 0x5fd80682, AR5K_INI_WRITE },
+	{ 0xa37c, 0x7fe00482, AR5K_INI_WRITE },
+	{ 0xa380, 0x7f3c7bba, AR5K_INI_WRITE },
+	{ 0xa384, 0xf3307ff0, AR5K_INI_WRITE },
+};
+
+/* Initial mode-specific settings for RF2425 (Written after ar5212_ini) */
+/* XXX: a mode ? */
+static const struct ath5k_ini_mode rf2425_ini_mode_end[] = {
+	{ AR5K_TXCFG,
+	/*	a/XR	   aTurbo	  b	   g (DYN)     gTurbo     */
+	   { 0x00000015, 0x00000015, 0x00000015, 0x00000015, 0x00000015 } },
+	{ AR5K_USEC_5211,
+	   { 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } },
+	{ AR5K_PHY_TURBO,
+	   { 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001 } },
+	{ AR5K_PHY_RF_CTL3,
+	   { 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } },
+	{ AR5K_PHY_RF_CTL4,
+	   { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } },
+	{ AR5K_PHY_PA_CTL,
+	   { 0x00000003, 0x00000003, 0x0000000b, 0x0000000b, 0x0000000b } },
+	{ AR5K_PHY_SETTLING,
+	   { 0x1372161c, 0x13721c25, 0x13721722, 0x13721422, 0x13721c25 } },
+	{ AR5K_PHY_GAIN,
+	   { 0x0018fa61, 0x0018fa61, 0x00199a65, 0x00199a65, 0x00199a65 } },
+	{ AR5K_PHY_DESIRED_SIZE,
+	   { 0x0c98b4e0, 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da, 0x0c98b0da } },
+	{ AR5K_PHY_SIG,
+	   { 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+	   { 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e } },
+	{ AR5K_PHY_WEAK_OFDM_LOW_THR,
+	   { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } },
+	{ AR5K_PHY_RX_DELAY,
+	   { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+	   { 0xf7b81000, 0xf7b81000, 0xf7b80d00, 0xf7b81000, 0xf7b81000 } },
+	{ AR5K_PHY_CCKTXCTL,
+	   { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY_CCK_CROSSCORR,
+	   { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } },
+	{ AR5K_PHY_GAIN_2GHZ,
+	   { 0x00000140, 0x00000140, 0x0052c140, 0x0052c140, 0x0052c140 } },
+	{ AR5K_PHY_CCK_RX_CTL_4,
+	   { 0x1883800a, 0x1883800a, 0x1863800a, 0x1883800a, 0x1883800a } },
+	{ 0xa324,
+	   { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+	{ 0xa328,
+	   { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+	{ 0xa32c,
+	   { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+	{ 0xa330,
+	   { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+	{ 0xa334,
+	   { 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf, 0xa7cfa7cf } },
+};
+
+static const struct ath5k_ini rf2425_ini_common_end[] = {
+	{ AR5K_DCU_FP,		0x000003e0, AR5K_INI_WRITE },
+	{ AR5K_SEQ_MASK,	0x0000000f, AR5K_INI_WRITE },
+	{ 0x809c,		0x00000000, AR5K_INI_WRITE },
+	{ 0x80a0,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MIC_QOS_CTL,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MIC_QOS_SEL,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_MISC_MODE,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_OFDM_FIL_CNT,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_CCK_FIL_CNT,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT1,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT1_MASK, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT2,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHYERR_CNT2_MASK, 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_TSF_THRES,	0x00000000, AR5K_INI_WRITE },
+	{ 0x8140,		0x800003f9, AR5K_INI_WRITE },
+	{ 0x8144,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_AGC,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_ADC_CTL,	0x0000a000, AR5K_INI_WRITE },
+	{ 0x983c,		0x00200400, AR5K_INI_WRITE },
+	{ AR5K_PHY_GAIN_OFFSET, 0x1284233c, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCR,		0x0000001f, AR5K_INI_WRITE },
+	{ AR5K_PHY_SLMT,	0x00000080, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCAL,	0x0000000e, AR5K_INI_WRITE },
+	{ 0x9958,		0x00081fff, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_7,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_8,	0x02800000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TIMING_11,	0x00000000, AR5K_INI_WRITE },
+	{ 0x99dc,		0xfebadbe8, AR5K_INI_WRITE },
+	{ AR5K_PHY_HEAVY_CLIP_ENABLE, 0x00000000, AR5K_INI_WRITE },
+	{ 0x99e4,		0xaaaaaaaa, AR5K_INI_WRITE },
+	{ 0x99e8,		0x3c466478, AR5K_INI_WRITE },
+	{ 0x99ec,		0x000000aa, AR5K_INI_WRITE },
+	{ AR5K_PHY_SCLOCK,	0x0000000c, AR5K_INI_WRITE },
+	{ AR5K_PHY_SDELAY,	0x000000ff, AR5K_INI_WRITE },
+	{ AR5K_PHY_SPENDING,	0x00000014, AR5K_INI_WRITE },
+	{ AR5K_PHY_DAG_CCK_CTL,	0x000009b5, AR5K_INI_WRITE },
+	{ AR5K_PHY_TXPOWER_RATE3, 0x20202020, AR5K_INI_WRITE },
+	{ AR5K_PHY_TXPOWER_RATE4, 0x20202020, AR5K_INI_WRITE },
+	{ 0xa23c,		0x93c889af, AR5K_INI_WRITE },
+	{ AR5K_PHY_FAST_ADC,	0x00000001, AR5K_INI_WRITE },
+	{ 0xa250,		0x0000a000, AR5K_INI_WRITE },
+	{ AR5K_PHY_BLUETOOTH,	0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TPC_RG1,	0x0cc75380, AR5K_INI_WRITE },
+	{ 0xa25c,		0x0f0f0f01, AR5K_INI_WRITE },
+	{ 0xa260,		0x5f690f01, AR5K_INI_WRITE },
+	{ 0xa264,		0x00418a11, AR5K_INI_WRITE },
+	{ 0xa268,		0x00000000, AR5K_INI_WRITE },
+	{ AR5K_PHY_TPC_RG5,	0x0c30c166, AR5K_INI_WRITE },
+	{ 0xa270, 0x00820820, AR5K_INI_WRITE },
+	{ 0xa274, 0x081a3caa, AR5K_INI_WRITE },
+	{ 0xa278, 0x1ce739ce, AR5K_INI_WRITE },
+	{ 0xa27c, 0x051701ce, AR5K_INI_WRITE },
+	{ 0xa300, 0x16010000, AR5K_INI_WRITE },
+	{ 0xa304, 0x2c032402, AR5K_INI_WRITE },
+	{ 0xa308, 0x48433e42, AR5K_INI_WRITE },
+	{ 0xa30c, 0x5a0f500b, AR5K_INI_WRITE },
+	{ 0xa310, 0x6c4b624a, AR5K_INI_WRITE },
+	{ 0xa314, 0x7e8b748a, AR5K_INI_WRITE },
+	{ 0xa318, 0x96cf8ccb, AR5K_INI_WRITE },
+	{ 0xa31c, 0xa34f9d0f, AR5K_INI_WRITE },
+	{ 0xa320, 0xa7cfa58f, AR5K_INI_WRITE },
+	{ 0xa348, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa34c, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa350, 0x3fffffff, AR5K_INI_WRITE },
+	{ 0xa354, 0x0003ffff, AR5K_INI_WRITE },
+	{ 0xa358, 0x79a8aa1f, AR5K_INI_WRITE },
+	{ 0xa35c, 0x066c420f, AR5K_INI_WRITE },
+	{ 0xa360, 0x0f282207, AR5K_INI_WRITE },
+	{ 0xa364, 0x17601685, AR5K_INI_WRITE },
+	{ 0xa368, 0x1f801104, AR5K_INI_WRITE },
+	{ 0xa36c, 0x37a00c03, AR5K_INI_WRITE },
+	{ 0xa370, 0x3fc40883, AR5K_INI_WRITE },
+	{ 0xa374, 0x57c00803, AR5K_INI_WRITE },
+	{ 0xa378, 0x5fd80682, AR5K_INI_WRITE },
+	{ 0xa37c, 0x7fe00482, AR5K_INI_WRITE },
+	{ 0xa380, 0x7f3c7bba, AR5K_INI_WRITE },
+	{ 0xa384, 0xf3307ff0, AR5K_INI_WRITE },
+};
+
+/*
+ * Initial BaseBand Gain settings for RF5111/5112 (AR5210 comes with
+ * RF5110 only so initial BB Gain settings are included in AR5K_AR5210_INI)
+ */
+
+/* RF5111 Initial BaseBand Gain settings */
+static const struct ath5k_ini rf5111_ini_bbgain[] = {
+	{ AR5K_BB_GAIN(0), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(1), 0x00000020, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(2), 0x00000010, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(3), 0x00000030, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(4), 0x00000008, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(5), 0x00000028, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(6), 0x00000004, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(7), 0x00000024, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(8), 0x00000014, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(9), 0x00000034, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(10), 0x0000000c, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(11), 0x0000002c, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(12), 0x00000002, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(13), 0x00000022, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(14), 0x00000012, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(15), 0x00000032, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(16), 0x0000000a, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(17), 0x0000002a, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(18), 0x00000006, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(19), 0x00000026, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(20), 0x00000016, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(21), 0x00000036, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(22), 0x0000000e, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(23), 0x0000002e, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(24), 0x00000001, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(25), 0x00000021, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(26), 0x00000011, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(27), 0x00000031, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(28), 0x00000009, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(29), 0x00000029, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(30), 0x00000005, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(31), 0x00000025, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(32), 0x00000015, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(33), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(34), 0x0000000d, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(35), 0x0000002d, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(36), 0x00000003, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(37), 0x00000023, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(38), 0x00000013, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(39), 0x00000033, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(40), 0x0000000b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(41), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(42), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(43), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(44), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(45), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(46), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(47), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(48), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(49), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(50), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(51), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(52), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(53), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(54), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(55), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(56), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(57), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(58), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(59), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(60), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(61), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(62), 0x00000002, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(63), 0x00000016, AR5K_INI_WRITE },
+};
+
+/* RF5112 Initial BaseBand Gain settings (Same for RF5413/5414+) */
+static const struct ath5k_ini rf5112_ini_bbgain[] = {
+	{ AR5K_BB_GAIN(0), 0x00000000, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(1), 0x00000001, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(2), 0x00000002, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(3), 0x00000003, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(4), 0x00000004, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(5), 0x00000005, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(6), 0x00000008, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(7), 0x00000009, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(8), 0x0000000a, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(9), 0x0000000b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(10), 0x0000000c, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(11), 0x0000000d, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(12), 0x00000010, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(13), 0x00000011, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(14), 0x00000012, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(15), 0x00000013, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(16), 0x00000014, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(17), 0x00000015, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(18), 0x00000018, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(19), 0x00000019, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(20), 0x0000001a, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(21), 0x0000001b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(22), 0x0000001c, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(23), 0x0000001d, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(24), 0x00000020, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(25), 0x00000021, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(26), 0x00000022, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(27), 0x00000023, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(28), 0x00000024, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(29), 0x00000025, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(30), 0x00000028, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(31), 0x00000029, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(32), 0x0000002a, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(33), 0x0000002b, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(34), 0x0000002c, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(35), 0x0000002d, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(36), 0x00000030, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(37), 0x00000031, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(38), 0x00000032, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(39), 0x00000033, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(40), 0x00000034, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(41), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(42), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(43), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(44), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(45), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(46), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(47), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(48), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(49), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(50), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(51), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(52), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(53), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(54), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(55), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(56), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(57), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(58), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(59), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(60), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(61), 0x00000035, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(62), 0x00000010, AR5K_INI_WRITE },
+	{ AR5K_BB_GAIN(63), 0x0000001a, AR5K_INI_WRITE },
+};
+
+
+/*
+ * Write initial register dump
+ */
+static void ath5k_hw_ini_registers(struct ath5k_hw *ah, unsigned int size,
+		const struct ath5k_ini *ini_regs, int change_channel)
+{
+	unsigned int i;
+
+	/* Write initial registers */
+	for (i = 0; i < size; i++) {
+		/* On channel change there is
+		 * no need to mess with PCU */
+		if (change_channel &&
+				ini_regs[i].ini_register >= AR5K_PCU_MIN &&
+				ini_regs[i].ini_register <= AR5K_PCU_MAX)
+			continue;
+
+		switch (ini_regs[i].ini_mode) {
+		case AR5K_INI_READ:
+			/* Cleared on read */
+			ath5k_hw_reg_read(ah, ini_regs[i].ini_register);
+			break;
+		case AR5K_INI_WRITE:
+		default:
+			AR5K_REG_WAIT(i);
+			ath5k_hw_reg_write(ah, ini_regs[i].ini_value,
+					ini_regs[i].ini_register);
+		}
+	}
+}
+
+static void ath5k_hw_ini_mode_registers(struct ath5k_hw *ah,
+		unsigned int size, const struct ath5k_ini_mode *ini_mode,
+		u8 mode)
+{
+	unsigned int i;
+
+	for (i = 0; i < size; i++) {
+		AR5K_REG_WAIT(i);
+		ath5k_hw_reg_write(ah, ini_mode[i].mode_value[mode],
+			(u32)ini_mode[i].mode_register);
+	}
+}
+
+int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, int change_channel)
+{
+	/*
+	 * Write initial register settings
+	 */
+
+	/* For AR5212 and combatible */
+	if (ah->ah_version == AR5K_AR5212) {
+
+		/* First set of mode-specific settings */
+		ath5k_hw_ini_mode_registers(ah,
+			ARRAY_SIZE(ar5212_ini_mode_start),
+			ar5212_ini_mode_start, mode);
+
+		/*
+		 * Write initial settings common for all modes
+		 */
+		ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5212_ini_common_start),
+				ar5212_ini_common_start, change_channel);
+
+		/* Second set of mode-specific settings */
+		switch (ah->ah_radio) {
+		case AR5K_RF5111:
+
+			ath5k_hw_ini_mode_registers(ah,
+					ARRAY_SIZE(rf5111_ini_mode_end),
+					rf5111_ini_mode_end, mode);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5111_ini_common_end),
+					rf5111_ini_common_end, change_channel);
+
+			/* Baseband gain table */
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5111_ini_bbgain),
+					rf5111_ini_bbgain, change_channel);
+
+			break;
+		case AR5K_RF5112:
+
+			ath5k_hw_ini_mode_registers(ah,
+					ARRAY_SIZE(rf5112_ini_mode_end),
+					rf5112_ini_mode_end, mode);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5112_ini_common_end),
+					rf5112_ini_common_end, change_channel);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5112_ini_bbgain),
+					rf5112_ini_bbgain, change_channel);
+
+			break;
+		case AR5K_RF5413:
+
+			ath5k_hw_ini_mode_registers(ah,
+					ARRAY_SIZE(rf5413_ini_mode_end),
+					rf5413_ini_mode_end, mode);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5413_ini_common_end),
+					rf5413_ini_common_end, change_channel);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5112_ini_bbgain),
+					rf5112_ini_bbgain, change_channel);
+
+			break;
+		case AR5K_RF2316:
+		case AR5K_RF2413:
+
+			ath5k_hw_ini_mode_registers(ah,
+					ARRAY_SIZE(rf2413_ini_mode_end),
+					rf2413_ini_mode_end, mode);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf2413_ini_common_end),
+					rf2413_ini_common_end, change_channel);
+
+			/* Override settings from rf2413_ini_common_end */
+			if (ah->ah_radio == AR5K_RF2316) {
+				ath5k_hw_reg_write(ah, 0x00004000,
+							AR5K_PHY_AGC);
+				ath5k_hw_reg_write(ah, 0x081b7caa,
+							0xa274);
+			}
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5112_ini_bbgain),
+					rf5112_ini_bbgain, change_channel);
+			break;
+		case AR5K_RF2317:
+		case AR5K_RF2425:
+
+			ath5k_hw_ini_mode_registers(ah,
+					ARRAY_SIZE(rf2425_ini_mode_end),
+					rf2425_ini_mode_end, mode);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf2425_ini_common_end),
+					rf2425_ini_common_end, change_channel);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5112_ini_bbgain),
+					rf5112_ini_bbgain, change_channel);
+			break;
+		default:
+			return -EINVAL;
+
+		}
+
+	/* For AR5211 */
+	} else if (ah->ah_version == AR5K_AR5211) {
+
+		/* AR5K_MODE_11B */
+		if (mode > 2) {
+			DBG("ath5k: unsupported channel mode %d\n", mode);
+			return -EINVAL;
+		}
+
+		/* Mode-specific settings */
+		ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(ar5211_ini_mode),
+				ar5211_ini_mode, mode);
+
+		/*
+		 * Write initial settings common for all modes
+		 */
+		ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5211_ini),
+				ar5211_ini, change_channel);
+
+		/* AR5211 only comes with 5111 */
+
+		/* Baseband gain table */
+		ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5111_ini_bbgain),
+				rf5111_ini_bbgain, change_channel);
+	/* For AR5210 (for mode settings check out ath5k_hw_reset_tx_queue) */
+	} else if (ah->ah_version == AR5K_AR5210) {
+		ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5210_ini),
+				ar5210_ini, change_channel);
+	}
+
+	return 0;
+}
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_pcu.c b/gpxe/src/drivers/net/ath5k/ath5k_pcu.c
new file mode 100644
index 0000000..d3e144c
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_pcu.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ * Copyright (c) 2007-2008 Matthew W. S. Bell  <mentor at madwifi.org>
+ * Copyright (c) 2007-2008 Luis Rodriguez <mcgrof at winlab.rutgers.edu>
+ * Copyright (c) 2007-2008 Pavel Roskin <proski at gnu.org>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby at gmail.com>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+/*********************************\
+* Protocol Control Unit Functions *
+\*********************************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/*******************\
+* Generic functions *
+\*******************/
+
+/**
+ * ath5k_hw_set_opmode - Set PCU operating mode
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Initialize PCU for the various operating modes (AP/STA etc)
+ *
+ * For gPXE we always assume STA mode.
+ */
+int ath5k_hw_set_opmode(struct ath5k_hw *ah)
+{
+	u32 pcu_reg, beacon_reg, low_id, high_id;
+
+
+	/* Preserve rest settings */
+	pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000;
+	pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP
+			| AR5K_STA_ID1_KEYSRCH_MODE
+			| (ah->ah_version == AR5K_AR5210 ?
+			(AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0));
+
+	beacon_reg = 0;
+
+	pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE
+		| (ah->ah_version == AR5K_AR5210 ?
+		   AR5K_STA_ID1_PWR_SV : 0);
+
+	/*
+	 * Set PCU registers
+	 */
+	low_id = AR5K_LOW_ID(ah->ah_sta_id);
+	high_id = AR5K_HIGH_ID(ah->ah_sta_id);
+	ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
+	ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1);
+
+	/*
+	 * Set Beacon Control Register on 5210
+	 */
+	if (ah->ah_version == AR5K_AR5210)
+		ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR);
+
+	return 0;
+}
+
+/**
+ * ath5k_hw_set_ack_bitrate - set bitrate for ACKs
+ *
+ * @ah: The &struct ath5k_hw
+ * @high: Flag to determine if we want to use high transmition rate
+ * for ACKs or not
+ *
+ * If high flag is set, we tell hw to use a set of control rates based on
+ * the current transmition rate (check out control_rates array inside reset.c).
+ * If not hw just uses the lowest rate available for the current modulation
+ * scheme being used (1Mbit for CCK and 6Mbits for OFDM).
+ */
+void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, int high)
+{
+	if (ah->ah_version != AR5K_AR5212)
+		return;
+	else {
+		u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB;
+		if (high)
+			AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val);
+		else
+			AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val);
+	}
+}
+
+
+/******************\
+* ACK/CTS Timeouts *
+\******************/
+
+/**
+ * ath5k_hw_het_ack_timeout - Get ACK timeout from PCU in usec
+ *
+ * @ah: The &struct ath5k_hw
+ */
+unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah)
+{
+	return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah,
+			AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), ah->ah_turbo);
+}
+
+/**
+ * ath5k_hw_set_ack_timeout - Set ACK timeout on PCU
+ *
+ * @ah: The &struct ath5k_hw
+ * @timeout: Timeout in usec
+ */
+int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout)
+{
+	if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK),
+			ah->ah_turbo) <= timeout)
+		return -EINVAL;
+
+	AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK,
+		ath5k_hw_htoclock(timeout, ah->ah_turbo));
+
+	return 0;
+}
+
+/**
+ * ath5k_hw_get_cts_timeout - Get CTS timeout from PCU in usec
+ *
+ * @ah: The &struct ath5k_hw
+ */
+unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah)
+{
+	return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah,
+			AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), ah->ah_turbo);
+}
+
+/**
+ * ath5k_hw_set_cts_timeout - Set CTS timeout on PCU
+ *
+ * @ah: The &struct ath5k_hw
+ * @timeout: Timeout in usec
+ */
+int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout)
+{
+	if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS),
+			ah->ah_turbo) <= timeout)
+		return -EINVAL;
+
+	AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS,
+			ath5k_hw_htoclock(timeout, ah->ah_turbo));
+
+	return 0;
+}
+
+
+/****************\
+* BSSID handling *
+\****************/
+
+/**
+ * ath5k_hw_get_lladdr - Get station id
+ *
+ * @ah: The &struct ath5k_hw
+ * @mac: The card's mac address
+ *
+ * Initialize ah->ah_sta_id using the mac address provided
+ * (just a memcpy).
+ *
+ * TODO: Remove it once we merge ath5k_softc and ath5k_hw
+ */
+void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac)
+{
+	memcpy(mac, ah->ah_sta_id, ETH_ALEN);
+}
+
+/**
+ * ath5k_hw_set_lladdr - Set station id
+ *
+ * @ah: The &struct ath5k_hw
+ * @mac: The card's mac address
+ *
+ * Set station id on hw using the provided mac address
+ */
+int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac)
+{
+	u32 low_id, high_id;
+	u32 pcu_reg;
+
+	/* Set new station ID */
+	memcpy(ah->ah_sta_id, mac, ETH_ALEN);
+
+	pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000;
+
+	low_id = AR5K_LOW_ID(mac);
+	high_id = AR5K_HIGH_ID(mac);
+
+	ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
+	ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1);
+
+	return 0;
+}
+
+/**
+ * ath5k_hw_set_associd - Set BSSID for association
+ *
+ * @ah: The &struct ath5k_hw
+ * @bssid: BSSID
+ * @assoc_id: Assoc id
+ *
+ * Sets the BSSID which trigers the "SME Join" operation
+ */
+void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id)
+{
+	u32 low_id, high_id;
+
+	/*
+	 * Set simple BSSID mask on 5212
+	 */
+	if (ah->ah_version == AR5K_AR5212) {
+		ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask),
+							AR5K_BSS_IDM0);
+		ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask),
+							AR5K_BSS_IDM1);
+	}
+
+	/*
+	 * Set BSSID which triggers the "SME Join" operation
+	 */
+	low_id = AR5K_LOW_ID(bssid);
+	high_id = AR5K_HIGH_ID(bssid);
+	ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0);
+	ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) <<
+				AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1);
+}
+
+/**
+ * ath5k_hw_set_bssid_mask - filter out bssids we listen
+ *
+ * @ah: the &struct ath5k_hw
+ * @mask: the bssid_mask, a u8 array of size ETH_ALEN
+ *
+ * BSSID masking is a method used by AR5212 and newer hardware to inform PCU
+ * which bits of the interface's MAC address should be looked at when trying
+ * to decide which packets to ACK. In station mode and AP mode with a single
+ * BSS every bit matters since we lock to only one BSS. In AP mode with
+ * multiple BSSes (virtual interfaces) not every bit matters because hw must
+ * accept frames for all BSSes and so we tweak some bits of our mac address
+ * in order to have multiple BSSes.
+ *
+ * NOTE: This is a simple filter and does *not* filter out all
+ * relevant frames. Some frames that are not for us might get ACKed from us
+ * by PCU because they just match the mask.
+ *
+ * When handling multiple BSSes you can get the BSSID mask by computing the
+ * set of  ~ ( MAC XOR BSSID ) for all bssids we handle.
+ *
+ * When you do this you are essentially computing the common bits of all your
+ * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with
+ * the MAC address to obtain the relevant bits and compare the result with
+ * (frame's BSSID & mask) to see if they match.
+ */
+/*
+ * Simple example: on your card you have have two BSSes you have created with
+ * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address.
+ * There is another BSSID-03 but you are not part of it. For simplicity's sake,
+ * assuming only 4 bits for a mac address and for BSSIDs you can then have:
+ *
+ *                  \
+ * MAC:                0001 |
+ * BSSID-01:   0100 | --> Belongs to us
+ * BSSID-02:   1001 |
+ *                  /
+ * -------------------
+ * BSSID-03:   0110  | --> External
+ * -------------------
+ *
+ * Our bssid_mask would then be:
+ *
+ *             On loop iteration for BSSID-01:
+ *             ~(0001 ^ 0100)  -> ~(0101)
+ *                             ->   1010
+ *             bssid_mask      =    1010
+ *
+ *             On loop iteration for BSSID-02:
+ *             bssid_mask &= ~(0001   ^   1001)
+ *             bssid_mask =   (1010)  & ~(0001 ^ 1001)
+ *             bssid_mask =   (1010)  & ~(1001)
+ *             bssid_mask =   (1010)  &  (0110)
+ *             bssid_mask =   0010
+ *
+ * A bssid_mask of 0010 means "only pay attention to the second least
+ * significant bit". This is because its the only bit common
+ * amongst the MAC and all BSSIDs we support. To findout what the real
+ * common bit is we can simply "&" the bssid_mask now with any BSSID we have
+ * or our MAC address (we assume the hardware uses the MAC address).
+ *
+ * Now, suppose there's an incoming frame for BSSID-03:
+ *
+ * IFRAME-01:  0110
+ *
+ * An easy eye-inspeciton of this already should tell you that this frame
+ * will not pass our check. This is beacuse the bssid_mask tells the
+ * hardware to only look at the second least significant bit and the
+ * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB
+ * as 1, which does not match 0.
+ *
+ * So with IFRAME-01 we *assume* the hardware will do:
+ *
+ *     allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0;
+ *  --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0;
+ *  --> allow = (0010) == 0000 ? 1 : 0;
+ *  --> allow = 0
+ *
+ *  Lets now test a frame that should work:
+ *
+ * IFRAME-02:  0001 (we should allow)
+ *
+ *     allow = (0001 & 1010) == 1010
+ *
+ *     allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0;
+ *  --> allow = (0001 & 0010) ==  (0010 & 0001) ? 1 :0;
+ *  --> allow = (0010) == (0010)
+ *  --> allow = 1
+ *
+ * Other examples:
+ *
+ * IFRAME-03:  0100 --> allowed
+ * IFRAME-04:  1001 --> allowed
+ * IFRAME-05:  1101 --> allowed but its not for us!!!
+ *
+ */
+int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask)
+{
+	u32 low_id, high_id;
+
+	/* Cache bssid mask so that we can restore it
+	 * on reset */
+	memcpy(ah->ah_bssid_mask, mask, ETH_ALEN);
+	if (ah->ah_version == AR5K_AR5212) {
+		low_id = AR5K_LOW_ID(mask);
+		high_id = AR5K_HIGH_ID(mask);
+
+		ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0);
+		ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1);
+
+		return 0;
+	}
+
+	return -EIO;
+}
+
+
+/************\
+* RX Control *
+\************/
+
+/**
+ * ath5k_hw_start_rx_pcu - Start RX engine
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Starts RX engine on PCU so that hw can process RXed frames
+ * (ACK etc).
+ *
+ * NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma
+ * TODO: Init ANI here
+ */
+void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah)
+{
+	AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
+}
+
+/**
+ * at5k_hw_stop_rx_pcu - Stop RX engine
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Stops RX engine on PCU
+ *
+ * TODO: Detach ANI here
+ */
+void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah)
+{
+	AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
+}
+
+/*
+ * Set multicast filter
+ */
+void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1)
+{
+	/* Set the multicat filter */
+	ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0);
+	ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1);
+}
+
+/**
+ * ath5k_hw_get_rx_filter - Get current rx filter
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Returns the RX filter by reading rx filter and
+ * phy error filter registers. RX filter is used
+ * to set the allowed frame types that PCU will accept
+ * and pass to the driver. For a list of frame types
+ * check out reg.h.
+ */
+u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah)
+{
+	u32 data, filter = 0;
+
+	filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER);
+
+	/*Radar detection for 5212*/
+	if (ah->ah_version == AR5K_AR5212) {
+		data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL);
+
+		if (data & AR5K_PHY_ERR_FIL_RADAR)
+			filter |= AR5K_RX_FILTER_RADARERR;
+		if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK))
+			filter |= AR5K_RX_FILTER_PHYERR;
+	}
+
+	return filter;
+}
+
+/**
+ * ath5k_hw_set_rx_filter - Set rx filter
+ *
+ * @ah: The &struct ath5k_hw
+ * @filter: RX filter mask (see reg.h)
+ *
+ * Sets RX filter register and also handles PHY error filter
+ * register on 5212 and newer chips so that we have proper PHY
+ * error reporting.
+ */
+void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter)
+{
+	u32 data = 0;
+
+	/* Set PHY error filter register on 5212*/
+	if (ah->ah_version == AR5K_AR5212) {
+		if (filter & AR5K_RX_FILTER_RADARERR)
+			data |= AR5K_PHY_ERR_FIL_RADAR;
+		if (filter & AR5K_RX_FILTER_PHYERR)
+			data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK;
+	}
+
+	/*
+	 * The AR5210 uses promiscous mode to detect radar activity
+	 */
+	if (ah->ah_version == AR5K_AR5210 &&
+			(filter & AR5K_RX_FILTER_RADARERR)) {
+		filter &= ~AR5K_RX_FILTER_RADARERR;
+		filter |= AR5K_RX_FILTER_PROM;
+	}
+
+	/*Zero length DMA (phy error reporting) */
+	if (data)
+		AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
+	else
+		AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
+
+	/*Write RX Filter register*/
+	ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER);
+
+	/*Write PHY error filter register on 5212*/
+	if (ah->ah_version == AR5K_AR5212)
+		ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL);
+
+}
+
+/*********************\
+* Key table functions *
+\*********************/
+
+/*
+ * Reset a key entry on the table
+ */
+int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry)
+{
+	unsigned int i, type;
+	u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET;
+
+	type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry));
+
+	for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
+		ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i));
+
+	/* Reset associated MIC entry if TKIP
+	 * is enabled located at offset (entry + 64) */
+	if (type == AR5K_KEYTABLE_TYPE_TKIP) {
+		for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++)
+			ath5k_hw_reg_write(ah, 0,
+				AR5K_KEYTABLE_OFF(micentry, i));
+	}
+
+	/*
+	 * Set NULL encryption on AR5212+
+	 *
+	 * Note: AR5K_KEYTABLE_TYPE -> AR5K_KEYTABLE_OFF(entry, 5)
+	 *       AR5K_KEYTABLE_TYPE_NULL -> 0x00000007
+	 *
+	 * Note2: Windows driver (ndiswrapper) sets this to
+	 *        0x00000714 instead of 0x00000007
+	 */
+	if (ah->ah_version >= AR5K_AR5211) {
+		ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
+				AR5K_KEYTABLE_TYPE(entry));
+
+		if (type == AR5K_KEYTABLE_TYPE_TKIP) {
+			ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
+				AR5K_KEYTABLE_TYPE(micentry));
+		}
+	}
+
+	return 0;
+}
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_phy.c b/gpxe/src/drivers/net/ath5k/ath5k_phy.c
new file mode 100644
index 0000000..8856fa3
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_phy.c
@@ -0,0 +1,2586 @@
+/*
+ * PHY functions
+ *
+ * Copyright (c) 2004-2007 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm at gmail.com>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby at gmail.com>
+ * Copyright (c) 2008-2009 Felix Fietkau <nbd at openwrt.org>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+#define _ATH5K_PHY
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+#include "rfbuffer.h"
+#include "rfgain.h"
+
+static inline int min(int x, int y)
+{
+	return (x < y) ? x : y;
+}
+
+static inline int max(int x, int y)
+{
+	return (x > y) ? x : y;
+}
+
+/*
+ * Used to modify RF Banks before writing them to AR5K_RF_BUFFER
+ */
+static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah,
+					const struct ath5k_rf_reg *rf_regs,
+					u32 val, u8 reg_id, int set)
+{
+	const struct ath5k_rf_reg *rfreg = NULL;
+	u8 offset, bank, num_bits, col, position;
+	u16 entry;
+	u32 mask, data, last_bit, bits_shifted, first_bit;
+	u32 *rfb;
+	s32 bits_left;
+	unsigned i;
+
+	data = 0;
+	rfb = ah->ah_rf_banks;
+
+	for (i = 0; i < ah->ah_rf_regs_count; i++) {
+		if (rf_regs[i].index == reg_id) {
+			rfreg = &rf_regs[i];
+			break;
+		}
+	}
+
+	if (rfb == NULL || rfreg == NULL) {
+		DBG("ath5k: RF register not found!\n");
+		/* should not happen */
+		return 0;
+	}
+
+	bank = rfreg->bank;
+	num_bits = rfreg->field.len;
+	first_bit = rfreg->field.pos;
+	col = rfreg->field.col;
+
+	/* first_bit is an offset from bank's
+	 * start. Since we have all banks on
+	 * the same array, we use this offset
+	 * to mark each bank's start */
+	offset = ah->ah_offset[bank];
+
+	/* Boundary check */
+	if (!(col <= 3 && num_bits <= 32 && first_bit + num_bits <= 319)) {
+		DBG("ath5k: RF invalid values at offset %d\n", offset);
+		return 0;
+	}
+
+	entry = ((first_bit - 1) / 8) + offset;
+	position = (first_bit - 1) % 8;
+
+	if (set)
+		data = ath5k_hw_bitswap(val, num_bits);
+
+	for (bits_shifted = 0, bits_left = num_bits; bits_left > 0;
+	position = 0, entry++) {
+
+		last_bit = (position + bits_left > 8) ? 8 :
+					position + bits_left;
+
+		mask = (((1 << last_bit) - 1) ^ ((1 << position) - 1)) <<
+								(col * 8);
+
+		if (set) {
+			rfb[entry] &= ~mask;
+			rfb[entry] |= ((data << position) << (col * 8)) & mask;
+			data >>= (8 - position);
+		} else {
+			data |= (((rfb[entry] & mask) >> (col * 8)) >> position)
+				<< bits_shifted;
+			bits_shifted += last_bit - position;
+		}
+
+		bits_left -= 8 - position;
+	}
+
+	data = set ? 1 : ath5k_hw_bitswap(data, num_bits);
+
+	return data;
+}
+
+/**********************\
+* RF Gain optimization *
+\**********************/
+
+/*
+ * This code is used to optimize rf gain on different environments
+ * (temprature mostly) based on feedback from a power detector.
+ *
+ * It's only used on RF5111 and RF5112, later RF chips seem to have
+ * auto adjustment on hw -notice they have a much smaller BANK 7 and
+ * no gain optimization ladder-.
+ *
+ * For more infos check out this patent doc
+ * http://www.freepatentsonline.com/7400691.html
+ *
+ * This paper describes power drops as seen on the receiver due to
+ * probe packets
+ * http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues
+ * %20of%20Power%20Control.pdf
+ *
+ * And this is the MadWiFi bug entry related to the above
+ * http://madwifi-project.org/ticket/1659
+ * with various measurements and diagrams
+ *
+ * TODO: Deal with power drops due to probes by setting an apropriate
+ * tx power on the probe packets ! Make this part of the calibration process.
+ */
+
+/* Initialize ah_gain durring attach */
+int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah)
+{
+	/* Initialize the gain optimization values */
+	switch (ah->ah_radio) {
+	case AR5K_RF5111:
+		ah->ah_gain.g_step_idx = rfgain_opt_5111.go_default;
+		ah->ah_gain.g_low = 20;
+		ah->ah_gain.g_high = 35;
+		ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+		break;
+	case AR5K_RF5112:
+		ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default;
+		ah->ah_gain.g_low = 20;
+		ah->ah_gain.g_high = 85;
+		ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Schedule a gain probe check on the next transmited packet.
+ * That means our next packet is going to be sent with lower
+ * tx power and a Peak to Average Power Detector (PAPD) will try
+ * to measure the gain.
+ *
+ * TODO: Use propper tx power setting for the probe packet so
+ * that we don't observe a serious power drop on the receiver
+ *
+ * XXX:  How about forcing a tx packet (bypassing PCU arbitrator etc)
+ * just after we enable the probe so that we don't mess with
+ * standard traffic ? Maybe it's time to use sw interrupts and
+ * a probe tasklet !!!
+ */
+static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah)
+{
+
+	/* Skip if gain calibration is inactive or
+	 * we already handle a probe request */
+	if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE)
+		return;
+
+	/* Send the packet with 2dB below max power as
+	 * patent doc suggest */
+	ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max_pwr - 4,
+			AR5K_PHY_PAPD_PROBE_TXPOWER) |
+			AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE);
+
+	ah->ah_gain.g_state = AR5K_RFGAIN_READ_REQUESTED;
+
+}
+
+/* Calculate gain_F measurement correction
+ * based on the current step for RF5112 rev. 2 */
+static u32 ath5k_hw_rf_gainf_corr(struct ath5k_hw *ah)
+{
+	u32 mix, step;
+	u32 *rf;
+	const struct ath5k_gain_opt *go;
+	const struct ath5k_gain_opt_step *g_step;
+	const struct ath5k_rf_reg *rf_regs;
+
+	/* Only RF5112 Rev. 2 supports it */
+	if ((ah->ah_radio != AR5K_RF5112) ||
+	(ah->ah_radio_5ghz_revision <= AR5K_SREV_RAD_5112A))
+		return 0;
+
+	go = &rfgain_opt_5112;
+	rf_regs = rf_regs_5112a;
+	ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a);
+
+	g_step = &go->go_step[ah->ah_gain.g_step_idx];
+
+	if (ah->ah_rf_banks == NULL)
+		return 0;
+
+	rf = ah->ah_rf_banks;
+	ah->ah_gain.g_f_corr = 0;
+
+	/* No VGA (Variable Gain Amplifier) override, skip */
+	if (ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, 0) != 1)
+		return 0;
+
+	/* Mix gain stepping */
+	step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXGAIN_STEP, 0);
+
+	/* Mix gain override */
+	mix = g_step->gos_param[0];
+
+	switch (mix) {
+	case 3:
+		ah->ah_gain.g_f_corr = step * 2;
+		break;
+	case 2:
+		ah->ah_gain.g_f_corr = (step - 5) * 2;
+		break;
+	case 1:
+		ah->ah_gain.g_f_corr = step;
+		break;
+	default:
+		ah->ah_gain.g_f_corr = 0;
+		break;
+	}
+
+	return ah->ah_gain.g_f_corr;
+}
+
+/* Check if current gain_F measurement is in the range of our
+ * power detector windows. If we get a measurement outside range
+ * we know it's not accurate (detectors can't measure anything outside
+ * their detection window) so we must ignore it */
+static int ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah)
+{
+	const struct ath5k_rf_reg *rf_regs;
+	u32 step, mix_ovr, level[4];
+	u32 *rf;
+
+	if (ah->ah_rf_banks == NULL)
+		return 0;
+
+	rf = ah->ah_rf_banks;
+
+	if (ah->ah_radio == AR5K_RF5111) {
+
+		rf_regs = rf_regs_5111;
+		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111);
+
+		step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_RFGAIN_STEP,
+			0);
+
+		level[0] = 0;
+		level[1] = (step == 63) ? 50 : step + 4;
+		level[2] = (step != 63) ? 64 : level[0];
+		level[3] = level[2] + 50 ;
+
+		ah->ah_gain.g_high = level[3] -
+			(step == 63 ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5);
+		ah->ah_gain.g_low = level[0] +
+			(step == 63 ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0);
+	} else {
+
+		rf_regs = rf_regs_5112;
+		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112);
+
+		mix_ovr = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR,
+			0);
+
+		level[0] = level[2] = 0;
+
+		if (mix_ovr == 1) {
+			level[1] = level[3] = 83;
+		} else {
+			level[1] = level[3] = 107;
+			ah->ah_gain.g_high = 55;
+		}
+	}
+
+	return (ah->ah_gain.g_current >= level[0] &&
+			ah->ah_gain.g_current <= level[1]) ||
+		(ah->ah_gain.g_current >= level[2] &&
+			ah->ah_gain.g_current <= level[3]);
+}
+
+/* Perform gain_F adjustment by choosing the right set
+ * of parameters from rf gain optimization ladder */
+static s8 ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah)
+{
+	const struct ath5k_gain_opt *go;
+	const struct ath5k_gain_opt_step *g_step;
+	int ret = 0;
+
+	switch (ah->ah_radio) {
+	case AR5K_RF5111:
+		go = &rfgain_opt_5111;
+		break;
+	case AR5K_RF5112:
+		go = &rfgain_opt_5112;
+		break;
+	default:
+		return 0;
+	}
+
+	g_step = &go->go_step[ah->ah_gain.g_step_idx];
+
+	if (ah->ah_gain.g_current >= ah->ah_gain.g_high) {
+
+		/* Reached maximum */
+		if (ah->ah_gain.g_step_idx == 0)
+			return -1;
+
+		for (ah->ah_gain.g_target = ah->ah_gain.g_current;
+				ah->ah_gain.g_target >=  ah->ah_gain.g_high &&
+				ah->ah_gain.g_step_idx > 0;
+				g_step = &go->go_step[ah->ah_gain.g_step_idx])
+			ah->ah_gain.g_target -= 2 *
+			    (go->go_step[--(ah->ah_gain.g_step_idx)].gos_gain -
+			    g_step->gos_gain);
+
+		ret = 1;
+		goto done;
+	}
+
+	if (ah->ah_gain.g_current <= ah->ah_gain.g_low) {
+
+		/* Reached minimum */
+		if (ah->ah_gain.g_step_idx == (go->go_steps_count - 1))
+			return -2;
+
+		for (ah->ah_gain.g_target = ah->ah_gain.g_current;
+				ah->ah_gain.g_target <= ah->ah_gain.g_low &&
+				ah->ah_gain.g_step_idx < go->go_steps_count-1;
+				g_step = &go->go_step[ah->ah_gain.g_step_idx])
+			ah->ah_gain.g_target -= 2 *
+			    (go->go_step[++ah->ah_gain.g_step_idx].gos_gain -
+			    g_step->gos_gain);
+
+		ret = 2;
+		goto done;
+	}
+
+done:
+	DBG2("ath5k RF adjust: ret %d, gain step %d, current gain %d, "
+	     "target gain %d\n", ret, ah->ah_gain.g_step_idx,
+	     ah->ah_gain.g_current, ah->ah_gain.g_target);
+
+	return ret;
+}
+
+/* Main callback for thermal rf gain calibration engine
+ * Check for a new gain reading and schedule an adjustment
+ * if needed.
+ *
+ * TODO: Use sw interrupt to schedule reset if gain_F needs
+ * adjustment */
+enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah)
+{
+	u32 data, type;
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+
+	if (ah->ah_rf_banks == NULL ||
+	ah->ah_gain.g_state == AR5K_RFGAIN_INACTIVE)
+		return AR5K_RFGAIN_INACTIVE;
+
+	/* No check requested, either engine is inactive
+	 * or an adjustment is already requested */
+	if (ah->ah_gain.g_state != AR5K_RFGAIN_READ_REQUESTED)
+		goto done;
+
+	/* Read the PAPD (Peak to Average Power Detector)
+	 * register */
+	data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE);
+
+	/* No probe is scheduled, read gain_F measurement */
+	if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) {
+		ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S;
+		type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE);
+
+		/* If tx packet is CCK correct the gain_F measurement
+		 * by cck ofdm gain delta */
+		if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) {
+			if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A)
+				ah->ah_gain.g_current +=
+					ee->ee_cck_ofdm_gain_delta;
+			else
+				ah->ah_gain.g_current +=
+					AR5K_GAIN_CCK_PROBE_CORR;
+		}
+
+		/* Further correct gain_F measurement for
+		 * RF5112A radios */
+		if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
+			ath5k_hw_rf_gainf_corr(ah);
+			ah->ah_gain.g_current =
+				ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ?
+				(ah->ah_gain.g_current-ah->ah_gain.g_f_corr) :
+				0;
+		}
+
+		/* Check if measurement is ok and if we need
+		 * to adjust gain, schedule a gain adjustment,
+		 * else switch back to the acive state */
+		if (ath5k_hw_rf_check_gainf_readback(ah) &&
+		AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) &&
+		ath5k_hw_rf_gainf_adjust(ah)) {
+			ah->ah_gain.g_state = AR5K_RFGAIN_NEED_CHANGE;
+		} else {
+			ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+		}
+	}
+
+done:
+	return ah->ah_gain.g_state;
+}
+
+/* Write initial rf gain table to set the RF sensitivity
+ * this one works on all RF chips and has nothing to do
+ * with gain_F calibration */
+int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq)
+{
+	const struct ath5k_ini_rfgain *ath5k_rfg;
+	unsigned int i, size;
+
+	switch (ah->ah_radio) {
+	case AR5K_RF5111:
+		ath5k_rfg = rfgain_5111;
+		size = ARRAY_SIZE(rfgain_5111);
+		break;
+	case AR5K_RF5112:
+		ath5k_rfg = rfgain_5112;
+		size = ARRAY_SIZE(rfgain_5112);
+		break;
+	case AR5K_RF2413:
+		ath5k_rfg = rfgain_2413;
+		size = ARRAY_SIZE(rfgain_2413);
+		break;
+	case AR5K_RF2316:
+		ath5k_rfg = rfgain_2316;
+		size = ARRAY_SIZE(rfgain_2316);
+		break;
+	case AR5K_RF5413:
+		ath5k_rfg = rfgain_5413;
+		size = ARRAY_SIZE(rfgain_5413);
+		break;
+	case AR5K_RF2317:
+	case AR5K_RF2425:
+		ath5k_rfg = rfgain_2425;
+		size = ARRAY_SIZE(rfgain_2425);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (freq) {
+	case AR5K_INI_RFGAIN_2GHZ:
+	case AR5K_INI_RFGAIN_5GHZ:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < size; i++) {
+		AR5K_REG_WAIT(i);
+		ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[freq],
+			(u32)ath5k_rfg[i].rfg_register);
+	}
+
+	return 0;
+}
+
+
+
+/********************\
+* RF Registers setup *
+\********************/
+
+
+/*
+ * Setup RF registers by writing rf buffer on hw
+ */
+int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct net80211_channel *channel,
+		unsigned int mode)
+{
+	const struct ath5k_rf_reg *rf_regs;
+	const struct ath5k_ini_rfbuffer *ini_rfb;
+	const struct ath5k_gain_opt *go = NULL;
+	const struct ath5k_gain_opt_step *g_step;
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	u8 ee_mode = 0;
+	u32 *rfb;
+	int obdb = -1, bank = -1;
+	unsigned i;
+
+	switch (ah->ah_radio) {
+	case AR5K_RF5111:
+		rf_regs = rf_regs_5111;
+		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111);
+		ini_rfb = rfb_5111;
+		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5111);
+		go = &rfgain_opt_5111;
+		break;
+	case AR5K_RF5112:
+		if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
+			rf_regs = rf_regs_5112a;
+			ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a);
+			ini_rfb = rfb_5112a;
+			ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112a);
+		} else {
+			rf_regs = rf_regs_5112;
+			ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112);
+			ini_rfb = rfb_5112;
+			ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112);
+		}
+		go = &rfgain_opt_5112;
+		break;
+	case AR5K_RF2413:
+		rf_regs = rf_regs_2413;
+		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2413);
+		ini_rfb = rfb_2413;
+		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2413);
+		break;
+	case AR5K_RF2316:
+		rf_regs = rf_regs_2316;
+		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2316);
+		ini_rfb = rfb_2316;
+		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2316);
+		break;
+	case AR5K_RF5413:
+		rf_regs = rf_regs_5413;
+		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5413);
+		ini_rfb = rfb_5413;
+		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5413);
+		break;
+	case AR5K_RF2317:
+		rf_regs = rf_regs_2425;
+		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425);
+		ini_rfb = rfb_2317;
+		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2317);
+		break;
+	case AR5K_RF2425:
+		rf_regs = rf_regs_2425;
+		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425);
+		if (ah->ah_mac_srev < AR5K_SREV_AR2417) {
+			ini_rfb = rfb_2425;
+			ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2425);
+		} else {
+			ini_rfb = rfb_2417;
+			ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2417);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* If it's the first time we set rf buffer, allocate
+	 * ah->ah_rf_banks based on ah->ah_rf_banks_size
+	 * we set above */
+	if (ah->ah_rf_banks == NULL) {
+		ah->ah_rf_banks = malloc(sizeof(u32) * ah->ah_rf_banks_size);
+		if (ah->ah_rf_banks == NULL) {
+			return -ENOMEM;
+		}
+	}
+
+	/* Copy values to modify them */
+	rfb = ah->ah_rf_banks;
+
+	for (i = 0; i < ah->ah_rf_banks_size; i++) {
+		if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) {
+			DBG("ath5k: invalid RF register bank\n");
+			return -EINVAL;
+		}
+
+		/* Bank changed, write down the offset */
+		if (bank != ini_rfb[i].rfb_bank) {
+			bank = ini_rfb[i].rfb_bank;
+			ah->ah_offset[bank] = i;
+		}
+
+		rfb[i] = ini_rfb[i].rfb_mode_data[mode];
+	}
+
+	/* Set Output and Driver bias current (OB/DB) */
+	if (channel->hw_value & CHANNEL_2GHZ) {
+
+		if (channel->hw_value & CHANNEL_CCK)
+			ee_mode = AR5K_EEPROM_MODE_11B;
+		else
+			ee_mode = AR5K_EEPROM_MODE_11G;
+
+		/* For RF511X/RF211X combination we
+		 * use b_OB and b_DB parameters stored
+		 * in eeprom on ee->ee_ob[ee_mode][0]
+		 *
+		 * For all other chips we use OB/DB for 2Ghz
+		 * stored in the b/g modal section just like
+		 * 802.11a on ee->ee_ob[ee_mode][1] */
+		if ((ah->ah_radio == AR5K_RF5111) ||
+		(ah->ah_radio == AR5K_RF5112))
+			obdb = 0;
+		else
+			obdb = 1;
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb],
+						AR5K_RF_OB_2GHZ, 1);
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb],
+						AR5K_RF_DB_2GHZ, 1);
+
+	/* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */
+	} else if ((channel->hw_value & CHANNEL_5GHZ) ||
+			(ah->ah_radio == AR5K_RF5111)) {
+
+		/* For 11a, Turbo and XR we need to choose
+		 * OB/DB based on frequency range */
+		ee_mode = AR5K_EEPROM_MODE_11A;
+		obdb =	 channel->center_freq >= 5725 ? 3 :
+			(channel->center_freq >= 5500 ? 2 :
+			(channel->center_freq >= 5260 ? 1 :
+			 (channel->center_freq > 4000 ? 0 : -1)));
+
+		if (obdb < 0)
+			return -EINVAL;
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb],
+						AR5K_RF_OB_5GHZ, 1);
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb],
+						AR5K_RF_DB_5GHZ, 1);
+	}
+
+	g_step = &go->go_step[ah->ah_gain.g_step_idx];
+
+	/* Bank Modifications (chip-specific) */
+	if (ah->ah_radio == AR5K_RF5111) {
+
+		/* Set gain_F settings according to current step */
+		if (channel->hw_value & CHANNEL_OFDM) {
+
+			AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL,
+					AR5K_PHY_FRAME_CTL_TX_CLIP,
+					g_step->gos_param[0]);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1],
+							AR5K_RF_PWD_90, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2],
+							AR5K_RF_PWD_84, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3],
+						AR5K_RF_RFGAIN_SEL, 1);
+
+			/* We programmed gain_F parameters, switch back
+			 * to active state */
+			ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+
+		}
+
+		/* Bank 6/7 setup */
+
+		ath5k_hw_rfb_op(ah, rf_regs, !ee->ee_xpd[ee_mode],
+						AR5K_RF_PWD_XPD, 1);
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_x_gain[ee_mode],
+						AR5K_RF_XPD_GAIN, 1);
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
+						AR5K_RF_GAIN_I, 1);
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
+						AR5K_RF_PLO_SEL, 1);
+
+		/* TODO: Half/quarter channel support */
+	}
+
+	if (ah->ah_radio == AR5K_RF5112) {
+
+		/* Set gain_F settings according to current step */
+		if (channel->hw_value & CHANNEL_OFDM) {
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0],
+						AR5K_RF_MIXGAIN_OVR, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1],
+						AR5K_RF_PWD_138, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2],
+						AR5K_RF_PWD_137, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3],
+						AR5K_RF_PWD_136, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[4],
+						AR5K_RF_PWD_132, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[5],
+						AR5K_RF_PWD_131, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[6],
+						AR5K_RF_PWD_130, 1);
+
+			/* We programmed gain_F parameters, switch back
+			 * to active state */
+			ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
+		}
+
+		/* Bank 6/7 setup */
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
+						AR5K_RF_XPD_SEL, 1);
+
+		if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) {
+			/* Rev. 1 supports only one xpd */
+			ath5k_hw_rfb_op(ah, rf_regs,
+						ee->ee_x_gain[ee_mode],
+						AR5K_RF_XPD_GAIN, 1);
+
+		} else {
+			/* TODO: Set high and low gain bits */
+			ath5k_hw_rfb_op(ah, rf_regs,
+						ee->ee_x_gain[ee_mode],
+						AR5K_RF_PD_GAIN_LO, 1);
+			ath5k_hw_rfb_op(ah, rf_regs,
+						ee->ee_x_gain[ee_mode],
+						AR5K_RF_PD_GAIN_HI, 1);
+
+			/* Lower synth voltage on Rev 2 */
+			ath5k_hw_rfb_op(ah, rf_regs, 2,
+					AR5K_RF_HIGH_VC_CP, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, 2,
+					AR5K_RF_MID_VC_CP, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, 2,
+					AR5K_RF_LOW_VC_CP, 1);
+
+			ath5k_hw_rfb_op(ah, rf_regs, 2,
+					AR5K_RF_PUSH_UP, 1);
+
+			/* Decrease power consumption on 5213+ BaseBand */
+			if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
+				ath5k_hw_rfb_op(ah, rf_regs, 1,
+						AR5K_RF_PAD2GND, 1);
+
+				ath5k_hw_rfb_op(ah, rf_regs, 1,
+						AR5K_RF_XB2_LVL, 1);
+
+				ath5k_hw_rfb_op(ah, rf_regs, 1,
+						AR5K_RF_XB5_LVL, 1);
+
+				ath5k_hw_rfb_op(ah, rf_regs, 1,
+						AR5K_RF_PWD_167, 1);
+
+				ath5k_hw_rfb_op(ah, rf_regs, 1,
+						AR5K_RF_PWD_166, 1);
+			}
+		}
+
+		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
+						AR5K_RF_GAIN_I, 1);
+
+		/* TODO: Half/quarter channel support */
+
+	}
+
+	if (ah->ah_radio == AR5K_RF5413 &&
+	channel->hw_value & CHANNEL_2GHZ) {
+
+		ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE,
+									1);
+
+		/* Set optimum value for early revisions (on pci-e chips) */
+		if (ah->ah_mac_srev >= AR5K_SREV_AR5424 &&
+		ah->ah_mac_srev < AR5K_SREV_AR5413)
+			ath5k_hw_rfb_op(ah, rf_regs, ath5k_hw_bitswap(6, 3),
+						AR5K_RF_PWD_ICLOBUF_2G, 1);
+
+	}
+
+	/* Write RF banks on hw */
+	for (i = 0; i < ah->ah_rf_banks_size; i++) {
+		AR5K_REG_WAIT(i);
+		ath5k_hw_reg_write(ah, rfb[i], ini_rfb[i].rfb_ctrl_register);
+	}
+
+	return 0;
+}
+
+
+/**************************\
+  PHY/RF channel functions
+\**************************/
+
+/*
+ * Check if a channel is supported
+ */
+int ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags)
+{
+	/* Check if the channel is in our supported range */
+	if (flags & CHANNEL_2GHZ) {
+		if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) &&
+		    (freq <= ah->ah_capabilities.cap_range.range_2ghz_max))
+			return 1;
+	} else if (flags & CHANNEL_5GHZ)
+		if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) &&
+		    (freq <= ah->ah_capabilities.cap_range.range_5ghz_max))
+			return 1;
+
+	return 0;
+}
+
+/*
+ * Convertion needed for RF5110
+ */
+static u32 ath5k_hw_rf5110_chan2athchan(struct net80211_channel *channel)
+{
+	u32 athchan;
+
+	/*
+	 * Convert IEEE channel/MHz to an internal channel value used
+	 * by the AR5210 chipset. This has not been verified with
+	 * newer chipsets like the AR5212A who have a completely
+	 * different RF/PHY part.
+	 */
+	athchan = (ath5k_hw_bitswap((ath5k_freq_to_channel(channel->center_freq)
+				     - 24) / 2, 5) << 1)
+		| (1 << 6) | 0x1;
+	return athchan;
+}
+
+/*
+ * Set channel on RF5110
+ */
+static int ath5k_hw_rf5110_channel(struct ath5k_hw *ah,
+		struct net80211_channel *channel)
+{
+	u32 data;
+
+	/*
+	 * Set the channel and wait
+	 */
+	data = ath5k_hw_rf5110_chan2athchan(channel);
+	ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER);
+	ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0);
+	mdelay(1);
+
+	return 0;
+}
+
+/*
+ * Convertion needed for 5111
+ */
+static int ath5k_hw_rf5111_chan2athchan(unsigned int ieee,
+		struct ath5k_athchan_2ghz *athchan)
+{
+	int channel;
+
+	/* Cast this value to catch negative channel numbers (>= -19) */
+	channel = (int)ieee;
+
+	/*
+	 * Map 2GHz IEEE channel to 5GHz Atheros channel
+	 */
+	if (channel <= 13) {
+		athchan->a2_athchan = 115 + channel;
+		athchan->a2_flags = 0x46;
+	} else if (channel == 14) {
+		athchan->a2_athchan = 124;
+		athchan->a2_flags = 0x44;
+	} else if (channel >= 15 && channel <= 26) {
+		athchan->a2_athchan = ((channel - 14) * 4) + 132;
+		athchan->a2_flags = 0x46;
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+/*
+ * Set channel on 5111
+ */
+static int ath5k_hw_rf5111_channel(struct ath5k_hw *ah,
+		struct net80211_channel *channel)
+{
+	struct ath5k_athchan_2ghz ath5k_channel_2ghz;
+	unsigned int ath5k_channel = ath5k_freq_to_channel(channel->center_freq);
+	u32 data0, data1, clock;
+	int ret;
+
+	/*
+	 * Set the channel on the RF5111 radio
+	 */
+	data0 = data1 = 0;
+
+	if (channel->hw_value & CHANNEL_2GHZ) {
+		/* Map 2GHz channel to 5GHz Atheros channel ID */
+		ret = ath5k_hw_rf5111_chan2athchan(ath5k_channel,
+						   &ath5k_channel_2ghz);
+		if (ret)
+			return ret;
+
+		ath5k_channel = ath5k_channel_2ghz.a2_athchan;
+		data0 = ((ath5k_hw_bitswap(ath5k_channel_2ghz.a2_flags, 8) & 0xff)
+		    << 5) | (1 << 4);
+	}
+
+	if (ath5k_channel < 145 || !(ath5k_channel & 1)) {
+		clock = 1;
+		data1 = ((ath5k_hw_bitswap(ath5k_channel - 24, 8) & 0xff) << 2) |
+			(clock << 1) | (1 << 10) | 1;
+	} else {
+		clock = 0;
+		data1 = ((ath5k_hw_bitswap((ath5k_channel - 24) / 2, 8) & 0xff)
+			<< 2) | (clock << 1) | (1 << 10) | 1;
+	}
+
+	ath5k_hw_reg_write(ah, (data1 & 0xff) | ((data0 & 0xff) << 8),
+			AR5K_RF_BUFFER);
+	ath5k_hw_reg_write(ah, ((data1 >> 8) & 0xff) | (data0 & 0xff00),
+			AR5K_RF_BUFFER_CONTROL_3);
+
+	return 0;
+}
+
+/*
+ * Set channel on 5112 and newer
+ */
+static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah,
+		struct net80211_channel *channel)
+{
+	u32 data, data0, data1, data2;
+	u16 c;
+
+	data = data0 = data1 = data2 = 0;
+	c = channel->center_freq;
+
+	if (c < 4800) {
+		if (!((c - 2224) % 5)) {
+			data0 = ((2 * (c - 704)) - 3040) / 10;
+			data1 = 1;
+		} else if (!((c - 2192) % 5)) {
+			data0 = ((2 * (c - 672)) - 3040) / 10;
+			data1 = 0;
+		} else
+			return -EINVAL;
+
+		data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8);
+	} else if ((c - (c % 5)) != 2 || c > 5435) {
+		if (!(c % 20) && c >= 5120) {
+			data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
+			data2 = ath5k_hw_bitswap(3, 2);
+		} else if (!(c % 10)) {
+			data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
+			data2 = ath5k_hw_bitswap(2, 2);
+		} else if (!(c % 5)) {
+			data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
+			data2 = ath5k_hw_bitswap(1, 2);
+		} else
+			return -EINVAL;
+	} else {
+		data0 = ath5k_hw_bitswap((10 * (c - 2) - 4800) / 25 + 1, 8);
+		data2 = ath5k_hw_bitswap(0, 2);
+	}
+
+	data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001;
+
+	ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER);
+	ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);
+
+	return 0;
+}
+
+/*
+ * Set the channel on the RF2425
+ */
+static int ath5k_hw_rf2425_channel(struct ath5k_hw *ah,
+		struct net80211_channel *channel)
+{
+	u32 data, data0, data2;
+	u16 c;
+
+	data = data0 = data2 = 0;
+	c = channel->center_freq;
+
+	if (c < 4800) {
+		data0 = ath5k_hw_bitswap((c - 2272), 8);
+		data2 = 0;
+	/* ? 5GHz ? */
+	} else if ((c - (c % 5)) != 2 || c > 5435) {
+		if (!(c % 20) && c < 5120)
+			data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
+		else if (!(c % 10))
+			data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
+		else if (!(c % 5))
+			data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
+		else
+			return -EINVAL;
+		data2 = ath5k_hw_bitswap(1, 2);
+	} else {
+		data0 = ath5k_hw_bitswap((10 * (c - 2) - 4800) / 25 + 1, 8);
+		data2 = ath5k_hw_bitswap(0, 2);
+	}
+
+	data = (data0 << 4) | data2 << 2 | 0x1001;
+
+	ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER);
+	ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);
+
+	return 0;
+}
+
+/*
+ * Set a channel on the radio chip
+ */
+int ath5k_hw_channel(struct ath5k_hw *ah, struct net80211_channel *channel)
+{
+	int ret;
+	/*
+	 * Check bounds supported by the PHY (we don't care about regultory
+	 * restrictions at this point). Note: hw_value already has the band
+	 * (CHANNEL_2GHZ, or CHANNEL_5GHZ) so we inform ath5k_channel_ok()
+	 * of the band by that */
+	if (!ath5k_channel_ok(ah, channel->center_freq, channel->hw_value)) {
+		DBG("ath5k: channel frequency (%d MHz) out of supported "
+		    "range\n", channel->center_freq);
+		return -EINVAL;
+	}
+
+	/*
+	 * Set the channel and wait
+	 */
+	switch (ah->ah_radio) {
+	case AR5K_RF5110:
+		ret = ath5k_hw_rf5110_channel(ah, channel);
+		break;
+	case AR5K_RF5111:
+		ret = ath5k_hw_rf5111_channel(ah, channel);
+		break;
+	case AR5K_RF2425:
+		ret = ath5k_hw_rf2425_channel(ah, channel);
+		break;
+	default:
+		ret = ath5k_hw_rf5112_channel(ah, channel);
+		break;
+	}
+
+	if (ret) {
+		DBG("ath5k: setting channel failed: %s\n", strerror(ret));
+		return ret;
+	}
+
+	/* Set JAPAN setting for channel 14 */
+	if (channel->center_freq == 2484) {
+		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
+				AR5K_PHY_CCKTXCTL_JAPAN);
+	} else {
+		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
+				AR5K_PHY_CCKTXCTL_WORLD);
+	}
+
+	ah->ah_current_channel = channel;
+	ah->ah_turbo = (channel->hw_value == CHANNEL_T ? 1 : 0);
+
+	return 0;
+}
+
+/*****************\
+  PHY calibration
+\*****************/
+
+/**
+ * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration
+ *
+ * @ah: struct ath5k_hw pointer we are operating on
+ * @freq: the channel frequency, just used for error logging
+ *
+ * This function performs a noise floor calibration of the PHY and waits for
+ * it to complete. Then the noise floor value is compared to some maximum
+ * noise floor we consider valid.
+ *
+ * Note that this is different from what the madwifi HAL does: it reads the
+ * noise floor and afterwards initiates the calibration. Since the noise floor
+ * calibration can take some time to finish, depending on the current channel
+ * use, that avoids the occasional timeout warnings we are seeing now.
+ *
+ * See the following link for an Atheros patent on noise floor calibration:
+ * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \
+ * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7
+ *
+ * XXX: Since during noise floor calibration antennas are detached according to
+ * the patent, we should stop tx queues here.
+ */
+int
+ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq)
+{
+	int ret;
+	unsigned int i;
+	s32 noise_floor;
+
+	/*
+	 * Enable noise floor calibration
+	 */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
+				AR5K_PHY_AGCCTL_NF);
+
+	ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
+			AR5K_PHY_AGCCTL_NF, 0, 0);
+
+	if (ret) {
+		DBG("ath5k: noise floor calibration timeout (%d MHz)\n", freq);
+		return -EAGAIN;
+	}
+
+	/* Wait until the noise floor is calibrated and read the value */
+	for (i = 20; i > 0; i--) {
+		mdelay(1);
+		noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
+		noise_floor = AR5K_PHY_NF_RVAL(noise_floor);
+		if (noise_floor & AR5K_PHY_NF_ACTIVE) {
+			noise_floor = AR5K_PHY_NF_AVAL(noise_floor);
+
+			if (noise_floor <= AR5K_TUNE_NOISE_FLOOR)
+				break;
+		}
+	}
+
+	DBG2("ath5k: noise floor %d\n", noise_floor);
+
+	if (noise_floor > AR5K_TUNE_NOISE_FLOOR) {
+		DBG("ath5k: noise floor calibration failed (%d MHz)\n", freq);
+		return -EAGAIN;
+	}
+
+	ah->ah_noise_floor = noise_floor;
+
+	return 0;
+}
+
+/*
+ * Perform a PHY calibration on RF5110
+ * -Fix BPSK/QAM Constellation (I/Q correction)
+ * -Calculate Noise Floor
+ */
+static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
+		struct net80211_channel *channel)
+{
+	u32 phy_sig, phy_agc, phy_sat, beacon;
+	int ret;
+
+	/*
+	 * Disable beacons and RX/TX queues, wait
+	 */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210,
+		AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210);
+	beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210);
+	ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210);
+
+	mdelay(2);
+
+	/*
+	 * Set the channel (with AGC turned off)
+	 */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+	udelay(10);
+	ret = ath5k_hw_channel(ah, channel);
+
+	/*
+	 * Activate PHY and wait
+	 */
+	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
+	mdelay(1);
+
+	AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+
+	if (ret)
+		return ret;
+
+	/*
+	 * Calibrate the radio chip
+	 */
+
+	/* Remember normal state */
+	phy_sig = ath5k_hw_reg_read(ah, AR5K_PHY_SIG);
+	phy_agc = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCOARSE);
+	phy_sat = ath5k_hw_reg_read(ah, AR5K_PHY_ADCSAT);
+
+	/* Update radio registers */
+	ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) |
+		AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG);
+
+	ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI |
+			AR5K_PHY_AGCCOARSE_LO)) |
+		AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) |
+		AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE);
+
+	ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT |
+			AR5K_PHY_ADCSAT_THR)) |
+		AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) |
+		AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT);
+
+	udelay(20);
+
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+	udelay(10);
+	ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG);
+	AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
+
+	mdelay(1);
+
+	/*
+	 * Enable calibration and wait until completion
+	 */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL);
+
+	ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
+			AR5K_PHY_AGCCTL_CAL, 0, 0);
+
+	/* Reset to normal state */
+	ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG);
+	ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE);
+	ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT);
+
+	if (ret) {
+		DBG("ath5k: calibration timeout (%d MHz)\n",
+		    channel->center_freq);
+		return ret;
+	}
+
+	ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
+
+	/*
+	 * Re-enable RX/TX and beacons
+	 */
+	AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210,
+		AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210);
+	ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210);
+
+	return 0;
+}
+
+/*
+ * Perform a PHY calibration on RF5111/5112 and newer chips
+ */
+static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah,
+		struct net80211_channel *channel)
+{
+	u32 i_pwr, q_pwr;
+	s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd;
+	int i;
+
+	if (!ah->ah_calibration ||
+		ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN)
+		goto done;
+
+	/* Calibration has finished, get the results and re-run */
+	for (i = 0; i <= 10; i++) {
+		iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR);
+		i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I);
+		q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q);
+	}
+
+	i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7;
+	q_coffd = q_pwr >> 7;
+
+	/* No correction */
+	if (i_coffd == 0 || q_coffd == 0)
+		goto done;
+
+	i_coff = ((-iq_corr) / i_coffd) & 0x3f;
+
+	/* Boundary check */
+	if (i_coff > 31)
+		i_coff = 31;
+	if (i_coff < -32)
+		i_coff = -32;
+
+	q_coff = (((s32)i_pwr / q_coffd) - 128) & 0x1f;
+
+	/* Boundary check */
+	if (q_coff > 15)
+		q_coff = 15;
+	if (q_coff < -16)
+		q_coff = -16;
+
+	/* Commit new I/Q value */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE |
+		((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S));
+
+	/* Re-enable calibration -if we don't we'll commit
+	 * the same values again and again */
+	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
+			AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN);
+
+done:
+
+	/* TODO: Separate noise floor calibration from I/Q calibration
+	 * since noise floor calibration interrupts rx path while I/Q
+	 * calibration doesn't. We don't need to run noise floor calibration
+	 * as often as I/Q calibration.*/
+	ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
+
+	/* Initiate a gain_F calibration */
+	ath5k_hw_request_rfgain_probe(ah);
+
+	return 0;
+}
+
+/*
+ * Perform a PHY calibration
+ */
+int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
+		struct net80211_channel *channel)
+{
+	int ret;
+
+	if (ah->ah_radio == AR5K_RF5110)
+		ret = ath5k_hw_rf5110_calibrate(ah, channel);
+	else
+		ret = ath5k_hw_rf511x_calibrate(ah, channel);
+
+	return ret;
+}
+
+int ath5k_hw_phy_disable(struct ath5k_hw *ah)
+{
+	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
+
+	return 0;
+}
+
+/********************\
+  Misc PHY functions
+\********************/
+
+/*
+ * Get the PHY Chip revision
+ */
+u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan)
+{
+	unsigned int i;
+	u32 srev;
+	u16 ret;
+
+	/*
+	 * Set the radio chip access register
+	 */
+	switch (chan) {
+	case CHANNEL_2GHZ:
+		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0));
+		break;
+	case CHANNEL_5GHZ:
+		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+		break;
+	default:
+		return 0;
+	}
+
+	mdelay(2);
+
+	/* ...wait until PHY is ready and read the selected radio revision */
+	ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34));
+
+	for (i = 0; i < 8; i++)
+		ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20));
+
+	if (ah->ah_version == AR5K_AR5210) {
+		srev = ath5k_hw_reg_read(ah, AR5K_PHY(256) >> 28) & 0xf;
+		ret = (u16)ath5k_hw_bitswap(srev, 4) + 1;
+	} else {
+		srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff;
+		ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) |
+				((srev & 0x0f) << 4), 8);
+	}
+
+	/* Reset to the 5GHz mode */
+	ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+
+	return ret;
+}
+
+void /*TODO:Boundary check*/
+ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant)
+{
+	if (ah->ah_version != AR5K_AR5210)
+		ath5k_hw_reg_write(ah, ant, AR5K_DEFAULT_ANTENNA);
+}
+
+unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah)
+{
+	if (ah->ah_version != AR5K_AR5210)
+		return ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA);
+
+	return 0; /*XXX: What do we return for 5210 ?*/
+}
+
+
+/****************\
+* TX power setup *
+\****************/
+
+/*
+ * Helper functions
+ */
+
+/*
+ * Do linear interpolation between two given (x, y) points
+ */
+static s16
+ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right,
+					s16 y_left, s16 y_right)
+{
+	s16 ratio, result;
+
+	/* Avoid divide by zero and skip interpolation
+	 * if we have the same point */
+	if ((x_left == x_right) || (y_left == y_right))
+		return y_left;
+
+	/*
+	 * Since we use ints and not fps, we need to scale up in
+	 * order to get a sane ratio value (or else we 'll eg. get
+	 * always 1 instead of 1.25, 1.75 etc). We scale up by 100
+	 * to have some accuracy both for 0.5 and 0.25 steps.
+	 */
+	ratio = ((100 * y_right - 100 * y_left)/(x_right - x_left));
+
+	/* Now scale down to be in range */
+	result = y_left + (ratio * (target - x_left) / 100);
+
+	return result;
+}
+
+/*
+ * Find vertical boundary (min pwr) for the linear PCDAC curve.
+ *
+ * Since we have the top of the curve and we draw the line below
+ * until we reach 1 (1 pcdac step) we need to know which point
+ * (x value) that is so that we don't go below y axis and have negative
+ * pcdac values when creating the curve, or fill the table with zeroes.
+ */
+static s16
+ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR,
+				const s16 *pwrL, const s16 *pwrR)
+{
+	s8 tmp;
+	s16 min_pwrL, min_pwrR;
+	s16 pwr_i;
+
+	if (pwrL[0] == pwrL[1])
+		min_pwrL = pwrL[0];
+	else {
+		pwr_i = pwrL[0];
+		do {
+			pwr_i--;
+			tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+							pwrL[0], pwrL[1],
+							stepL[0], stepL[1]);
+		} while (tmp > 1);
+
+		min_pwrL = pwr_i;
+	}
+
+	if (pwrR[0] == pwrR[1])
+		min_pwrR = pwrR[0];
+	else {
+		pwr_i = pwrR[0];
+		do {
+			pwr_i--;
+			tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+							pwrR[0], pwrR[1],
+							stepR[0], stepR[1]);
+		} while (tmp > 1);
+
+		min_pwrR = pwr_i;
+	}
+
+	/* Keep the right boundary so that it works for both curves */
+	return max(min_pwrL, min_pwrR);
+}
+
+/*
+ * Interpolate (pwr,vpd) points to create a Power to PDADC or a
+ * Power to PCDAC curve.
+ *
+ * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC
+ * steps (offsets) on y axis. Power can go up to 31.5dB and max
+ * PCDAC/PDADC step for each curve is 64 but we can write more than
+ * one curves on hw so we can go up to 128 (which is the max step we
+ * can write on the final table).
+ *
+ * We write y values (PCDAC/PDADC steps) on hw.
+ */
+static void
+ath5k_create_power_curve(s16 pmin, s16 pmax,
+			const s16 *pwr, const u8 *vpd,
+			u8 num_points,
+			u8 *vpd_table, u8 type)
+{
+	u8 idx[2] = { 0, 1 };
+	s16 pwr_i = 2*pmin;
+	int i;
+
+	if (num_points < 2)
+		return;
+
+	/* We want the whole line, so adjust boundaries
+	 * to cover the entire power range. Note that
+	 * power values are already 0.25dB so no need
+	 * to multiply pwr_i by 2 */
+	if (type == AR5K_PWRTABLE_LINEAR_PCDAC) {
+		pwr_i = pmin;
+		pmin = 0;
+		pmax = 63;
+	}
+
+	/* Find surrounding turning points (TPs)
+	 * and interpolate between them */
+	for (i = 0; (i <= (u16) (pmax - pmin)) &&
+	(i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
+
+		/* We passed the right TP, move to the next set of TPs
+		 * if we pass the last TP, extrapolate above using the last
+		 * two TPs for ratio */
+		if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) {
+			idx[0]++;
+			idx[1]++;
+		}
+
+		vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i,
+						pwr[idx[0]], pwr[idx[1]],
+						vpd[idx[0]], vpd[idx[1]]);
+
+		/* Increase by 0.5dB
+		 * (0.25 dB units) */
+		pwr_i += 2;
+	}
+}
+
+/*
+ * Get the surrounding per-channel power calibration piers
+ * for a given frequency so that we can interpolate between
+ * them and come up with an apropriate dataset for our current
+ * channel.
+ */
+static void
+ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah,
+			struct net80211_channel *channel,
+			struct ath5k_chan_pcal_info **pcinfo_l,
+			struct ath5k_chan_pcal_info **pcinfo_r)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_chan_pcal_info *pcinfo;
+	u8 idx_l, idx_r;
+	u8 mode, max, i;
+	u32 target = channel->center_freq;
+
+	idx_l = 0;
+	idx_r = 0;
+
+	if (!(channel->hw_value & CHANNEL_OFDM)) {
+		pcinfo = ee->ee_pwr_cal_b;
+		mode = AR5K_EEPROM_MODE_11B;
+	} else if (channel->hw_value & CHANNEL_2GHZ) {
+		pcinfo = ee->ee_pwr_cal_g;
+		mode = AR5K_EEPROM_MODE_11G;
+	} else {
+		pcinfo = ee->ee_pwr_cal_a;
+		mode = AR5K_EEPROM_MODE_11A;
+	}
+	max = ee->ee_n_piers[mode] - 1;
+
+	/* Frequency is below our calibrated
+	 * range. Use the lowest power curve
+	 * we have */
+	if (target < pcinfo[0].freq) {
+		idx_l = idx_r = 0;
+		goto done;
+	}
+
+	/* Frequency is above our calibrated
+	 * range. Use the highest power curve
+	 * we have */
+	if (target > pcinfo[max].freq) {
+		idx_l = idx_r = max;
+		goto done;
+	}
+
+	/* Frequency is inside our calibrated
+	 * channel range. Pick the surrounding
+	 * calibration piers so that we can
+	 * interpolate */
+	for (i = 0; i <= max; i++) {
+
+		/* Frequency matches one of our calibration
+		 * piers, no need to interpolate, just use
+		 * that calibration pier */
+		if (pcinfo[i].freq == target) {
+			idx_l = idx_r = i;
+			goto done;
+		}
+
+		/* We found a calibration pier that's above
+		 * frequency, use this pier and the previous
+		 * one to interpolate */
+		if (target < pcinfo[i].freq) {
+			idx_r = i;
+			idx_l = idx_r - 1;
+			goto done;
+		}
+	}
+
+done:
+	*pcinfo_l = &pcinfo[idx_l];
+	*pcinfo_r = &pcinfo[idx_r];
+
+	return;
+}
+
+/*
+ * Get the surrounding per-rate power calibration data
+ * for a given frequency and interpolate between power
+ * values to set max target power supported by hw for
+ * each rate.
+ */
+static void
+ath5k_get_rate_pcal_data(struct ath5k_hw *ah,
+			struct net80211_channel *channel,
+			struct ath5k_rate_pcal_info *rates)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_rate_pcal_info *rpinfo;
+	u8 idx_l, idx_r;
+	u8 mode, max, i;
+	u32 target = channel->center_freq;
+
+	idx_l = 0;
+	idx_r = 0;
+
+	if (!(channel->hw_value & CHANNEL_OFDM)) {
+		rpinfo = ee->ee_rate_tpwr_b;
+		mode = AR5K_EEPROM_MODE_11B;
+	} else if (channel->hw_value & CHANNEL_2GHZ) {
+		rpinfo = ee->ee_rate_tpwr_g;
+		mode = AR5K_EEPROM_MODE_11G;
+	} else {
+		rpinfo = ee->ee_rate_tpwr_a;
+		mode = AR5K_EEPROM_MODE_11A;
+	}
+	max = ee->ee_rate_target_pwr_num[mode] - 1;
+
+	/* Get the surrounding calibration
+	 * piers - same as above */
+	if (target < rpinfo[0].freq) {
+		idx_l = idx_r = 0;
+		goto done;
+	}
+
+	if (target > rpinfo[max].freq) {
+		idx_l = idx_r = max;
+		goto done;
+	}
+
+	for (i = 0; i <= max; i++) {
+
+		if (rpinfo[i].freq == target) {
+			idx_l = idx_r = i;
+			goto done;
+		}
+
+		if (target < rpinfo[i].freq) {
+			idx_r = i;
+			idx_l = idx_r - 1;
+			goto done;
+		}
+	}
+
+done:
+	/* Now interpolate power value, based on the frequency */
+	rates->freq = target;
+
+	rates->target_power_6to24 =
+		ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+					rpinfo[idx_r].freq,
+					rpinfo[idx_l].target_power_6to24,
+					rpinfo[idx_r].target_power_6to24);
+
+	rates->target_power_36 =
+		ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+					rpinfo[idx_r].freq,
+					rpinfo[idx_l].target_power_36,
+					rpinfo[idx_r].target_power_36);
+
+	rates->target_power_48 =
+		ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+					rpinfo[idx_r].freq,
+					rpinfo[idx_l].target_power_48,
+					rpinfo[idx_r].target_power_48);
+
+	rates->target_power_54 =
+		ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+					rpinfo[idx_r].freq,
+					rpinfo[idx_l].target_power_54,
+					rpinfo[idx_r].target_power_54);
+}
+
+/*
+ * Get the max edge power for this channel if
+ * we have such data from EEPROM's Conformance Test
+ * Limits (CTL), and limit max power if needed.
+ *
+ * FIXME: Only works for world regulatory domains
+ */
+static void
+ath5k_get_max_ctl_power(struct ath5k_hw *ah,
+			struct net80211_channel *channel)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	struct ath5k_edge_power *rep = ee->ee_ctl_pwr;
+	u8 *ctl_val = ee->ee_ctl;
+	s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4;
+	s16 edge_pwr = 0;
+	u8 rep_idx;
+	u8 i, ctl_mode;
+	u8 ctl_idx = 0xFF;
+	u32 target = channel->center_freq;
+
+	/* Find out a CTL for our mode that's not mapped
+	 * on a specific reg domain.
+	 *
+	 * TODO: Map our current reg domain to one of the 3 available
+	 * reg domain ids so that we can support more CTLs. */
+	switch (channel->hw_value & CHANNEL_MODES) {
+	case CHANNEL_A:
+		ctl_mode = AR5K_CTL_11A | AR5K_CTL_NO_REGDOMAIN;
+		break;
+	case CHANNEL_G:
+		ctl_mode = AR5K_CTL_11G | AR5K_CTL_NO_REGDOMAIN;
+		break;
+	case CHANNEL_B:
+		ctl_mode = AR5K_CTL_11B | AR5K_CTL_NO_REGDOMAIN;
+		break;
+	case CHANNEL_T:
+		ctl_mode = AR5K_CTL_TURBO | AR5K_CTL_NO_REGDOMAIN;
+		break;
+	case CHANNEL_TG:
+		ctl_mode = AR5K_CTL_TURBOG | AR5K_CTL_NO_REGDOMAIN;
+		break;
+	case CHANNEL_XR:
+		/* Fall through */
+	default:
+		return;
+	}
+
+	for (i = 0; i < ee->ee_ctls; i++) {
+		if (ctl_val[i] == ctl_mode) {
+			ctl_idx = i;
+			break;
+		}
+	}
+
+	/* If we have a CTL dataset available grab it and find the
+	 * edge power for our frequency */
+	if (ctl_idx == 0xFF)
+		return;
+
+	/* Edge powers are sorted by frequency from lower
+	 * to higher. Each CTL corresponds to 8 edge power
+	 * measurements. */
+	rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES;
+
+	/* Don't do boundaries check because we
+	 * might have more that one bands defined
+	 * for this mode */
+
+	/* Get the edge power that's closer to our
+	 * frequency */
+	for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) {
+		rep_idx += i;
+		if (target <= rep[rep_idx].freq)
+			edge_pwr = (s16) rep[rep_idx].edge;
+	}
+
+	if (edge_pwr) {
+		ah->ah_txpower.txp_max_pwr = 4*min(edge_pwr, max_chan_pwr);
+	}
+}
+
+
+/*
+ * Power to PCDAC table functions
+ */
+
+/*
+ * Fill Power to PCDAC table on RF5111
+ *
+ * No further processing is needed for RF5111, the only thing we have to
+ * do is fill the values below and above calibration range since eeprom data
+ * may not cover the entire PCDAC table.
+ */
+static void
+ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min,
+							s16 *table_max)
+{
+	u8 	*pcdac_out = ah->ah_txpower.txp_pd_table;
+	u8	*pcdac_tmp = ah->ah_txpower.tmpL[0];
+	u8	pcdac_0, pcdac_n, pcdac_i, pwr_idx, i;
+	s16	min_pwr, max_pwr;
+
+	/* Get table boundaries */
+	min_pwr = table_min[0];
+	pcdac_0 = pcdac_tmp[0];
+
+	max_pwr = table_max[0];
+	pcdac_n = pcdac_tmp[table_max[0] - table_min[0]];
+
+	/* Extrapolate below minimum using pcdac_0 */
+	pcdac_i = 0;
+	for (i = 0; i < min_pwr; i++)
+		pcdac_out[pcdac_i++] = pcdac_0;
+
+	/* Copy values from pcdac_tmp */
+	pwr_idx = min_pwr;
+	for (i = 0 ; pwr_idx <= max_pwr &&
+	pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
+		pcdac_out[pcdac_i++] = pcdac_tmp[i];
+		pwr_idx++;
+	}
+
+	/* Extrapolate above maximum */
+	while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE)
+		pcdac_out[pcdac_i++] = pcdac_n;
+
+}
+
+/*
+ * Combine available XPD Curves and fill Linear Power to PCDAC table
+ * on RF5112
+ *
+ * RFX112 can have up to 2 curves (one for low txpower range and one for
+ * higher txpower range). We need to put them both on pcdac_out and place
+ * them in the correct location. In case we only have one curve available
+ * just fit it on pcdac_out (it's supposed to cover the entire range of
+ * available pwr levels since it's always the higher power curve). Extrapolate
+ * below and above final table if needed.
+ */
+static void
+ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
+						s16 *table_max, u8 pdcurves)
+{
+	u8 	*pcdac_out = ah->ah_txpower.txp_pd_table;
+	u8	*pcdac_low_pwr;
+	u8	*pcdac_high_pwr;
+	u8	*pcdac_tmp;
+	u8	pwr;
+	s16	max_pwr_idx;
+	s16	min_pwr_idx;
+	s16	mid_pwr_idx = 0;
+	/* Edge flag turs on the 7nth bit on the PCDAC
+	 * to delcare the higher power curve (force values
+	 * to be greater than 64). If we only have one curve
+	 * we don't need to set this, if we have 2 curves and
+	 * fill the table backwards this can also be used to
+	 * switch from higher power curve to lower power curve */
+	u8	edge_flag;
+	int	i;
+
+	/* When we have only one curve available
+	 * that's the higher power curve. If we have
+	 * two curves the first is the high power curve
+	 * and the next is the low power curve. */
+	if (pdcurves > 1) {
+		pcdac_low_pwr = ah->ah_txpower.tmpL[1];
+		pcdac_high_pwr = ah->ah_txpower.tmpL[0];
+		mid_pwr_idx = table_max[1] - table_min[1] - 1;
+		max_pwr_idx = (table_max[0] - table_min[0]) / 2;
+
+		/* If table size goes beyond 31.5dB, keep the
+		 * upper 31.5dB range when setting tx power.
+		 * Note: 126 = 31.5 dB in quarter dB steps */
+		if (table_max[0] - table_min[1] > 126)
+			min_pwr_idx = table_max[0] - 126;
+		else
+			min_pwr_idx = table_min[1];
+
+		/* Since we fill table backwards
+		 * start from high power curve */
+		pcdac_tmp = pcdac_high_pwr;
+
+		edge_flag = 0x40;
+	} else {
+		pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */
+		pcdac_high_pwr = ah->ah_txpower.tmpL[0];
+		min_pwr_idx = table_min[0];
+		max_pwr_idx = (table_max[0] - table_min[0]) / 2;
+		pcdac_tmp = pcdac_high_pwr;
+		edge_flag = 0;
+	}
+
+	/* This is used when setting tx power*/
+	ah->ah_txpower.txp_min_idx = min_pwr_idx/2;
+
+	/* Fill Power to PCDAC table backwards */
+	pwr = max_pwr_idx;
+	for (i = 63; i >= 0; i--) {
+		/* Entering lower power range, reset
+		 * edge flag and set pcdac_tmp to lower
+		 * power curve.*/
+		if (edge_flag == 0x40 &&
+		(2*pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
+			edge_flag = 0x00;
+			pcdac_tmp = pcdac_low_pwr;
+			pwr = mid_pwr_idx/2;
+		}
+
+		/* Don't go below 1, extrapolate below if we have
+		 * already swithced to the lower power curve -or
+		 * we only have one curve and edge_flag is zero
+		 * anyway */
+		if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) {
+			while (i >= 0) {
+				pcdac_out[i] = pcdac_out[i + 1];
+				i--;
+			}
+			break;
+		}
+
+		pcdac_out[i] = pcdac_tmp[pwr] | edge_flag;
+
+		/* Extrapolate above if pcdac is greater than
+		 * 126 -this can happen because we OR pcdac_out
+		 * value with edge_flag on high power curve */
+		if (pcdac_out[i] > 126)
+			pcdac_out[i] = 126;
+
+		/* Decrease by a 0.5dB step */
+		pwr--;
+	}
+}
+
+/* Write PCDAC values on hw */
+static void
+ath5k_setup_pcdac_table(struct ath5k_hw *ah)
+{
+	u8 	*pcdac_out = ah->ah_txpower.txp_pd_table;
+	int	i;
+
+	/*
+	 * Write TX power values
+	 */
+	for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
+		ath5k_hw_reg_write(ah,
+			(((pcdac_out[2*i + 0] << 8 | 0xff) & 0xffff) << 0) |
+			(((pcdac_out[2*i + 1] << 8 | 0xff) & 0xffff) << 16),
+			AR5K_PHY_PCDAC_TXPOWER(i));
+	}
+}
+
+
+/*
+ * Power to PDADC table functions
+ */
+
+/*
+ * Set the gain boundaries and create final Power to PDADC table
+ *
+ * We can have up to 4 pd curves, we need to do a simmilar process
+ * as we do for RF5112. This time we don't have an edge_flag but we
+ * set the gain boundaries on a separate register.
+ */
+static void
+ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah,
+			s16 *pwr_min, s16 *pwr_max, u8 pdcurves)
+{
+	u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS];
+	u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+	u8 *pdadc_tmp;
+	s16 pdadc_0;
+	u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size;
+	u8 pd_gain_overlap;
+
+	/* Note: Register value is initialized on initvals
+	 * there is no feedback from hw.
+	 * XXX: What about pd_gain_overlap from EEPROM ? */
+	pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
+		AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;
+
+	/* Create final PDADC table */
+	for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) {
+		pdadc_tmp = ah->ah_txpower.tmpL[pdg];
+
+		if (pdg == pdcurves - 1)
+			/* 2 dB boundary stretch for last
+			 * (higher power) curve */
+			gain_boundaries[pdg] = pwr_max[pdg] + 4;
+		else
+			/* Set gain boundary in the middle
+			 * between this curve and the next one */
+			gain_boundaries[pdg] =
+				(pwr_max[pdg] + pwr_min[pdg + 1]) / 2;
+
+		/* Sanity check in case our 2 db stretch got out of
+		 * range. */
+		if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER)
+			gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER;
+
+		/* For the first curve (lower power)
+		 * start from 0 dB */
+		if (pdg == 0)
+			pdadc_0 = 0;
+		else
+			/* For the other curves use the gain overlap */
+			pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) -
+							pd_gain_overlap;
+
+		/* Force each power step to be at least 0.5 dB */
+		if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1)
+			pwr_step = pdadc_tmp[1] - pdadc_tmp[0];
+		else
+			pwr_step = 1;
+
+		/* If pdadc_0 is negative, we need to extrapolate
+		 * below this pdgain by a number of pwr_steps */
+		while ((pdadc_0 < 0) && (pdadc_i < 128)) {
+			s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step;
+			pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp;
+			pdadc_0++;
+		}
+
+		/* Set last pwr level, using gain boundaries */
+		pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg];
+		/* Limit it to be inside pwr range */
+		table_size = pwr_max[pdg] - pwr_min[pdg];
+		max_idx = (pdadc_n < table_size) ? pdadc_n : table_size;
+
+		/* Fill pdadc_out table */
+		while (pdadc_0 < max_idx)
+			pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++];
+
+		/* Need to extrapolate above this pdgain? */
+		if (pdadc_n <= max_idx)
+			continue;
+
+		/* Force each power step to be at least 0.5 dB */
+		if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1)
+			pwr_step = pdadc_tmp[table_size - 1] -
+						pdadc_tmp[table_size - 2];
+		else
+			pwr_step = 1;
+
+		/* Extrapolate above */
+		while ((pdadc_0 < (s16) pdadc_n) &&
+		(pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) {
+			s16 tmp = pdadc_tmp[table_size - 1] +
+					(pdadc_0 - max_idx) * pwr_step;
+			pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp;
+			pdadc_0++;
+		}
+	}
+
+	while (pdg < AR5K_EEPROM_N_PD_GAINS) {
+		gain_boundaries[pdg] = gain_boundaries[pdg - 1];
+		pdg++;
+	}
+
+	while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) {
+		pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1];
+		pdadc_i++;
+	}
+
+	/* Set gain boundaries */
+	ath5k_hw_reg_write(ah,
+		AR5K_REG_SM(pd_gain_overlap,
+			AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) |
+		AR5K_REG_SM(gain_boundaries[0],
+			AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) |
+		AR5K_REG_SM(gain_boundaries[1],
+			AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) |
+		AR5K_REG_SM(gain_boundaries[2],
+			AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) |
+		AR5K_REG_SM(gain_boundaries[3],
+			AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4),
+		AR5K_PHY_TPC_RG5);
+
+	/* Used for setting rate power table */
+	ah->ah_txpower.txp_min_idx = pwr_min[0];
+
+}
+
+/* Write PDADC values on hw */
+static void
+ath5k_setup_pwr_to_pdadc_table(struct ath5k_hw *ah,
+			u8 pdcurves, u8 *pdg_to_idx)
+{
+	u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+	u32 reg;
+	u8 i;
+
+	/* Select the right pdgain curves */
+
+	/* Clear current settings */
+	reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1);
+	reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 |
+		AR5K_PHY_TPC_RG1_PDGAIN_2 |
+		AR5K_PHY_TPC_RG1_PDGAIN_3 |
+		AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
+
+	/*
+	 * Use pd_gains curve from eeprom
+	 *
+	 * This overrides the default setting from initvals
+	 * in case some vendors (e.g. Zcomax) don't use the default
+	 * curves. If we don't honor their settings we 'll get a
+	 * 5dB (1 * gain overlap ?) drop.
+	 */
+	reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
+
+	switch (pdcurves) {
+	case 3:
+		reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3);
+		/* Fall through */
+	case 2:
+		reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2);
+		/* Fall through */
+	case 1:
+		reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1);
+		break;
+	}
+	ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1);
+
+	/*
+	 * Write TX power values
+	 */
+	for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
+		ath5k_hw_reg_write(ah,
+			((pdadc_out[4*i + 0] & 0xff) << 0) |
+			((pdadc_out[4*i + 1] & 0xff) << 8) |
+			((pdadc_out[4*i + 2] & 0xff) << 16) |
+			((pdadc_out[4*i + 3] & 0xff) << 24),
+			AR5K_PHY_PDADC_TXPOWER(i));
+	}
+}
+
+
+/*
+ * Common code for PCDAC/PDADC tables
+ */
+
+/*
+ * This is the main function that uses all of the above
+ * to set PCDAC/PDADC table on hw for the current channel.
+ * This table is used for tx power calibration on the basband,
+ * without it we get weird tx power levels and in some cases
+ * distorted spectral mask
+ */
+static int
+ath5k_setup_channel_powertable(struct ath5k_hw *ah,
+			struct net80211_channel *channel,
+			u8 ee_mode, u8 type)
+{
+	struct ath5k_pdgain_info *pdg_L, *pdg_R;
+	struct ath5k_chan_pcal_info *pcinfo_L;
+	struct ath5k_chan_pcal_info *pcinfo_R;
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
+	s16 table_min[AR5K_EEPROM_N_PD_GAINS];
+	s16 table_max[AR5K_EEPROM_N_PD_GAINS];
+	u8 *tmpL;
+	u8 *tmpR;
+	u32 target = channel->center_freq;
+	int pdg, i;
+
+	/* Get surounding freq piers for this channel */
+	ath5k_get_chan_pcal_surrounding_piers(ah, channel,
+						&pcinfo_L,
+						&pcinfo_R);
+
+	/* Loop over pd gain curves on
+	 * surounding freq piers by index */
+	for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) {
+
+		/* Fill curves in reverse order
+		 * from lower power (max gain)
+		 * to higher power. Use curve -> idx
+		 * backmaping we did on eeprom init */
+		u8 idx = pdg_curve_to_idx[pdg];
+
+		/* Grab the needed curves by index */
+		pdg_L = &pcinfo_L->pd_curves[idx];
+		pdg_R = &pcinfo_R->pd_curves[idx];
+
+		/* Initialize the temp tables */
+		tmpL = ah->ah_txpower.tmpL[pdg];
+		tmpR = ah->ah_txpower.tmpR[pdg];
+
+		/* Set curve's x boundaries and create
+		 * curves so that they cover the same
+		 * range (if we don't do that one table
+		 * will have values on some range and the
+		 * other one won't have any so interpolation
+		 * will fail) */
+		table_min[pdg] = min(pdg_L->pd_pwr[0],
+					pdg_R->pd_pwr[0]) / 2;
+
+		table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
+				pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2;
+
+		/* Now create the curves on surrounding channels
+		 * and interpolate if needed to get the final
+		 * curve for this gain on this channel */
+		switch (type) {
+		case AR5K_PWRTABLE_LINEAR_PCDAC:
+			/* Override min/max so that we don't loose
+			 * accuracy (don't divide by 2) */
+			table_min[pdg] = min(pdg_L->pd_pwr[0],
+						pdg_R->pd_pwr[0]);
+
+			table_max[pdg] =
+				max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
+					pdg_R->pd_pwr[pdg_R->pd_points - 1]);
+
+			/* Override minimum so that we don't get
+			 * out of bounds while extrapolating
+			 * below. Don't do this when we have 2
+			 * curves and we are on the high power curve
+			 * because table_min is ok in this case */
+			if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) {
+
+				table_min[pdg] =
+					ath5k_get_linear_pcdac_min(pdg_L->pd_step,
+								pdg_R->pd_step,
+								pdg_L->pd_pwr,
+								pdg_R->pd_pwr);
+
+				/* Don't go too low because we will
+				 * miss the upper part of the curve.
+				 * Note: 126 = 31.5dB (max power supported)
+				 * in 0.25dB units */
+				if (table_max[pdg] - table_min[pdg] > 126)
+					table_min[pdg] = table_max[pdg] - 126;
+			}
+
+			/* Fall through */
+		case AR5K_PWRTABLE_PWR_TO_PCDAC:
+		case AR5K_PWRTABLE_PWR_TO_PDADC:
+
+			ath5k_create_power_curve(table_min[pdg],
+						table_max[pdg],
+						pdg_L->pd_pwr,
+						pdg_L->pd_step,
+						pdg_L->pd_points, tmpL, type);
+
+			/* We are in a calibration
+			 * pier, no need to interpolate
+			 * between freq piers */
+			if (pcinfo_L == pcinfo_R)
+				continue;
+
+			ath5k_create_power_curve(table_min[pdg],
+						table_max[pdg],
+						pdg_R->pd_pwr,
+						pdg_R->pd_step,
+						pdg_R->pd_points, tmpR, type);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* Interpolate between curves
+		 * of surounding freq piers to
+		 * get the final curve for this
+		 * pd gain. Re-use tmpL for interpolation
+		 * output */
+		for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) &&
+		(i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
+			tmpL[i] = (u8) ath5k_get_interpolated_value(target,
+							(s16) pcinfo_L->freq,
+							(s16) pcinfo_R->freq,
+							(s16) tmpL[i],
+							(s16) tmpR[i]);
+		}
+	}
+
+	/* Now we have a set of curves for this
+	 * channel on tmpL (x range is table_max - table_min
+	 * and y values are tmpL[pdg][]) sorted in the same
+	 * order as EEPROM (because we've used the backmaping).
+	 * So for RF5112 it's from higher power to lower power
+	 * and for RF2413 it's from lower power to higher power.
+	 * For RF5111 we only have one curve. */
+
+	/* Fill min and max power levels for this
+	 * channel by interpolating the values on
+	 * surounding channels to complete the dataset */
+	ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target,
+					(s16) pcinfo_L->freq,
+					(s16) pcinfo_R->freq,
+					pcinfo_L->min_pwr, pcinfo_R->min_pwr);
+
+	ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target,
+					(s16) pcinfo_L->freq,
+					(s16) pcinfo_R->freq,
+					pcinfo_L->max_pwr, pcinfo_R->max_pwr);
+
+	/* We are ready to go, fill PCDAC/PDADC
+	 * table and write settings on hardware */
+	switch (type) {
+	case AR5K_PWRTABLE_LINEAR_PCDAC:
+		/* For RF5112 we can have one or two curves
+		 * and each curve covers a certain power lvl
+		 * range so we need to do some more processing */
+		ath5k_combine_linear_pcdac_curves(ah, table_min, table_max,
+						ee->ee_pd_gains[ee_mode]);
+
+		/* Set txp.offset so that we can
+		 * match max power value with max
+		 * table index */
+		ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2);
+
+		/* Write settings on hw */
+		ath5k_setup_pcdac_table(ah);
+		break;
+	case AR5K_PWRTABLE_PWR_TO_PCDAC:
+		/* We are done for RF5111 since it has only
+		 * one curve, just fit the curve on the table */
+		ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max);
+
+		/* No rate powertable adjustment for RF5111 */
+		ah->ah_txpower.txp_min_idx = 0;
+		ah->ah_txpower.txp_offset = 0;
+
+		/* Write settings on hw */
+		ath5k_setup_pcdac_table(ah);
+		break;
+	case AR5K_PWRTABLE_PWR_TO_PDADC:
+		/* Set PDADC boundaries and fill
+		 * final PDADC table */
+		ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max,
+						ee->ee_pd_gains[ee_mode]);
+
+		/* Write settings on hw */
+		ath5k_setup_pwr_to_pdadc_table(ah, pdg, pdg_curve_to_idx);
+
+		/* Set txp.offset, note that table_min
+		 * can be negative */
+		ah->ah_txpower.txp_offset = table_min[0];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Per-rate tx power setting
+ *
+ * This is the code that sets the desired tx power (below
+ * maximum) on hw for each rate (we also have TPC that sets
+ * power per packet). We do that by providing an index on the
+ * PCDAC/PDADC table we set up.
+ */
+
+/*
+ * Set rate power table
+ *
+ * For now we only limit txpower based on maximum tx power
+ * supported by hw (what's inside rate_info). We need to limit
+ * this even more, based on regulatory domain etc.
+ *
+ * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps)
+ * and is indexed as follows:
+ * rates[0] - rates[7] -> OFDM rates
+ * rates[8] - rates[14] -> CCK rates
+ * rates[15] -> XR rates (they all have the same power)
+ */
+static void
+ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr,
+			struct ath5k_rate_pcal_info *rate_info,
+			u8 ee_mode)
+{
+	unsigned int i;
+	u16 *rates;
+
+	/* max_pwr is power level we got from driver/user in 0.5dB
+	 * units, switch to 0.25dB units so we can compare */
+	max_pwr *= 2;
+	max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2;
+
+	/* apply rate limits */
+	rates = ah->ah_txpower.txp_rates_power_table;
+
+	/* OFDM rates 6 to 24Mb/s */
+	for (i = 0; i < 5; i++)
+		rates[i] = min(max_pwr, rate_info->target_power_6to24);
+
+	/* Rest OFDM rates */
+	rates[5] = min(rates[0], rate_info->target_power_36);
+	rates[6] = min(rates[0], rate_info->target_power_48);
+	rates[7] = min(rates[0], rate_info->target_power_54);
+
+	/* CCK rates */
+	/* 1L */
+	rates[8] = min(rates[0], rate_info->target_power_6to24);
+	/* 2L */
+	rates[9] = min(rates[0], rate_info->target_power_36);
+	/* 2S */
+	rates[10] = min(rates[0], rate_info->target_power_36);
+	/* 5L */
+	rates[11] = min(rates[0], rate_info->target_power_48);
+	/* 5S */
+	rates[12] = min(rates[0], rate_info->target_power_48);
+	/* 11L */
+	rates[13] = min(rates[0], rate_info->target_power_54);
+	/* 11S */
+	rates[14] = min(rates[0], rate_info->target_power_54);
+
+	/* XR rates */
+	rates[15] = min(rates[0], rate_info->target_power_6to24);
+
+	/* CCK rates have different peak to average ratio
+	 * so we have to tweak their power so that gainf
+	 * correction works ok. For this we use OFDM to
+	 * CCK delta from eeprom */
+	if ((ee_mode == AR5K_EEPROM_MODE_11G) &&
+	(ah->ah_phy_revision < AR5K_SREV_PHY_5212A))
+		for (i = 8; i <= 15; i++)
+			rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta;
+
+	ah->ah_txpower.txp_min_pwr = rates[7];
+	ah->ah_txpower.txp_max_pwr = rates[0];
+	ah->ah_txpower.txp_ofdm = rates[7];
+}
+
+
+/*
+ * Set transmition power
+ */
+int
+ath5k_hw_txpower(struct ath5k_hw *ah, struct net80211_channel *channel,
+		u8 ee_mode, u8 txpower)
+{
+	struct ath5k_rate_pcal_info rate_info;
+	u8 type;
+	int ret;
+
+	if (txpower > AR5K_TUNE_MAX_TXPOWER) {
+		DBG("ath5k: invalid tx power %d\n", txpower);
+		return -EINVAL;
+	}
+	if (txpower == 0)
+		txpower = AR5K_TUNE_DEFAULT_TXPOWER;
+
+	/* Reset TX power values */
+	memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
+	ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
+	ah->ah_txpower.txp_min_pwr = 0;
+	ah->ah_txpower.txp_max_pwr = AR5K_TUNE_MAX_TXPOWER;
+
+	/* Initialize TX power table */
+	switch (ah->ah_radio) {
+	case AR5K_RF5111:
+		type = AR5K_PWRTABLE_PWR_TO_PCDAC;
+		break;
+	case AR5K_RF5112:
+		type = AR5K_PWRTABLE_LINEAR_PCDAC;
+		break;
+	case AR5K_RF2413:
+	case AR5K_RF5413:
+	case AR5K_RF2316:
+	case AR5K_RF2317:
+	case AR5K_RF2425:
+		type = AR5K_PWRTABLE_PWR_TO_PDADC;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* FIXME: Only on channel/mode change */
+	ret = ath5k_setup_channel_powertable(ah, channel, ee_mode, type);
+	if (ret)
+		return ret;
+
+	/* Limit max power if we have a CTL available */
+	ath5k_get_max_ctl_power(ah, channel);
+
+	/* FIXME: Tx power limit for this regdomain
+	 * XXX: Mac80211/CRDA will do that anyway ? */
+
+	/* FIXME: Antenna reduction stuff */
+
+	/* FIXME: Limit power on turbo modes */
+
+	/* FIXME: TPC scale reduction */
+
+	/* Get surounding channels for per-rate power table
+	 * calibration */
+	ath5k_get_rate_pcal_data(ah, channel, &rate_info);
+
+	/* Setup rate power table */
+	ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode);
+
+	/* Write rate power table on hw */
+	ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) |
+		AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) |
+		AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1);
+
+	ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(7, 24) |
+		AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) |
+		AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2);
+
+	ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(10, 24) |
+		AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) |
+		AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3);
+
+	ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(14, 24) |
+		AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
+		AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);
+
+	/* FIXME: TPC support */
+	if (ah->ah_txpower.txp_tpc) {
+		ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
+			AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+
+		ath5k_hw_reg_write(ah,
+			AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) |
+			AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) |
+			AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
+			AR5K_TPC);
+	} else {
+		ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
+			AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+	}
+
+	return 0;
+}
+
+int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 mode, u8 txpower)
+{
+	struct net80211_channel *channel = ah->ah_current_channel;
+
+	DBG2("ath5k: changing txpower to %d\n", txpower);
+
+	return ath5k_hw_txpower(ah, channel, mode, txpower);
+}
+
+#undef _ATH5K_PHY
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_qcu.c b/gpxe/src/drivers/net/ath5k/ath5k_qcu.c
new file mode 100644
index 0000000..a674b85
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_qcu.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+/********************************************\
+Queue Control Unit, DFS Control Unit Functions
+\********************************************/
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/*
+ * Set properties for a transmit queue
+ */
+int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah,
+				const struct ath5k_txq_info *queue_info)
+{
+	if (ah->ah_txq.tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return -EIO;
+
+	memcpy(&ah->ah_txq, queue_info, sizeof(struct ath5k_txq_info));
+
+	/*XXX: Is this supported on 5210 ?*/
+	if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA &&
+			((queue_info->tqi_subtype == AR5K_WME_AC_VI) ||
+			(queue_info->tqi_subtype == AR5K_WME_AC_VO))) ||
+			queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD)
+		ah->ah_txq.tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
+
+	return 0;
+}
+
+/*
+ * Initialize a transmit queue
+ */
+int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
+		struct ath5k_txq_info *queue_info)
+{
+	unsigned int queue;
+	int ret;
+
+	/* We only use one queue */
+	queue = 0;
+
+	/*
+	 * Setup internal queue structure
+	 */
+	memset(&ah->ah_txq, 0, sizeof(struct ath5k_txq_info));
+	ah->ah_txq.tqi_type = queue_type;
+
+	if (queue_info != NULL) {
+		queue_info->tqi_type = queue_type;
+		ret = ath5k_hw_set_tx_queueprops(ah, queue_info);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * We use ah_txq_status to hold a temp value for
+	 * the Secondary interrupt mask registers on 5211+
+	 * check out ath5k_hw_reset_tx_queue
+	 */
+	AR5K_Q_ENABLE_BITS(ah->ah_txq_status, 0);
+
+	return 0;
+}
+
+/*
+ * Set a transmit queue inactive
+ */
+void ath5k_hw_release_tx_queue(struct ath5k_hw *ah)
+{
+	/* This queue will be skipped in further operations */
+	ah->ah_txq.tqi_type = AR5K_TX_QUEUE_INACTIVE;
+	/*For SIMR setup*/
+	AR5K_Q_DISABLE_BITS(ah->ah_txq_status, 0);
+}
+
+/*
+ * Set DFS properties for a transmit queue on DCU
+ */
+int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah)
+{
+	u32 cw_min, cw_max, retry_lg, retry_sh;
+	struct ath5k_txq_info *tq = &ah->ah_txq;
+	const int queue = 0;
+
+	tq = &ah->ah_txq;
+
+	if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)
+		return 0;
+
+	if (ah->ah_version == AR5K_AR5210) {
+		/* Only handle data queues, others will be ignored */
+		if (tq->tqi_type != AR5K_TX_QUEUE_DATA)
+			return 0;
+
+		/* Set Slot time */
+		ath5k_hw_reg_write(ah, ah->ah_turbo ?
+			AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME,
+			AR5K_SLOT_TIME);
+		/* Set ACK_CTS timeout */
+		ath5k_hw_reg_write(ah, ah->ah_turbo ?
+			AR5K_INIT_ACK_CTS_TIMEOUT_TURBO :
+			AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME);
+		/* Set Transmit Latency */
+		ath5k_hw_reg_write(ah, ah->ah_turbo ?
+			AR5K_INIT_TRANSMIT_LATENCY_TURBO :
+			AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210);
+
+		/* Set IFS0 */
+		if (ah->ah_turbo) {
+			 ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
+				(ah->ah_aifs + tq->tqi_aifs) *
+				AR5K_INIT_SLOT_TIME_TURBO) <<
+				AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
+				AR5K_IFS0);
+		} else {
+			ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS +
+				(ah->ah_aifs + tq->tqi_aifs) *
+				AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) |
+				AR5K_INIT_SIFS, AR5K_IFS0);
+		}
+
+		/* Set IFS1 */
+		ath5k_hw_reg_write(ah, ah->ah_turbo ?
+			AR5K_INIT_PROTO_TIME_CNTRL_TURBO :
+			AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1);
+		/* Set AR5K_PHY_SETTLING */
+		ath5k_hw_reg_write(ah, ah->ah_turbo ?
+			(ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
+			| 0x38 :
+			(ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
+			| 0x1C,
+			AR5K_PHY_SETTLING);
+		/* Set Frame Control Register */
+		ath5k_hw_reg_write(ah, ah->ah_turbo ?
+			(AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE |
+			AR5K_PHY_TURBO_SHORT | 0x2020) :
+			(AR5K_PHY_FRAME_CTL_INI | 0x1020),
+			AR5K_PHY_FRAME_CTL_5210);
+	}
+
+	/*
+	 * Calculate cwmin/max by channel mode
+	 */
+	cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN;
+	cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX;
+	ah->ah_aifs = AR5K_TUNE_AIFS;
+	/*XR is only supported on 5212*/
+	if (IS_CHAN_XR(ah->ah_current_channel) &&
+			ah->ah_version == AR5K_AR5212) {
+		cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR;
+		cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR;
+		ah->ah_aifs = AR5K_TUNE_AIFS_XR;
+	/*B mode is not supported on 5210*/
+	} else if (IS_CHAN_B(ah->ah_current_channel) &&
+			ah->ah_version != AR5K_AR5210) {
+		cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B;
+		cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B;
+		ah->ah_aifs = AR5K_TUNE_AIFS_11B;
+	}
+
+	cw_min = 1;
+	while (cw_min < ah->ah_cw_min)
+		cw_min = (cw_min << 1) | 1;
+
+	cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) :
+		((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1);
+	cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) :
+		((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1);
+
+	/*
+	 * Calculate and set retry limits
+	 */
+	if (ah->ah_software_retry) {
+		/* XXX Need to test this */
+		retry_lg = ah->ah_limit_tx_retries;
+		retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ?
+			AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg;
+	} else {
+		retry_lg = AR5K_INIT_LG_RETRY;
+		retry_sh = AR5K_INIT_SH_RETRY;
+	}
+
+	/*No QCU/DCU [5210]*/
+	if (ah->ah_version == AR5K_AR5210) {
+		ath5k_hw_reg_write(ah,
+			(cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
+			| AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
+				AR5K_NODCU_RETRY_LMT_SLG_RETRY)
+			| AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
+				AR5K_NODCU_RETRY_LMT_SSH_RETRY)
+			| AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY)
+			| AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY),
+			AR5K_NODCU_RETRY_LMT);
+	} else {
+		/*QCU/DCU [5211+]*/
+		ath5k_hw_reg_write(ah,
+			AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
+				AR5K_DCU_RETRY_LMT_SLG_RETRY) |
+			AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
+				AR5K_DCU_RETRY_LMT_SSH_RETRY) |
+			AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) |
+			AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY),
+			AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
+
+	/*===Rest is also for QCU/DCU only [5211+]===*/
+
+		/*
+		 * Set initial content window (cw_min/cw_max)
+		 * and arbitrated interframe space (aifs)...
+		 */
+		ath5k_hw_reg_write(ah,
+			AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
+			AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
+			AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs,
+				AR5K_DCU_LCL_IFS_AIFS),
+			AR5K_QUEUE_DFS_LOCAL_IFS(queue));
+
+		/*
+		 * Set misc registers
+		 */
+		/* Enable DCU early termination for this queue */
+		AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+					AR5K_QCU_MISC_DCU_EARLY);
+
+		/* Enable DCU to wait for next fragment from QCU */
+		AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
+					AR5K_DCU_MISC_FRAG_WAIT);
+
+		/* On Maui and Spirit use the global seqnum on DCU */
+		if (ah->ah_mac_version < AR5K_SREV_AR5211)
+			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
+						AR5K_DCU_MISC_SEQNUM_CTL);
+
+		if (tq->tqi_cbr_period) {
+			ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period,
+				AR5K_QCU_CBRCFG_INTVAL) |
+				AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
+				AR5K_QCU_CBRCFG_ORN_THRES),
+				AR5K_QUEUE_CBRCFG(queue));
+			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+				AR5K_QCU_MISC_FRSHED_CBR);
+			if (tq->tqi_cbr_overflow_limit)
+				AR5K_REG_ENABLE_BITS(ah,
+					AR5K_QUEUE_MISC(queue),
+					AR5K_QCU_MISC_CBR_THRES_ENABLE);
+		}
+
+		if (tq->tqi_ready_time &&
+		(tq->tqi_type != AR5K_TX_QUEUE_ID_CAB))
+			ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time,
+				AR5K_QCU_RDYTIMECFG_INTVAL) |
+				AR5K_QCU_RDYTIMECFG_ENABLE,
+				AR5K_QUEUE_RDYTIMECFG(queue));
+
+		if (tq->tqi_burst_time) {
+			ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time,
+				AR5K_DCU_CHAN_TIME_DUR) |
+				AR5K_DCU_CHAN_TIME_ENABLE,
+				AR5K_QUEUE_DFS_CHANNEL_TIME(queue));
+
+			if (tq->tqi_flags
+			& AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)
+				AR5K_REG_ENABLE_BITS(ah,
+					AR5K_QUEUE_MISC(queue),
+					AR5K_QCU_MISC_RDY_VEOL_POLICY);
+		}
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
+			ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
+				AR5K_QUEUE_DFS_MISC(queue));
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
+			ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG,
+				AR5K_QUEUE_DFS_MISC(queue));
+
+		/* TODO: Handle frame compression */
+
+		/*
+		 * Enable interrupts for this tx queue
+		 * in the secondary interrupt mask registers
+		 */
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue);
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue);
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue);
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue);
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue);
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue);
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue);
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue);
+
+		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE)
+			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue);
+
+		/* Update secondary interrupt mask registers */
+
+		/* Filter out inactive queues */
+		ah->ah_txq_imr_txok &= ah->ah_txq_status;
+		ah->ah_txq_imr_txerr &= ah->ah_txq_status;
+		ah->ah_txq_imr_txurn &= ah->ah_txq_status;
+		ah->ah_txq_imr_txdesc &= ah->ah_txq_status;
+		ah->ah_txq_imr_txeol &= ah->ah_txq_status;
+		ah->ah_txq_imr_cbrorn &= ah->ah_txq_status;
+		ah->ah_txq_imr_cbrurn &= ah->ah_txq_status;
+		ah->ah_txq_imr_qtrig &= ah->ah_txq_status;
+		ah->ah_txq_imr_nofrm &= ah->ah_txq_status;
+
+		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok,
+			AR5K_SIMR0_QCU_TXOK) |
+			AR5K_REG_SM(ah->ah_txq_imr_txdesc,
+			AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0);
+		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr,
+			AR5K_SIMR1_QCU_TXERR) |
+			AR5K_REG_SM(ah->ah_txq_imr_txeol,
+			AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1);
+		/* Update simr2 but don't overwrite rest simr2 settings */
+		AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN);
+		AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2,
+			AR5K_REG_SM(ah->ah_txq_imr_txurn,
+			AR5K_SIMR2_QCU_TXURN));
+		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn,
+			AR5K_SIMR3_QCBRORN) |
+			AR5K_REG_SM(ah->ah_txq_imr_cbrurn,
+			AR5K_SIMR3_QCBRURN), AR5K_SIMR3);
+		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig,
+			AR5K_SIMR4_QTRIG), AR5K_SIMR4);
+		/* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
+		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm,
+			AR5K_TXNOFRM_QCU), AR5K_TXNOFRM);
+		/* No queue has TXNOFRM enabled, disable the interrupt
+		 * by setting AR5K_TXNOFRM to zero */
+		if (ah->ah_txq_imr_nofrm == 0)
+			ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM);
+
+		/* Set QCU mask for this DCU to save power */
+		AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue);
+	}
+
+	return 0;
+}
+
+/*
+ * Set slot time on DCU
+ */
+int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time)
+{
+	if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX)
+		return -EINVAL;
+
+	if (ah->ah_version == AR5K_AR5210)
+		ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time,
+				ah->ah_turbo), AR5K_SLOT_TIME);
+	else
+		ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT);
+
+	return 0;
+}
+
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_reset.c b/gpxe/src/drivers/net/ath5k/ath5k_reset.c
new file mode 100644
index 0000000..dc80093
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_reset.c
@@ -0,0 +1,1176 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ * Copyright (c) 2007-2008 Luis Rodriguez <mcgrof at winlab.rutgers.edu>
+ * Copyright (c) 2007-2008 Pavel Roskin <proski at gnu.org>
+ * Copyright (c) 2007-2008 Jiri Slaby <jirislaby at gmail.com>
+ *
+ * Lightly modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( MIT );
+
+#define _ATH5K_RESET
+
+/*****************************\
+  Reset functions and helpers
+\*****************************/
+
+#include <gpxe/pci.h> 		/* To determine if a card is pci-e */
+#include <unistd.h>
+
+#include "ath5k.h"
+#include "reg.h"
+#include "base.h"
+
+/* Find last set bit; fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32 */
+static int fls(int x)
+{
+        int r = 32;
+
+        if (!x)
+                return 0;
+        if (!(x & 0xffff0000u)) {
+                x <<= 16;
+                r -= 16;
+        }
+        if (!(x & 0xff000000u)) {
+                x <<= 8;
+                r -= 8;
+        }
+        if (!(x & 0xf0000000u)) {
+                x <<= 4;
+                r -= 4;
+        }
+        if (!(x & 0xc0000000u)) {
+                x <<= 2;
+                r -= 2;
+        }
+        if (!(x & 0x80000000u)) {
+                x <<= 1;
+                r -= 1;
+        }
+        return r;
+}
+
+
+/**
+ * ath5k_hw_write_ofdm_timings - set OFDM timings on AR5212
+ *
+ * @ah: the &struct ath5k_hw
+ * @channel: the currently set channel upon reset
+ *
+ * Write the delta slope coefficient (used on pilot tracking ?) for OFDM
+ * operation on the AR5212 upon reset. This is a helper for ath5k_hw_reset().
+ *
+ * Since delta slope is floating point we split it on its exponent and
+ * mantissa and provide these values on hw.
+ *
+ * For more infos i think this patent is related
+ * http://www.freepatentsonline.com/7184495.html
+ */
+static int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
+	struct net80211_channel *channel)
+{
+	/* Get exponent and mantissa and set it */
+	u32 coef_scaled, coef_exp, coef_man,
+		ds_coef_exp, ds_coef_man, clock;
+
+	if (!(ah->ah_version == AR5K_AR5212) ||
+	    !(channel->hw_value & CHANNEL_OFDM)) {
+		DBG("ath5k: attempt to set OFDM timings on non-OFDM channel\n");
+		return -EFAULT;
+	}
+
+	/* Get coefficient
+	 * ALGO: coef = (5 * clock * carrier_freq) / 2)
+	 * we scale coef by shifting clock value by 24 for
+	 * better precision since we use integers */
+	/* TODO: Half/quarter rate */
+	clock =  ath5k_hw_htoclock(1, channel->hw_value & CHANNEL_TURBO);
+
+	coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq;
+
+	/* Get exponent
+	 * ALGO: coef_exp = 14 - highest set bit position */
+	coef_exp = fls(coef_scaled) - 1;
+
+	/* Doesn't make sense if it's zero*/
+	if (!coef_scaled || !coef_exp)
+		return -EINVAL;
+
+	/* Note: we've shifted coef_scaled by 24 */
+	coef_exp = 14 - (coef_exp - 24);
+
+
+	/* Get mantissa (significant digits)
+	 * ALGO: coef_mant = floor(coef_scaled* 2^coef_exp+0.5) */
+	coef_man = coef_scaled +
+		(1 << (24 - coef_exp - 1));
+
+	/* Calculate delta slope coefficient exponent
+	 * and mantissa (remove scaling) and set them on hw */
+	ds_coef_man = coef_man >> (24 - coef_exp);
+	ds_coef_exp = coef_exp - 16;
+
+	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
+		AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man);
+	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
+		AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp);
+
+	return 0;
+}
+
+
+/*
+ * index into rates for control rates, we can set it up like this because
+ * this is only used for AR5212 and we know it supports G mode
+ */
+static const unsigned int control_rates[] =
+	{ 0, 1, 1, 1, 4, 4, 6, 6, 8, 8, 8, 8 };
+
+/**
+ * ath5k_hw_write_rate_duration - fill rate code to duration table
+ *
+ * @ah: the &struct ath5k_hw
+ * @mode: one of enum ath5k_driver_mode
+ *
+ * Write the rate code to duration table upon hw reset. This is a helper for
+ * ath5k_hw_reset(). It seems all this is doing is setting an ACK timeout on
+ * the hardware, based on current mode, for each rate. The rates which are
+ * capable of short preamble (802.11b rates 2Mbps, 5.5Mbps, and 11Mbps) have
+ * different rate code so we write their value twice (one for long preample
+ * and one for short).
+ *
+ * Note: Band doesn't matter here, if we set the values for OFDM it works
+ * on both a and g modes. So all we have to do is set values for all g rates
+ * that include all OFDM and CCK rates. If we operate in turbo or xr/half/
+ * quarter rate mode, we need to use another set of bitrates (that's why we
+ * need the mode parameter) but we don't handle these proprietary modes yet.
+ */
+static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah,
+       unsigned int mode __unused)
+{
+	struct ath5k_softc *sc = ah->ah_sc;
+	u16 rate;
+	int i;
+
+	/* Write rate duration table */
+	for (i = 0; i < sc->hwinfo->nr_rates[NET80211_BAND_2GHZ]; i++) {
+		u32 reg;
+		u16 tx_time;
+
+		rate = sc->hwinfo->rates[NET80211_BAND_2GHZ][i];
+
+		/* Set ACK timeout */
+		reg = AR5K_RATE_DUR(ath5k_bitrate_to_hw_rix(rate));
+
+		/* An ACK frame consists of 10 bytes. If you add the FCS,
+		 * it's 14 bytes. Note we use the control rate and not the
+		 * actual rate for this rate. See mac80211 tx.c
+		 * ieee80211_duration() for a brief description of
+		 * what rate we should choose to TX ACKs. */
+		tx_time = net80211_duration(sc->dev, 14, rate);
+
+		ath5k_hw_reg_write(ah, tx_time, reg);
+
+		if (rate != 20 && rate != 55 && rate != 110)
+			continue;
+
+		/*
+		 * We're not distinguishing short preamble here,
+		 * This is true, all we'll get is a longer value here
+		 * which is not necessarilly bad.
+		 */
+		ath5k_hw_reg_write(ah, tx_time,
+			reg + (AR5K_SET_SHORT_PREAMBLE << 2));
+	}
+}
+
+/*
+ * Reset chipset
+ */
+static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val)
+{
+	int ret;
+	u32 mask = val ? val : ~0U;
+
+	/* Read-and-clear RX Descriptor Pointer*/
+	ath5k_hw_reg_read(ah, AR5K_RXDP);
+
+	/*
+	 * Reset the device and wait until success
+	 */
+	ath5k_hw_reg_write(ah, val, AR5K_RESET_CTL);
+
+	/* Wait at least 128 PCI clocks */
+	udelay(15);
+
+	if (ah->ah_version == AR5K_AR5210) {
+		val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA
+			| AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY;
+		mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA
+			| AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY;
+	} else {
+		val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND;
+		mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND;
+	}
+
+	ret = ath5k_hw_register_timeout(ah, AR5K_RESET_CTL, mask, val, 0);
+
+	/*
+	 * Reset configuration register (for hw byte-swap). Note that this
+	 * is only set for big endian. We do the necessary magic in
+	 * AR5K_INIT_CFG.
+	 */
+	if ((val & AR5K_RESET_CTL_PCU) == 0)
+		ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG);
+
+	return ret;
+}
+
+/*
+ * Sleep control
+ */
+int ath5k_hw_wake(struct ath5k_hw *ah)
+{
+	unsigned int i;
+	u32 staid, data;
+
+	staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1);
+	staid &= ~AR5K_STA_ID1_PWR_SV;
+
+	/* Preserve sleep duration */
+	data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL);
+	if (data & 0xffc00000)
+		data = 0;
+	else
+		data = data & 0xfffcffff;
+
+	ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL);
+	udelay(15);
+
+	for (i = 50; i > 0; i--) {
+		/* Check if the chip did wake up */
+		if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) &
+		     AR5K_PCICFG_SPWR_DN) == 0)
+			break;
+
+		/* Wait a bit and retry */
+		udelay(200);
+		ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL);
+	}
+
+	/* Fail if the chip didn't wake up */
+	if (i <= 0)
+		return -EIO;
+
+	ath5k_hw_reg_write(ah, staid, AR5K_STA_ID1);
+
+	return 0;
+}
+
+/*
+ * Bring up MAC + PHY Chips and program PLL
+ * TODO: Half/Quarter rate support
+ */
+int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, int initial __unused)
+{
+	struct pci_device *pdev = ah->ah_sc->pdev;
+	u32 turbo, mode, clock, bus_flags;
+	int ret;
+
+	turbo = 0;
+	mode = 0;
+	clock = 0;
+
+	/* Wakeup the device */
+	ret = ath5k_hw_wake(ah);
+	if (ret) {
+		DBG("ath5k: failed to wake up the MAC chip\n");
+		return ret;
+	}
+
+	if (ah->ah_version != AR5K_AR5210) {
+		/*
+		 * Get channel mode flags
+		 */
+
+		if (ah->ah_radio >= AR5K_RF5112) {
+			mode = AR5K_PHY_MODE_RAD_RF5112;
+			clock = AR5K_PHY_PLL_RF5112;
+		} else {
+			mode = AR5K_PHY_MODE_RAD_RF5111;	/*Zero*/
+			clock = AR5K_PHY_PLL_RF5111;		/*Zero*/
+		}
+
+		if (flags & CHANNEL_2GHZ) {
+			mode |= AR5K_PHY_MODE_FREQ_2GHZ;
+			clock |= AR5K_PHY_PLL_44MHZ;
+
+			if (flags & CHANNEL_CCK) {
+				mode |= AR5K_PHY_MODE_MOD_CCK;
+			} else if (flags & CHANNEL_OFDM) {
+				/* XXX Dynamic OFDM/CCK is not supported by the
+				 * AR5211 so we set MOD_OFDM for plain g (no
+				 * CCK headers) operation. We need to test
+				 * this, 5211 might support ofdm-only g after
+				 * all, there are also initial register values
+				 * in the code for g mode (see initvals.c). */
+				if (ah->ah_version == AR5K_AR5211)
+					mode |= AR5K_PHY_MODE_MOD_OFDM;
+				else
+					mode |= AR5K_PHY_MODE_MOD_DYN;
+			} else {
+				DBG("ath5k: invalid radio modulation mode\n");
+				return -EINVAL;
+			}
+		} else if (flags & CHANNEL_5GHZ) {
+			mode |= AR5K_PHY_MODE_FREQ_5GHZ;
+
+			if (ah->ah_radio == AR5K_RF5413)
+				clock = AR5K_PHY_PLL_40MHZ_5413;
+			else
+				clock |= AR5K_PHY_PLL_40MHZ;
+
+			if (flags & CHANNEL_OFDM)
+				mode |= AR5K_PHY_MODE_MOD_OFDM;
+			else {
+				DBG("ath5k: invalid radio modulation mode\n");
+				return -EINVAL;
+			}
+		} else {
+			DBG("ath5k: invalid radio frequency mode\n");
+			return -EINVAL;
+		}
+
+		if (flags & CHANNEL_TURBO)
+			turbo = AR5K_PHY_TURBO_MODE | AR5K_PHY_TURBO_SHORT;
+	} else { /* Reset the device */
+
+		/* ...enable Atheros turbo mode if requested */
+		if (flags & CHANNEL_TURBO)
+			ath5k_hw_reg_write(ah, AR5K_PHY_TURBO_MODE,
+					AR5K_PHY_TURBO);
+	}
+
+	/* reseting PCI on PCI-E cards results card to hang
+	 * and always return 0xffff... so we ingore that flag
+	 * for PCI-E cards */
+	if (pci_find_capability(pdev, PCI_CAP_ID_EXP))
+		bus_flags = 0;
+	else
+		bus_flags = AR5K_RESET_CTL_PCI;
+
+	/* Reset chipset */
+	if (ah->ah_version == AR5K_AR5210) {
+		ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
+			AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
+			AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
+		mdelay(2);
+	} else {
+		ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
+			AR5K_RESET_CTL_BASEBAND | bus_flags);
+	}
+	if (ret) {
+		DBG("ath5k: failed to reset the MAC chip\n");
+		return -EIO;
+	}
+
+	/* ...wakeup again!*/
+	ret = ath5k_hw_wake(ah);
+	if (ret) {
+		DBG("ath5k: failed to resume the MAC chip\n");
+		return ret;
+	}
+
+	/* ...final warm reset */
+	if (ath5k_hw_nic_reset(ah, 0)) {
+		DBG("ath5k: failed to warm reset the MAC chip\n");
+		return -EIO;
+	}
+
+	if (ah->ah_version != AR5K_AR5210) {
+
+		/* ...update PLL if needed */
+		if (ath5k_hw_reg_read(ah, AR5K_PHY_PLL) != clock) {
+			ath5k_hw_reg_write(ah, clock, AR5K_PHY_PLL);
+			udelay(300);
+		}
+
+		/* ...set the PHY operating mode */
+		ath5k_hw_reg_write(ah, mode, AR5K_PHY_MODE);
+		ath5k_hw_reg_write(ah, turbo, AR5K_PHY_TURBO);
+	}
+
+	return 0;
+}
+
+static int ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
+				struct net80211_channel *channel)
+{
+	u8 refclk_freq;
+
+	if ((ah->ah_radio == AR5K_RF5112) ||
+	(ah->ah_radio == AR5K_RF5413) ||
+	(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
+		refclk_freq = 40;
+	else
+		refclk_freq = 32;
+
+	if ((channel->center_freq % refclk_freq != 0) &&
+	((channel->center_freq % refclk_freq < 10) ||
+	(channel->center_freq % refclk_freq > 22)))
+		return 1;
+	else
+		return 0;
+}
+
+/* TODO: Half/Quarter rate */
+static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
+				struct net80211_channel *channel)
+{
+	if (ah->ah_version == AR5K_AR5212 &&
+	    ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
+
+		/* Setup ADC control */
+		ath5k_hw_reg_write(ah,
+				(AR5K_REG_SM(2,
+				AR5K_PHY_ADC_CTL_INBUFGAIN_OFF) |
+				AR5K_REG_SM(2,
+				AR5K_PHY_ADC_CTL_INBUFGAIN_ON) |
+				AR5K_PHY_ADC_CTL_PWD_DAC_OFF |
+				AR5K_PHY_ADC_CTL_PWD_ADC_OFF),
+				AR5K_PHY_ADC_CTL);
+
+
+
+		/* Disable barker RSSI threshold */
+		AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_DAG_CCK_CTL,
+				AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR);
+
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DAG_CCK_CTL,
+			AR5K_PHY_DAG_CCK_CTL_RSSI_THR, 2);
+
+		/* Set the mute mask */
+		ath5k_hw_reg_write(ah, 0x0000000f, AR5K_SEQ_MASK);
+	}
+
+	/* Clear PHY_BLUETOOTH to allow RX_CLEAR line debug */
+	if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212B)
+		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BLUETOOTH);
+
+	/* Enable DCU double buffering */
+	if (ah->ah_phy_revision > AR5K_SREV_PHY_5212B)
+		AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
+				AR5K_TXCFG_DCU_DBL_BUF_DIS);
+
+	/* Set DAC/ADC delays */
+	if (ah->ah_version == AR5K_AR5212) {
+		u32 scal;
+		if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))
+			scal = AR5K_PHY_SCAL_32MHZ_2417;
+		else if (ath5k_eeprom_is_hb63(ah))
+			scal = AR5K_PHY_SCAL_32MHZ_HB63;
+		else
+			scal = AR5K_PHY_SCAL_32MHZ;
+		ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL);
+	}
+
+	/* Set fast ADC */
+	if ((ah->ah_radio == AR5K_RF5413) ||
+	(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
+		u32 fast_adc = 1;
+
+		if (channel->center_freq == 2462 ||
+		channel->center_freq == 2467)
+			fast_adc = 0;
+
+		/* Only update if needed */
+		if (ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ADC) != fast_adc)
+				ath5k_hw_reg_write(ah, fast_adc,
+						AR5K_PHY_FAST_ADC);
+	}
+
+	/* Fix for first revision of the RF5112 RF chipset */
+	if (ah->ah_radio == AR5K_RF5112 &&
+			ah->ah_radio_5ghz_revision <
+			AR5K_SREV_RAD_5112A) {
+		u32 data;
+		ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD,
+				AR5K_PHY_CCKTXCTL);
+		if (channel->hw_value & CHANNEL_5GHZ)
+			data = 0xffb81020;
+		else
+			data = 0xffb80d20;
+		ath5k_hw_reg_write(ah, data, AR5K_PHY_FRAME_CTL);
+	}
+
+	if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+		u32 usec_reg;
+		/* 5311 has different tx/rx latency masks
+		 * from 5211, since we deal 5311 the same
+		 * as 5211 when setting initvals, shift
+		 * values here to their proper locations */
+		usec_reg = ath5k_hw_reg_read(ah, AR5K_USEC_5211);
+		ath5k_hw_reg_write(ah, usec_reg & (AR5K_USEC_1 |
+				AR5K_USEC_32 |
+				AR5K_USEC_TX_LATENCY_5211 |
+				AR5K_REG_SM(29,
+				AR5K_USEC_RX_LATENCY_5210)),
+				AR5K_USEC_5211);
+		/* Clear QCU/DCU clock gating register */
+		ath5k_hw_reg_write(ah, 0, AR5K_QCUDCU_CLKGT);
+		/* Set DAC/ADC delays */
+		ath5k_hw_reg_write(ah, 0x08, AR5K_PHY_SCAL);
+		/* Enable PCU FIFO corruption ECO */
+		AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
+					AR5K_DIAG_SW_ECO_ENABLE);
+	}
+}
+
+static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
+		struct net80211_channel *channel, u8 *ant, u8 ee_mode)
+{
+	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+	s16 cck_ofdm_pwr_delta;
+
+	/* Adjust power delta for channel 14 */
+	if (channel->center_freq == 2484)
+		cck_ofdm_pwr_delta =
+			((ee->ee_cck_ofdm_power_delta -
+			ee->ee_scaled_cck_delta) * 2) / 10;
+	else
+		cck_ofdm_pwr_delta =
+			(ee->ee_cck_ofdm_power_delta * 2) / 10;
+
+	/* Set CCK to OFDM power delta on tx power
+	 * adjustment register */
+	if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
+		if (channel->hw_value == CHANNEL_G)
+			ath5k_hw_reg_write(ah,
+			AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1),
+				AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) |
+			AR5K_REG_SM((cck_ofdm_pwr_delta * -1),
+				AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX),
+				AR5K_PHY_TX_PWR_ADJ);
+		else
+			ath5k_hw_reg_write(ah, 0, AR5K_PHY_TX_PWR_ADJ);
+	} else {
+		/* For older revs we scale power on sw during tx power
+		 * setup */
+		ah->ah_txpower.txp_cck_ofdm_pwr_delta = cck_ofdm_pwr_delta;
+		ah->ah_txpower.txp_cck_ofdm_gainf_delta =
+						ee->ee_cck_ofdm_gain_delta;
+	}
+
+	/* Set antenna idle switch table */
+	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_ANT_CTL,
+			AR5K_PHY_ANT_CTL_SWTABLE_IDLE,
+			(ah->ah_antenna[ee_mode][0] |
+			AR5K_PHY_ANT_CTL_TXRX_EN));
+
+	/* Set antenna switch table */
+	ath5k_hw_reg_write(ah, ah->ah_antenna[ee_mode][ant[0]],
+		AR5K_PHY_ANT_SWITCH_TABLE_0);
+	ath5k_hw_reg_write(ah, ah->ah_antenna[ee_mode][ant[1]],
+		AR5K_PHY_ANT_SWITCH_TABLE_1);
+
+	/* Noise floor threshold */
+	ath5k_hw_reg_write(ah,
+		AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]),
+		AR5K_PHY_NFTHRES);
+
+	if ((channel->hw_value & CHANNEL_TURBO) &&
+	(ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0)) {
+		/* Switch settling time (Turbo) */
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
+				AR5K_PHY_SETTLING_SWITCH,
+				ee->ee_switch_settling_turbo[ee_mode]);
+
+		/* Tx/Rx attenuation (Turbo) */
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN,
+				AR5K_PHY_GAIN_TXRX_ATTEN,
+				ee->ee_atn_tx_rx_turbo[ee_mode]);
+
+		/* ADC/PGA desired size (Turbo) */
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+				AR5K_PHY_DESIRED_SIZE_ADC,
+				ee->ee_adc_desired_size_turbo[ee_mode]);
+
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+				AR5K_PHY_DESIRED_SIZE_PGA,
+				ee->ee_pga_desired_size_turbo[ee_mode]);
+
+		/* Tx/Rx margin (Turbo) */
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ,
+				AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX,
+				ee->ee_margin_tx_rx_turbo[ee_mode]);
+
+	} else {
+		/* Switch settling time */
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
+				AR5K_PHY_SETTLING_SWITCH,
+				ee->ee_switch_settling[ee_mode]);
+
+		/* Tx/Rx attenuation */
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN,
+				AR5K_PHY_GAIN_TXRX_ATTEN,
+				ee->ee_atn_tx_rx[ee_mode]);
+
+		/* ADC/PGA desired size */
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+				AR5K_PHY_DESIRED_SIZE_ADC,
+				ee->ee_adc_desired_size[ee_mode]);
+
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
+				AR5K_PHY_DESIRED_SIZE_PGA,
+				ee->ee_pga_desired_size[ee_mode]);
+
+		/* Tx/Rx margin */
+		if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+			AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ,
+				AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX,
+				ee->ee_margin_tx_rx[ee_mode]);
+	}
+
+	/* XPA delays */
+	ath5k_hw_reg_write(ah,
+		(ee->ee_tx_end2xpa_disable[ee_mode] << 24) |
+		(ee->ee_tx_end2xpa_disable[ee_mode] << 16) |
+		(ee->ee_tx_frm2xpa_enable[ee_mode] << 8) |
+		(ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY_RF_CTL4);
+
+	/* XLNA delay */
+	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL3,
+			AR5K_PHY_RF_CTL3_TXE2XLNA_ON,
+			ee->ee_tx_end2xlna_enable[ee_mode]);
+
+	/* Thresh64 (ANI) */
+	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_NF,
+			AR5K_PHY_NF_THRESH62,
+			ee->ee_thr_62[ee_mode]);
+
+
+	/* False detect backoff for channels
+	 * that have spur noise. Write the new
+	 * cyclic power RSSI threshold. */
+	if (ath5k_hw_chan_has_spur_noise(ah, channel))
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
+				AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1,
+				AR5K_INIT_CYCRSSI_THR1 +
+				ee->ee_false_detect[ee_mode]);
+	else
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
+				AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1,
+				AR5K_INIT_CYCRSSI_THR1);
+
+	/* I/Q correction
+	 * TODO: Per channel i/q infos ? */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
+		AR5K_PHY_IQ_CORR_ENABLE |
+		(ee->ee_i_cal[ee_mode] << AR5K_PHY_IQ_CORR_Q_I_COFF_S) |
+		ee->ee_q_cal[ee_mode]);
+
+	/* Heavy clipping -disable for now */
+	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_1)
+		ath5k_hw_reg_write(ah, 0, AR5K_PHY_HEAVY_CLIP_ENABLE);
+
+	return;
+}
+
+/*
+ * Main reset function
+ */
+int ath5k_hw_reset(struct ath5k_hw *ah,
+	struct net80211_channel *channel, int change_channel)
+{
+	u32 s_seq[10], s_ant, s_led[3], staid1_flags, tsf_up, tsf_lo;
+	u32 phy_tst1;
+	u8 mode, freq, ee_mode, ant[2];
+	int i, ret;
+
+	s_ant = 0;
+	ee_mode = 0;
+	staid1_flags = 0;
+	tsf_up = 0;
+	tsf_lo = 0;
+	freq = 0;
+	mode = 0;
+
+	/*
+	 * Save some registers before a reset
+	 */
+	/*DCU/Antenna selection not available on 5210*/
+	if (ah->ah_version != AR5K_AR5210) {
+
+		switch (channel->hw_value & CHANNEL_MODES) {
+		case CHANNEL_A:
+			mode = AR5K_MODE_11A;
+			freq = AR5K_INI_RFGAIN_5GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11A;
+			break;
+		case CHANNEL_G:
+			mode = AR5K_MODE_11G;
+			freq = AR5K_INI_RFGAIN_2GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11G;
+			break;
+		case CHANNEL_B:
+			mode = AR5K_MODE_11B;
+			freq = AR5K_INI_RFGAIN_2GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11B;
+			break;
+		case CHANNEL_T:
+			mode = AR5K_MODE_11A_TURBO;
+			freq = AR5K_INI_RFGAIN_5GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11A;
+			break;
+		case CHANNEL_TG:
+			if (ah->ah_version == AR5K_AR5211) {
+				DBG("ath5k: TurboG not available on 5211\n");
+				return -EINVAL;
+			}
+			mode = AR5K_MODE_11G_TURBO;
+			freq = AR5K_INI_RFGAIN_2GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11G;
+			break;
+		case CHANNEL_XR:
+			if (ah->ah_version == AR5K_AR5211) {
+				DBG("ath5k: XR mode not available on 5211\n");
+				return -EINVAL;
+			}
+			mode = AR5K_MODE_XR;
+			freq = AR5K_INI_RFGAIN_5GHZ;
+			ee_mode = AR5K_EEPROM_MODE_11A;
+			break;
+		default:
+			DBG("ath5k: invalid channel (%d MHz)\n",
+			    channel->center_freq);
+			return -EINVAL;
+		}
+
+		if (change_channel) {
+			/*
+			 * Save frame sequence count
+			 * For revs. after Oahu, only save
+			 * seq num for DCU 0 (Global seq num)
+			 */
+			if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+
+				for (i = 0; i < 10; i++)
+					s_seq[i] = ath5k_hw_reg_read(ah,
+						AR5K_QUEUE_DCU_SEQNUM(i));
+
+			} else {
+				s_seq[0] = ath5k_hw_reg_read(ah,
+						AR5K_QUEUE_DCU_SEQNUM(0));
+			}
+		}
+
+		/* Save default antenna */
+		s_ant = ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA);
+
+		if (ah->ah_version == AR5K_AR5212) {
+			/* Since we are going to write rf buffer
+			 * check if we have any pending gain_F
+			 * optimization settings */
+			if (change_channel && ah->ah_rf_banks != NULL)
+				ath5k_hw_gainf_calibrate(ah);
+		}
+	}
+
+	/*GPIOs*/
+	s_led[0] = ath5k_hw_reg_read(ah, AR5K_PCICFG) &
+					AR5K_PCICFG_LEDSTATE;
+	s_led[1] = ath5k_hw_reg_read(ah, AR5K_GPIOCR);
+	s_led[2] = ath5k_hw_reg_read(ah, AR5K_GPIODO);
+
+	/* AR5K_STA_ID1 flags, only preserve antenna
+	 * settings and ack/cts rate mode */
+	staid1_flags = ath5k_hw_reg_read(ah, AR5K_STA_ID1) &
+			(AR5K_STA_ID1_DEFAULT_ANTENNA |
+			AR5K_STA_ID1_DESC_ANTENNA |
+			AR5K_STA_ID1_RTS_DEF_ANTENNA |
+			AR5K_STA_ID1_ACKCTS_6MB |
+			AR5K_STA_ID1_BASE_RATE_11B |
+			AR5K_STA_ID1_SELFGEN_DEF_ANT);
+
+	/* Wakeup the device */
+	ret = ath5k_hw_nic_wakeup(ah, channel->hw_value, 0);
+	if (ret)
+		return ret;
+
+	/* PHY access enable */
+	if (ah->ah_mac_srev >= AR5K_SREV_AR5211)
+		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
+	else
+		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ | 0x40,
+							AR5K_PHY(0));
+
+	/* Write initial settings */
+	ret = ath5k_hw_write_initvals(ah, mode, change_channel);
+	if (ret)
+		return ret;
+
+	/*
+	 * 5211/5212 Specific
+	 */
+	if (ah->ah_version != AR5K_AR5210) {
+
+		/*
+		 * Write initial RF gain settings
+		 * This should work for both 5111/5112
+		 */
+		ret = ath5k_hw_rfgain_init(ah, freq);
+		if (ret)
+			return ret;
+
+		mdelay(1);
+
+		/*
+		 * Tweak initval settings for revised
+		 * chipsets and add some more config
+		 * bits
+		 */
+		ath5k_hw_tweak_initval_settings(ah, channel);
+
+		/*
+		 * Set TX power (FIXME)
+		 */
+		ret = ath5k_hw_txpower(ah, channel, ee_mode,
+					AR5K_TUNE_DEFAULT_TXPOWER);
+		if (ret)
+			return ret;
+
+		/* Write rate duration table only on AR5212 */
+		if (ah->ah_version == AR5K_AR5212)
+			ath5k_hw_write_rate_duration(ah, mode);
+
+		/*
+		 * Write RF buffer
+		 */
+		ret = ath5k_hw_rfregs_init(ah, channel, mode);
+		if (ret)
+			return ret;
+
+
+		/* Write OFDM timings on 5212*/
+		if (ah->ah_version == AR5K_AR5212 &&
+			channel->hw_value & CHANNEL_OFDM) {
+			ret = ath5k_hw_write_ofdm_timings(ah, channel);
+			if (ret)
+				return ret;
+		}
+
+		/*Enable/disable 802.11b mode on 5111
+		(enable 2111 frequency converter + CCK)*/
+		if (ah->ah_radio == AR5K_RF5111) {
+			if (mode == AR5K_MODE_11B)
+				AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG,
+				    AR5K_TXCFG_B_MODE);
+			else
+				AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
+				    AR5K_TXCFG_B_MODE);
+		}
+
+		/*
+		 * In case a fixed antenna was set as default
+		 * write the same settings on both AR5K_PHY_ANT_SWITCH_TABLE
+		 * registers.
+		 */
+		if (s_ant != 0) {
+			if (s_ant == AR5K_ANT_FIXED_A) /* 1 - Main */
+				ant[0] = ant[1] = AR5K_ANT_FIXED_A;
+			else	/* 2 - Aux */
+				ant[0] = ant[1] = AR5K_ANT_FIXED_B;
+		} else {
+			ant[0] = AR5K_ANT_FIXED_A;
+			ant[1] = AR5K_ANT_FIXED_B;
+		}
+
+		/* Commit values from EEPROM */
+		ath5k_hw_commit_eeprom_settings(ah, channel, ant, ee_mode);
+
+	} else {
+		/*
+		 * For 5210 we do all initialization using
+		 * initvals, so we don't have to modify
+		 * any settings (5210 also only supports
+		 * a/aturbo modes)
+		 */
+		mdelay(1);
+		/* Disable phy and wait */
+		ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
+		mdelay(1);
+	}
+
+	/*
+	 * Restore saved values
+	 */
+
+	/*DCU/Antenna selection not available on 5210*/
+	if (ah->ah_version != AR5K_AR5210) {
+
+		if (change_channel) {
+			if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+				for (i = 0; i < 10; i++)
+					ath5k_hw_reg_write(ah, s_seq[i],
+						AR5K_QUEUE_DCU_SEQNUM(i));
+			} else {
+				ath5k_hw_reg_write(ah, s_seq[0],
+					AR5K_QUEUE_DCU_SEQNUM(0));
+			}
+		}
+
+		ath5k_hw_reg_write(ah, s_ant, AR5K_DEFAULT_ANTENNA);
+	}
+
+	/* Ledstate */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, s_led[0]);
+
+	/* Gpio settings */
+	ath5k_hw_reg_write(ah, s_led[1], AR5K_GPIOCR);
+	ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO);
+
+	/* Restore sta_id flags and preserve our mac address*/
+	ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_sta_id),
+						AR5K_STA_ID0);
+	ath5k_hw_reg_write(ah, staid1_flags | AR5K_HIGH_ID(ah->ah_sta_id),
+						AR5K_STA_ID1);
+
+
+	/*
+	 * Configure PCU
+	 */
+
+	/* Restore bssid and bssid mask */
+	/* XXX: add ah->aid once mac80211 gives this to us */
+	ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
+
+	/* Set PCU config */
+	ath5k_hw_set_opmode(ah);
+
+	/* Clear any pending interrupts
+	 * PISR/SISR Not available on 5210 */
+	if (ah->ah_version != AR5K_AR5210)
+		ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR);
+
+	/* Set RSSI/BRSSI thresholds
+	 *
+	 * Note: If we decide to set this value
+	 * dynamicaly, have in mind that when AR5K_RSSI_THR
+	 * register is read it might return 0x40 if we haven't
+	 * wrote anything to it plus BMISS RSSI threshold is zeroed.
+	 * So doing a save/restore procedure here isn't the right
+	 * choice. Instead store it on ath5k_hw */
+	ath5k_hw_reg_write(ah, (AR5K_TUNE_RSSI_THRES |
+				AR5K_TUNE_BMISS_THRES <<
+				AR5K_RSSI_THR_BMISS_S),
+				AR5K_RSSI_THR);
+
+	/* MIC QoS support */
+	if (ah->ah_mac_srev >= AR5K_SREV_AR2413) {
+		ath5k_hw_reg_write(ah, 0x000100aa, AR5K_MIC_QOS_CTL);
+		ath5k_hw_reg_write(ah, 0x00003210, AR5K_MIC_QOS_SEL);
+	}
+
+	/* QoS NOACK Policy */
+	if (ah->ah_version == AR5K_AR5212) {
+		ath5k_hw_reg_write(ah,
+			AR5K_REG_SM(2, AR5K_QOS_NOACK_2BIT_VALUES) |
+			AR5K_REG_SM(5, AR5K_QOS_NOACK_BIT_OFFSET)  |
+			AR5K_REG_SM(0, AR5K_QOS_NOACK_BYTE_OFFSET),
+			AR5K_QOS_NOACK);
+	}
+
+
+	/*
+	 * Configure PHY
+	 */
+
+	/* Set channel on PHY */
+	ret = ath5k_hw_channel(ah, channel);
+	if (ret)
+		return ret;
+
+	/*
+	 * Enable the PHY and wait until completion
+	 * This includes BaseBand and Synthesizer
+	 * activation.
+	 */
+	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
+
+	/*
+	 * On 5211+ read activation -> rx delay
+	 * and use it.
+	 *
+	 * TODO: Half/quarter rate support
+	 */
+	if (ah->ah_version != AR5K_AR5210) {
+		u32 delay;
+		delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) &
+			AR5K_PHY_RX_DELAY_M;
+		delay = (channel->hw_value & CHANNEL_CCK) ?
+			((delay << 2) / 22) : (delay / 10);
+
+		udelay(100 + (2 * delay));
+	} else {
+		mdelay(1);
+	}
+
+	/*
+	 * Perform ADC test to see if baseband is ready
+	 * Set tx hold and check adc test register
+	 */
+	phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1);
+	ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1);
+	for (i = 0; i <= 20; i++) {
+		if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10))
+			break;
+		udelay(200);
+	}
+	ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1);
+
+	/*
+	 * Start automatic gain control calibration
+	 *
+	 * During AGC calibration RX path is re-routed to
+	 * a power detector so we don't receive anything.
+	 *
+	 * This method is used to calibrate some static offsets
+	 * used together with on-the fly I/Q calibration (the
+	 * one performed via ath5k_hw_phy_calibrate), that doesn't
+	 * interrupt rx path.
+	 *
+	 * While rx path is re-routed to the power detector we also
+	 * start a noise floor calibration, to measure the
+	 * card's noise floor (the noise we measure when we are not
+	 * transmiting or receiving anything).
+	 *
+	 * If we are in a noisy environment AGC calibration may time
+	 * out and/or noise floor calibration might timeout.
+	 */
+	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
+				AR5K_PHY_AGCCTL_CAL);
+
+	/* At the same time start I/Q calibration for QAM constellation
+	 * -no need for CCK- */
+	ah->ah_calibration = 0;
+	if (!(mode == AR5K_MODE_11B)) {
+		ah->ah_calibration = 1;
+		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
+				AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
+		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
+				AR5K_PHY_IQ_RUN);
+	}
+
+	/* Wait for gain calibration to finish (we check for I/Q calibration
+	 * during ath5k_phy_calibrate) */
+	if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
+			AR5K_PHY_AGCCTL_CAL, 0, 0)) {
+		DBG("ath5k: gain calibration timeout (%d MHz)\n",
+		    channel->center_freq);
+	}
+
+	/*
+	 * If we run NF calibration before AGC, it always times out.
+	 * Binary HAL starts NF and AGC calibration at the same time
+	 * and only waits for AGC to finish. Also if AGC or NF cal.
+	 * times out, reset doesn't fail on binary HAL. I believe
+	 * that's wrong because since rx path is routed to a detector,
+	 * if cal. doesn't finish we won't have RX. Sam's HAL for AR5210/5211
+	 * enables noise floor calibration after offset calibration and if noise
+	 * floor calibration fails, reset fails. I believe that's
+	 * a better approach, we just need to find a polling interval
+	 * that suits best, even if reset continues we need to make
+	 * sure that rx path is ready.
+	 */
+	ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
+
+
+	/*
+	 * Configure QCUs/DCUs
+	 */
+
+	/* TODO: HW Compression support for data queues */
+	/* TODO: Burst prefetch for data queues */
+
+	/*
+	 * Reset queues and start beacon timers at the end of the reset routine
+	 * This also sets QCU mask on each DCU for 1:1 qcu to dcu mapping
+	 * Note: If we want we can assign multiple qcus on one dcu.
+	 */
+	ret = ath5k_hw_reset_tx_queue(ah);
+	if (ret) {
+		DBG("ath5k: failed to reset TX queue\n");
+		return ret;
+	}
+
+	/*
+	 * Configure DMA/Interrupts
+	 */
+
+	/*
+	 * Set Rx/Tx DMA Configuration
+	 *
+	 * Set standard DMA size (128). Note that
+	 * a DMA size of 512 causes rx overruns and tx errors
+	 * on pci-e cards (tested on 5424 but since rx overruns
+	 * also occur on 5416/5418 with madwifi we set 128
+	 * for all PCI-E cards to be safe).
+	 *
+	 * XXX: need to check 5210 for this
+	 * TODO: Check out tx triger level, it's always 64 on dumps but I
+	 * guess we can tweak it and see how it goes ;-)
+	 */
+	if (ah->ah_version != AR5K_AR5210) {
+		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
+			AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
+		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
+			AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
+	}
+
+	/* Pre-enable interrupts on 5211/5212*/
+	if (ah->ah_version != AR5K_AR5210)
+		ath5k_hw_set_imr(ah, ah->ah_imr);
+
+	/*
+	 * Setup RFKill interrupt if rfkill flag is set on eeprom.
+	 * TODO: Use gpio pin and polarity infos from eeprom
+	 * TODO: Handle this in ath5k_intr because it'll result
+	 * 	 a nasty interrupt storm.
+	 */
+#if 0
+	if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) {
+		ath5k_hw_set_gpio_input(ah, 0);
+		ah->ah_gpio[0] = ath5k_hw_get_gpio(ah, 0);
+		if (ah->ah_gpio[0] == 0)
+			ath5k_hw_set_gpio_intr(ah, 0, 1);
+		else
+			ath5k_hw_set_gpio_intr(ah, 0, 0);
+	}
+#endif
+
+	/*
+	 * Disable beacons and reset the register
+	 */
+	AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE |
+			AR5K_BEACON_RESET_TSF);
+
+	return 0;
+}
+
+#undef _ATH5K_RESET
diff --git a/gpxe/src/drivers/net/ath5k/ath5k_rfkill.c b/gpxe/src/drivers/net/ath5k/ath5k_rfkill.c
new file mode 100644
index 0000000..9d0a2ff
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/ath5k_rfkill.c
@@ -0,0 +1,107 @@
+/*
+ * RFKILL support for ath5k
+ *
+ * Copyright (c) 2009 Tobias Doerffel <tobias.doerffel at gmail.com>
+ * Lightly modified for gPXE, Sep 2008 by Joshua Oreman <oremanj at rwcr.net>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+FILE_LICENCE ( MIT );
+
+#include "base.h"
+
+
+static inline void ath5k_rfkill_disable(struct ath5k_softc *sc)
+{
+	DBG("ath5k: rfkill disable (gpio:%d polarity:%d)\n",
+	    sc->rf_kill.gpio, sc->rf_kill.polarity);
+	ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio);
+	ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, !sc->rf_kill.polarity);
+}
+
+
+static inline void ath5k_rfkill_enable(struct ath5k_softc *sc)
+{
+	DBG("ath5k: rfkill enable (gpio:%d polarity:%d)\n",
+	    sc->rf_kill.gpio, sc->rf_kill.polarity);
+	ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio);
+	ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, sc->rf_kill.polarity);
+}
+
+static inline void ath5k_rfkill_set_intr(struct ath5k_softc *sc, int enable)
+{
+	struct ath5k_hw *ah = sc->ah;
+	u32 curval;
+
+	ath5k_hw_set_gpio_input(ah, sc->rf_kill.gpio);
+	curval = ath5k_hw_get_gpio(ah, sc->rf_kill.gpio);
+	ath5k_hw_set_gpio_intr(ah, sc->rf_kill.gpio, enable ?
+			       !!curval : !curval);
+}
+
+static int __unused
+ath5k_is_rfkill_set(struct ath5k_softc *sc)
+{
+	/* configuring GPIO for input for some reason disables rfkill */
+	/*ath5k_hw_set_gpio_input(sc->ah, sc->rf_kill.gpio);*/
+	return (ath5k_hw_get_gpio(sc->ah, sc->rf_kill.gpio) ==
+		sc->rf_kill.polarity);
+}
+
+void
+ath5k_rfkill_hw_start(struct ath5k_hw *ah)
+{
+	struct ath5k_softc *sc = ah->ah_sc;
+
+	/* read rfkill GPIO configuration from EEPROM header */
+	sc->rf_kill.gpio = ah->ah_capabilities.cap_eeprom.ee_rfkill_pin;
+	sc->rf_kill.polarity = ah->ah_capabilities.cap_eeprom.ee_rfkill_pol;
+
+	ath5k_rfkill_disable(sc);
+
+	/* enable interrupt for rfkill switch */
+	if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header))
+		ath5k_rfkill_set_intr(sc, 1);
+}
+
+
+void
+ath5k_rfkill_hw_stop(struct ath5k_hw *ah)
+{
+	struct ath5k_softc *sc = ah->ah_sc;
+
+	/* disable interrupt for rfkill switch */
+	if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header))
+		ath5k_rfkill_set_intr(sc, 0);
+
+	/* enable RFKILL when stopping HW so Wifi LED is turned off */
+	ath5k_rfkill_enable(sc);
+}
diff --git a/gpxe/src/drivers/net/ath5k/base.h b/gpxe/src/drivers/net/ath5k/base.h
new file mode 100644
index 0000000..870b0ed
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/base.h
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Modified for gPXE, July 2009, by Joshua Oreman <oremanj at rwcr.net>
+ * Original from Linux kernel 2.6.30.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+/*
+ * Defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_ATHVAR_H
+#define _DEV_ATH_ATHVAR_H
+
+FILE_LICENCE ( BSD3 );
+
+#include "ath5k.h"
+#include <gpxe/iobuf.h>
+
+#define	ATH_RXBUF	16		/* number of RX buffers */
+#define	ATH_TXBUF	16		/* number of TX buffers */
+
+struct ath5k_buf {
+	struct list_head	list;
+	unsigned int		flags;	/* rx descriptor flags */
+	struct ath5k_desc	*desc;	/* virtual addr of desc */
+	u32			daddr;	/* physical addr of desc */
+	struct io_buffer	*iob;	/* I/O buffer for buf */
+	u32			iobaddr;/* physical addr of iob data */
+};
+
+/*
+ * Data transmit queue state.  One of these exists for each
+ * hardware transmit queue.  Packets sent to us from above
+ * are assigned to queues based on their priority.  Not all
+ * devices support a complete set of hardware transmit queues.
+ * For those devices the array sc_ac2q will map multiple
+ * priorities to fewer hardware queues (typically all to one
+ * hardware queue).
+ */
+struct ath5k_txq {
+	unsigned int		qnum;	/* hardware q number */
+	u32			*link;	/* link ptr in last TX desc */
+	struct list_head	q;	/* transmit queue */
+	int			setup;
+};
+
+#if CHAN_DEBUG
+#define ATH_CHAN_MAX	(26+26+26+200+200)
+#else
+#define ATH_CHAN_MAX	(14+14+14+252+20)
+#endif
+
+/* Software Carrier, keeps track of the driver state
+ * associated with an instance of a device */
+struct ath5k_softc {
+	struct pci_device	*pdev;		/* for dma mapping */
+	void			*iobase;	/* address of the device */
+	struct net80211_device	*dev;		/* IEEE 802.11 common */
+	struct ath5k_hw		*ah;		/* Atheros HW */
+	struct net80211_hw_info	*hwinfo;
+	int			curband;
+	int			irq_ena; 	/* interrupts enabled */
+
+	struct ath5k_buf	*bufptr;	/* allocated buffer ptr */
+	struct ath5k_desc	*desc;		/* TX/RX descriptors */
+	u32			desc_daddr;	/* DMA (physical) address */
+	size_t			desc_len;	/* size of TX/RX descriptors */
+	u16			cachelsz;	/* cache line size */
+
+	int			status;
+#define ATH_STAT_INVALID	0x01		/* disable hardware accesses */
+#define ATH_STAT_MRRETRY	0x02		/* multi-rate retry support */
+#define ATH_STAT_PROMISC	0x04
+#define ATH_STAT_LEDSOFT	0x08		/* enable LED gpio status */
+#define ATH_STAT_STARTED	0x10		/* opened & irqs enabled */
+
+	unsigned int		filter_flags;	/* HW flags, AR5K_RX_FILTER_* */
+	unsigned int		curmode;	/* current phy mode */
+	struct net80211_channel	*curchan;	/* current h/w channel */
+
+	enum ath5k_int		imask;		/* interrupt mask copy */
+
+	u8			bssidmask[ETH_ALEN];
+
+	unsigned int		rxbufsize;	/* rx size based on mtu */
+	struct list_head	rxbuf;		/* receive buffer */
+	u32			*rxlink;	/* link ptr in last RX desc */
+
+	struct list_head	txbuf;		/* transmit buffer */
+	unsigned int		txbuf_len;	/* buf count in txbuf list */
+	struct ath5k_txq	txq;		/* tx queue */
+
+	struct {
+		u16 gpio;
+		unsigned polarity;
+	} rf_kill;
+
+	int			last_calib_ticks;
+
+	int 			power_level;	/* Requested tx power in dbm */
+	int			assoc;		/* assocate state */
+
+	int			hw_rate;	/* Hardware tx rate code */
+	int			hw_rtscts_rate;	/* Hardware rts/cts rate code */
+};
+
+#define ath5k_hw_hasbssidmask(_ah) \
+	(ath5k_hw_get_capability(_ah, AR5K_CAP_BSSIDMASK, 0, NULL) == 0)
+#define ath5k_hw_hasveol(_ah) \
+	(ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0)
+
+#endif
diff --git a/gpxe/src/drivers/net/ath5k/desc.h b/gpxe/src/drivers/net/ath5k/desc.h
new file mode 100644
index 0000000..6e11b0d
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/desc.h
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * Internal RX/TX descriptor structures
+ * (rX: reserved fields possibily used by future versions of the ar5k chipset)
+ */
+
+/*
+ * common hardware RX control descriptor
+ */
+struct ath5k_hw_rx_ctl {
+	u32	rx_control_0; /* RX control word 0 */
+	u32	rx_control_1; /* RX control word 1 */
+} __attribute__ ((packed));
+
+/* RX control word 0 field/sflags */
+#define AR5K_DESC_RX_CTL0			0x00000000
+
+/* RX control word 1 fields/flags */
+#define AR5K_DESC_RX_CTL1_BUF_LEN		0x00000fff
+#define AR5K_DESC_RX_CTL1_INTREQ		0x00002000
+
+/*
+ * common hardware RX status descriptor
+ * 5210/11 and 5212 differ only in the flags defined below
+ */
+struct ath5k_hw_rx_status {
+	u32	rx_status_0; /* RX status word 0 */
+	u32	rx_status_1; /* RX status word 1 */
+} __attribute__ ((packed));
+
+/* 5210/5211 */
+/* RX status word 0 fields/flags */
+#define AR5K_5210_RX_DESC_STATUS0_DATA_LEN		0x00000fff
+#define AR5K_5210_RX_DESC_STATUS0_MORE			0x00001000
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE		0x00078000
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S	15
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL	0x07f80000
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	19
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA	0x38000000
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	27
+
+/* RX status word 1 fields/flags */
+#define AR5K_5210_RX_DESC_STATUS1_DONE			0x00000001
+#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002
+#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR		0x00000004
+#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN		0x00000008
+#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000010
+#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR		0x000000e0
+#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S		5
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX		0x00007e00
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S		9
+#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x0fff8000
+#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	15
+#define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS	0x10000000
+
+/* 5212 */
+/* RX status word 0 fields/flags */
+#define AR5K_5212_RX_DESC_STATUS0_DATA_LEN		0x00000fff
+#define AR5K_5212_RX_DESC_STATUS0_MORE			0x00001000
+#define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR	0x00002000
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE		0x000f8000
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S	15
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL	0x0ff00000
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	20
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA	0xf0000000
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	28
+
+/* RX status word 1 fields/flags */
+#define AR5K_5212_RX_DESC_STATUS1_DONE			0x00000001
+#define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002
+#define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR		0x00000004
+#define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000008
+#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR		0x00000010
+#define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR		0x00000020
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX		0x0000fe00
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S		9
+#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x7fff0000
+#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	16
+#define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS	0x80000000
+
+/*
+ * common hardware RX error descriptor
+ */
+struct ath5k_hw_rx_error {
+	u32	rx_error_0; /* RX status word 0 */
+	u32	rx_error_1; /* RX status word 1 */
+} __attribute__ ((packed));
+
+/* RX error word 0 fields/flags */
+#define AR5K_RX_DESC_ERROR0			0x00000000
+
+/* RX error word 1 fields/flags */
+#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE	0x0000ff00
+#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE_S	8
+
+/* PHY Error codes */
+#define AR5K_DESC_RX_PHY_ERROR_NONE		0x00
+#define AR5K_DESC_RX_PHY_ERROR_TIMING		0x20
+#define AR5K_DESC_RX_PHY_ERROR_PARITY		0x40
+#define AR5K_DESC_RX_PHY_ERROR_RATE		0x60
+#define AR5K_DESC_RX_PHY_ERROR_LENGTH		0x80
+#define AR5K_DESC_RX_PHY_ERROR_64QAM		0xa0
+#define AR5K_DESC_RX_PHY_ERROR_SERVICE		0xc0
+#define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR	0xe0
+
+/*
+ * 5210/5211 hardware 2-word TX control descriptor
+ */
+struct ath5k_hw_2w_tx_ctl {
+	u32	tx_control_0; /* TX control word 0 */
+	u32	tx_control_1; /* TX control word 1 */
+} __attribute__ ((packed));
+
+/* TX control word 0 fields/flags */
+#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN		0x00000fff
+#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN		0x0003f000 /*[5210 ?]*/
+#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_S	12
+#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE		0x003c0000
+#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S	18
+#define AR5K_2W_TX_DESC_CTL0_RTSENA		0x00400000
+#define AR5K_2W_TX_DESC_CTL0_CLRDMASK		0x01000000
+#define AR5K_2W_TX_DESC_CTL0_LONG_PACKET	0x00800000 /*[5210]*/
+#define AR5K_2W_TX_DESC_CTL0_VEOL		0x00800000 /*[5211]*/
+#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE		0x1c000000 /*[5210]*/
+#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_S	26
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210	0x02000000
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211	0x1e000000
+
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT			\
+		(ah->ah_version == AR5K_AR5210 ?		\
+		AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 :	\
+		AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211)
+
+#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_S	25
+#define AR5K_2W_TX_DESC_CTL0_INTREQ		0x20000000
+#define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID	0x40000000
+
+/* TX control word 1 fields/flags */
+#define AR5K_2W_TX_DESC_CTL1_BUF_LEN		0x00000fff
+#define AR5K_2W_TX_DESC_CTL1_MORE		0x00001000
+#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210	0x0007e000
+#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211	0x000fe000
+
+#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX				\
+			(ah->ah_version == AR5K_AR5210 ?		\
+			AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 :	\
+			AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211)
+
+#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S	13
+#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE		0x00700000 /*[5211]*/
+#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_S	20
+#define AR5K_2W_TX_DESC_CTL1_NOACK		0x00800000 /*[5211]*/
+#define AR5K_2W_TX_DESC_CTL1_RTS_DURATION	0xfff80000 /*[5210 ?]*/
+
+/* Frame types */
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL   0x00
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM     0x04
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL   0x08
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 0x0c
+#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS     0x10
+
+/*
+ * 5212 hardware 4-word TX control descriptor
+ */
+struct ath5k_hw_4w_tx_ctl {
+	u32	tx_control_0; /* TX control word 0 */
+
+#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN		0x00000fff
+#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER		0x003f0000
+#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S	16
+#define AR5K_4W_TX_DESC_CTL0_RTSENA		0x00400000
+#define AR5K_4W_TX_DESC_CTL0_VEOL		0x00800000
+#define AR5K_4W_TX_DESC_CTL0_CLRDMASK		0x01000000
+#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT	0x1e000000
+#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S	25
+#define AR5K_4W_TX_DESC_CTL0_INTREQ		0x20000000
+#define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID	0x40000000
+#define AR5K_4W_TX_DESC_CTL0_CTSENA		0x80000000
+
+	u32	tx_control_1; /* TX control word 1 */
+
+#define AR5K_4W_TX_DESC_CTL1_BUF_LEN		0x00000fff
+#define AR5K_4W_TX_DESC_CTL1_MORE		0x00001000
+#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX	0x000fe000
+#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S	13
+#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE		0x00f00000
+#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S	20
+#define AR5K_4W_TX_DESC_CTL1_NOACK		0x01000000
+#define AR5K_4W_TX_DESC_CTL1_COMP_PROC		0x06000000
+#define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S	25
+#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN	0x18000000
+#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S	27
+#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN	0x60000000
+#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S	29
+
+	u32	tx_control_2; /* TX control word 2 */
+
+#define AR5K_4W_TX_DESC_CTL2_RTS_DURATION		0x00007fff
+#define AR5K_4W_TX_DESC_CTL2_DURATION_UPDATE_ENABLE	0x00008000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0		0x000f0000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S		16
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1		0x00f00000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S		20
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2		0x0f000000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S		24
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3		0xf0000000
+#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S		28
+
+	u32	tx_control_3; /* TX control word 3 */
+
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0		0x0000001f
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1		0x000003e0
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S	5
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2		0x00007c00
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S	10
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3		0x000f8000
+#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S	15
+#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE	0x01f00000
+#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S	20
+} __attribute__ ((packed));
+
+/*
+ * Common TX status descriptor
+ */
+struct ath5k_hw_tx_status {
+	u32	tx_status_0; /* TX status word 0 */
+	u32	tx_status_1; /* TX status word 1 */
+} __attribute__ ((packed));
+
+/* TX status word 0 fields/flags */
+#define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK	0x00000001
+#define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES	0x00000002
+#define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN	0x00000004
+#define AR5K_DESC_TX_STATUS0_FILTERED		0x00000008
+/*???
+#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT	0x000000f0
+#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT_S	4
+*/
+#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT	0x000000f0
+#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S	4
+/*???
+#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT	0x00000f00
+#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT_S	8
+*/
+#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT	0x00000f00
+#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S	8
+#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT	0x0000f000
+#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT_S	12
+#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP	0xffff0000
+#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S	16
+
+/* TX status word 1 fields/flags */
+#define AR5K_DESC_TX_STATUS1_DONE		0x00000001
+#define AR5K_DESC_TX_STATUS1_SEQ_NUM		0x00001ffe
+#define AR5K_DESC_TX_STATUS1_SEQ_NUM_S		1
+#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH	0x001fe000
+#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S	13
+#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX	0x00600000
+#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX_S	21
+#define AR5K_DESC_TX_STATUS1_COMP_SUCCESS	0x00800000
+#define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA	0x01000000
+
+/*
+ * 5210/5211 hardware TX descriptor
+ */
+struct ath5k_hw_5210_tx_desc {
+	struct ath5k_hw_2w_tx_ctl	tx_ctl;
+	struct ath5k_hw_tx_status	tx_stat;
+} __attribute__ ((packed));
+
+/*
+ * 5212 hardware TX descriptor
+ */
+struct ath5k_hw_5212_tx_desc {
+	struct ath5k_hw_4w_tx_ctl	tx_ctl;
+	struct ath5k_hw_tx_status	tx_stat;
+} __attribute__ ((packed));
+
+/*
+ * common hardware RX descriptor
+ */
+struct ath5k_hw_all_rx_desc {
+	struct ath5k_hw_rx_ctl			rx_ctl;
+	union {
+		struct ath5k_hw_rx_status	rx_stat;
+		struct ath5k_hw_rx_error	rx_err;
+	} u;
+} __attribute__ ((packed));
+
+/*
+ * Atheros hardware descriptor
+ * This is read and written to by the hardware
+ */
+struct ath5k_desc {
+	u32	ds_link;	/* physical address of the next descriptor */
+	u32	ds_data;	/* physical address of data buffer (skb) */
+
+	union {
+		struct ath5k_hw_5210_tx_desc	ds_tx5210;
+		struct ath5k_hw_5212_tx_desc	ds_tx5212;
+		struct ath5k_hw_all_rx_desc	ds_rx;
+	} ud;
+} __attribute__ ((packed));
+
+#define AR5K_RXDESC_INTREQ	0x0020
+
+#define AR5K_TXDESC_CLRDMASK	0x0001
+#define AR5K_TXDESC_NOACK	0x0002	/*[5211+]*/
+#define AR5K_TXDESC_RTSENA	0x0004
+#define AR5K_TXDESC_CTSENA	0x0008
+#define AR5K_TXDESC_INTREQ	0x0010
+#define AR5K_TXDESC_VEOL	0x0020	/*[5211+]*/
+
diff --git a/gpxe/src/drivers/net/ath5k/eeprom.h b/gpxe/src/drivers/net/ath5k/eeprom.h
new file mode 100644
index 0000000..da45433
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/eeprom.h
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE)
+ */
+#define AR5K_EEPROM_MAGIC		0x003d	/* EEPROM Magic number */
+#define AR5K_EEPROM_MAGIC_VALUE		0x5aa5	/* Default - found on EEPROM */
+#define AR5K_EEPROM_MAGIC_5212		0x0000145c /* 5212 */
+#define AR5K_EEPROM_MAGIC_5211		0x0000145b /* 5211 */
+#define AR5K_EEPROM_MAGIC_5210		0x0000145a /* 5210 */
+
+#define	AR5K_EEPROM_IS_HB63		0x000b	/* Talon detect */
+
+#define AR5K_EEPROM_RFKILL		0x0f
+#define AR5K_EEPROM_RFKILL_GPIO_SEL	0x0000001c
+#define AR5K_EEPROM_RFKILL_GPIO_SEL_S	2
+#define AR5K_EEPROM_RFKILL_POLARITY	0x00000002
+#define AR5K_EEPROM_RFKILL_POLARITY_S	1
+
+#define AR5K_EEPROM_REG_DOMAIN		0x00bf	/* EEPROM regdom */
+#define AR5K_EEPROM_CHECKSUM		0x00c0	/* EEPROM checksum */
+#define AR5K_EEPROM_INFO_BASE		0x00c0	/* EEPROM header */
+#define AR5K_EEPROM_INFO_MAX		(0x400 - AR5K_EEPROM_INFO_BASE)
+#define AR5K_EEPROM_INFO_CKSUM		0xffff
+#define AR5K_EEPROM_INFO(_n)		(AR5K_EEPROM_INFO_BASE + (_n))
+
+#define AR5K_EEPROM_VERSION		AR5K_EEPROM_INFO(1)	/* EEPROM Version */
+#define AR5K_EEPROM_VERSION_3_0		0x3000	/* No idea what's going on before this version */
+#define AR5K_EEPROM_VERSION_3_1		0x3001	/* ob/db values for 2Ghz (ar5211_rfregs) */
+#define AR5K_EEPROM_VERSION_3_2		0x3002	/* different frequency representation (eeprom_bin2freq) */
+#define AR5K_EEPROM_VERSION_3_3		0x3003	/* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */
+#define AR5K_EEPROM_VERSION_3_4		0x3004	/* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */
+#define AR5K_EEPROM_VERSION_4_0		0x4000	/* has ee_misc, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_1		0x4001	/* has ee_margin_tx_rx (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_2		0x4002	/* has ee_cck_ofdm_gain_delta (eeprom_init) */
+#define AR5K_EEPROM_VERSION_4_3		0x4003	/* power calibration changes */
+#define AR5K_EEPROM_VERSION_4_4		0x4004
+#define AR5K_EEPROM_VERSION_4_5		0x4005
+#define AR5K_EEPROM_VERSION_4_6		0x4006	/* has ee_scaled_cck_delta */
+#define AR5K_EEPROM_VERSION_4_7		0x3007	/* 4007 ? */
+#define AR5K_EEPROM_VERSION_4_9		0x4009	/* EAR futureproofing */
+#define AR5K_EEPROM_VERSION_5_0		0x5000	/* Has 2413 PDADC calibration etc */
+#define AR5K_EEPROM_VERSION_5_1		0x5001	/* Has capability values */
+#define AR5K_EEPROM_VERSION_5_3		0x5003	/* Has spur mitigation tables */
+
+#define AR5K_EEPROM_MODE_11A		0
+#define AR5K_EEPROM_MODE_11B		1
+#define AR5K_EEPROM_MODE_11G		2
+
+#define AR5K_EEPROM_HDR			AR5K_EEPROM_INFO(2)	/* Header that contains the device caps */
+#define AR5K_EEPROM_HDR_11A(_v)		(((_v) >> AR5K_EEPROM_MODE_11A) & 0x1)
+#define AR5K_EEPROM_HDR_11B(_v)		(((_v) >> AR5K_EEPROM_MODE_11B) & 0x1)
+#define AR5K_EEPROM_HDR_11G(_v)		(((_v) >> AR5K_EEPROM_MODE_11G) & 0x1)
+#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v)	(((_v) >> 3) & 0x1)	/* Disable turbo for 2Ghz (?) */
+#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v)	(((_v) >> 4) & 0x7f)	/* Max turbo power for a/XR mode (eeprom_init) */
+#define AR5K_EEPROM_HDR_DEVICE(_v)	(((_v) >> 11) & 0x7)
+#define AR5K_EEPROM_HDR_RFKILL(_v)	(((_v) >> 14) & 0x1)	/* Device has RFKill support */
+#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v)	(((_v) >> 15) & 0x1)	/* Disable turbo for 5Ghz */
+
+#define AR5K_EEPROM_RFKILL_GPIO_SEL	0x0000001c
+#define AR5K_EEPROM_RFKILL_GPIO_SEL_S	2
+#define AR5K_EEPROM_RFKILL_POLARITY	0x00000002
+#define AR5K_EEPROM_RFKILL_POLARITY_S	1
+
+/* Newer EEPROMs are using a different offset */
+#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \
+	(((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0)
+
+#define AR5K_EEPROM_ANT_GAIN(_v)	AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3)
+#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v)	((s8)(((_v) >> 8) & 0xff))
+#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v)	((s8)((_v) & 0xff))
+
+/* Misc values available since EEPROM 4.0 */
+#define AR5K_EEPROM_MISC0		AR5K_EEPROM_INFO(4)
+#define AR5K_EEPROM_EARSTART(_v)	((_v) & 0xfff)
+#define AR5K_EEPROM_HDR_XR2_DIS(_v)	(((_v) >> 12) & 0x1)
+#define AR5K_EEPROM_HDR_XR5_DIS(_v)	(((_v) >> 13) & 0x1)
+#define AR5K_EEPROM_EEMAP(_v)		(((_v) >> 14) & 0x3)
+
+#define AR5K_EEPROM_MISC1			AR5K_EEPROM_INFO(5)
+#define AR5K_EEPROM_TARGET_PWRSTART(_v)		((_v) & 0xfff)
+#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v)		(((_v) >> 14) & 0x1)
+#define AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(_v)	(((_v) >> 15) & 0x1)
+
+#define AR5K_EEPROM_MISC2			AR5K_EEPROM_INFO(6)
+#define AR5K_EEPROM_EEP_FILE_VERSION(_v)	(((_v) >> 8) & 0xff)
+#define AR5K_EEPROM_EAR_FILE_VERSION(_v)	((_v) & 0xff)
+
+#define AR5K_EEPROM_MISC3		AR5K_EEPROM_INFO(7)
+#define AR5K_EEPROM_ART_BUILD_NUM(_v)	(((_v) >> 10) & 0x3f)
+#define AR5K_EEPROM_EAR_FILE_ID(_v)	((_v) & 0xff)
+
+#define AR5K_EEPROM_MISC4		AR5K_EEPROM_INFO(8)
+#define AR5K_EEPROM_CAL_DATA_START(_v)	(((_v) >> 4) & 0xfff)
+#define AR5K_EEPROM_MASK_R0(_v)		(((_v) >> 2) & 0x3)
+#define AR5K_EEPROM_MASK_R1(_v)		((_v) & 0x3)
+
+#define AR5K_EEPROM_MISC5		AR5K_EEPROM_INFO(9)
+#define AR5K_EEPROM_COMP_DIS(_v)	((_v) & 0x1)
+#define AR5K_EEPROM_AES_DIS(_v)		(((_v) >> 1) & 0x1)
+#define AR5K_EEPROM_FF_DIS(_v)		(((_v) >> 2) & 0x1)
+#define AR5K_EEPROM_BURST_DIS(_v)	(((_v) >> 3) & 0x1)
+#define AR5K_EEPROM_MAX_QCU(_v)		(((_v) >> 4) & 0xf)
+#define AR5K_EEPROM_HEAVY_CLIP_EN(_v)	(((_v) >> 8) & 0x1)
+#define AR5K_EEPROM_KEY_CACHE_SIZE(_v)	(((_v) >> 12) & 0xf)
+
+#define AR5K_EEPROM_MISC6		AR5K_EEPROM_INFO(10)
+#define AR5K_EEPROM_TX_CHAIN_DIS	((_v) & 0x8)
+#define AR5K_EEPROM_RX_CHAIN_DIS	(((_v) >> 3) & 0x8)
+#define AR5K_EEPROM_FCC_MID_EN		(((_v) >> 6) & 0x1)
+#define AR5K_EEPROM_JAP_U1EVEN_EN	(((_v) >> 7) & 0x1)
+#define AR5K_EEPROM_JAP_U2_EN		(((_v) >> 8) & 0x1)
+#define AR5K_EEPROM_JAP_U1ODD_EN	(((_v) >> 9) & 0x1)
+#define AR5K_EEPROM_JAP_11A_NEW_EN	(((_v) >> 10) & 0x1)
+
+/* calibration settings */
+#define AR5K_EEPROM_MODES_11A(_v)	AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4)
+#define AR5K_EEPROM_MODES_11B(_v)	AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2)
+#define AR5K_EEPROM_MODES_11G(_v)	AR5K_EEPROM_OFF(_v, 0x00da, 0x010d)
+#define AR5K_EEPROM_CTL(_v)		AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128)	/* Conformance test limits */
+#define AR5K_EEPROM_GROUPS_START(_v)	AR5K_EEPROM_OFF(_v, 0x0100, 0x0150)	/* Start of Groups */
+#define AR5K_EEPROM_GROUP1_OFFSET	0x0
+#define AR5K_EEPROM_GROUP2_OFFSET	0x5
+#define AR5K_EEPROM_GROUP3_OFFSET	0x37
+#define AR5K_EEPROM_GROUP4_OFFSET	0x46
+#define AR5K_EEPROM_GROUP5_OFFSET	0x55
+#define AR5K_EEPROM_GROUP6_OFFSET	0x65
+#define AR5K_EEPROM_GROUP7_OFFSET	0x69
+#define AR5K_EEPROM_GROUP8_OFFSET	0x6f
+
+#define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v)	AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+								AR5K_EEPROM_GROUP5_OFFSET, 0x0000)
+#define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v)	AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+								AR5K_EEPROM_GROUP6_OFFSET, 0x0010)
+#define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v)	AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+								AR5K_EEPROM_GROUP7_OFFSET, 0x0014)
+
+/* [3.1 - 3.3] */
+#define AR5K_EEPROM_OBDB0_2GHZ		0x00ec
+#define AR5K_EEPROM_OBDB1_2GHZ		0x00ed
+
+#define AR5K_EEPROM_PROTECT		0x003f	/* EEPROM protect status */
+#define AR5K_EEPROM_PROTECT_RD_0_31	0x0001	/* Read protection bit for offsets 0x0 - 0x1f */
+#define AR5K_EEPROM_PROTECT_WR_0_31	0x0002	/* Write protection bit for offsets 0x0 - 0x1f */
+#define AR5K_EEPROM_PROTECT_RD_32_63	0x0004	/* 0x20 - 0x3f */
+#define AR5K_EEPROM_PROTECT_WR_32_63	0x0008
+#define AR5K_EEPROM_PROTECT_RD_64_127	0x0010	/* 0x40 - 0x7f */
+#define AR5K_EEPROM_PROTECT_WR_64_127	0x0020
+#define AR5K_EEPROM_PROTECT_RD_128_191	0x0040	/* 0x80 - 0xbf (regdom) */
+#define AR5K_EEPROM_PROTECT_WR_128_191	0x0080
+#define AR5K_EEPROM_PROTECT_RD_192_207	0x0100	/* 0xc0 - 0xcf */
+#define AR5K_EEPROM_PROTECT_WR_192_207	0x0200
+#define AR5K_EEPROM_PROTECT_RD_208_223	0x0400	/* 0xd0 - 0xdf */
+#define AR5K_EEPROM_PROTECT_WR_208_223	0x0800
+#define AR5K_EEPROM_PROTECT_RD_224_239	0x1000	/* 0xe0 - 0xef */
+#define AR5K_EEPROM_PROTECT_WR_224_239	0x2000
+#define AR5K_EEPROM_PROTECT_RD_240_255	0x4000	/* 0xf0 - 0xff */
+#define AR5K_EEPROM_PROTECT_WR_240_255	0x8000
+
+/* Some EEPROM defines */
+#define AR5K_EEPROM_EEP_SCALE		100
+#define AR5K_EEPROM_EEP_DELTA		10
+#define AR5K_EEPROM_N_MODES		3
+#define AR5K_EEPROM_N_5GHZ_CHAN		10
+#define AR5K_EEPROM_N_2GHZ_CHAN		3
+#define AR5K_EEPROM_N_2GHZ_CHAN_2413	4
+#define	AR5K_EEPROM_N_2GHZ_CHAN_MAX	4
+#define AR5K_EEPROM_MAX_CHAN		10
+#define AR5K_EEPROM_N_PWR_POINTS_5111	11
+#define AR5K_EEPROM_N_PCDAC		11
+#define AR5K_EEPROM_N_PHASE_CAL		5
+#define AR5K_EEPROM_N_TEST_FREQ		8
+#define AR5K_EEPROM_N_EDGES		8
+#define AR5K_EEPROM_N_INTERCEPTS	11
+#define AR5K_EEPROM_FREQ_M(_v)		AR5K_EEPROM_OFF(_v, 0x7f, 0xff)
+#define AR5K_EEPROM_PCDAC_M		0x3f
+#define AR5K_EEPROM_PCDAC_START		1
+#define AR5K_EEPROM_PCDAC_STOP		63
+#define AR5K_EEPROM_PCDAC_STEP		1
+#define AR5K_EEPROM_NON_EDGE_M		0x40
+#define AR5K_EEPROM_CHANNEL_POWER	8
+#define AR5K_EEPROM_N_OBDB		4
+#define AR5K_EEPROM_OBDB_DIS		0xffff
+#define AR5K_EEPROM_CHANNEL_DIS		0xff
+#define AR5K_EEPROM_SCALE_OC_DELTA(_x)	(((_x) * 2) / 10)
+#define AR5K_EEPROM_N_CTLS(_v)		AR5K_EEPROM_OFF(_v, 16, 32)
+#define AR5K_EEPROM_MAX_CTLS		32
+#define AR5K_EEPROM_N_PD_CURVES		4
+#define AR5K_EEPROM_N_XPD0_POINTS	4
+#define AR5K_EEPROM_N_XPD3_POINTS	3
+#define AR5K_EEPROM_N_PD_GAINS		4
+#define AR5K_EEPROM_N_PD_POINTS		5
+#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ	35
+#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ	55
+#define AR5K_EEPROM_POWER_M		0x3f
+#define AR5K_EEPROM_POWER_MIN		0
+#define AR5K_EEPROM_POWER_MAX		3150
+#define AR5K_EEPROM_POWER_STEP		50
+#define AR5K_EEPROM_POWER_TABLE_SIZE	64
+#define AR5K_EEPROM_N_POWER_LOC_11B	4
+#define AR5K_EEPROM_N_POWER_LOC_11G	6
+#define AR5K_EEPROM_I_GAIN		10
+#define AR5K_EEPROM_CCK_OFDM_DELTA	15
+#define AR5K_EEPROM_N_IQ_CAL		2
+
+#define AR5K_EEPROM_READ(_o, _v) do {			\
+	ret = ath5k_hw_eeprom_read(ah, (_o), &(_v));	\
+	if (ret)					\
+		return ret;				\
+} while (0)
+
+#define AR5K_EEPROM_READ_HDR(_o, _v)					\
+	AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v);	\
+
+enum ath5k_ant_setting {
+	AR5K_ANT_VARIABLE	= 0,	/* variable by programming */
+	AR5K_ANT_FIXED_A	= 1,	/* fixed to 11a frequencies */
+	AR5K_ANT_FIXED_B	= 2,	/* fixed to 11b frequencies */
+	AR5K_ANT_MAX		= 3,
+};
+
+enum ath5k_ctl_mode {
+	AR5K_CTL_11A = 0,
+	AR5K_CTL_11B = 1,
+	AR5K_CTL_11G = 2,
+	AR5K_CTL_TURBO = 3,
+	AR5K_CTL_TURBOG = 4,
+	AR5K_CTL_2GHT20 = 5,
+	AR5K_CTL_5GHT20 = 6,
+	AR5K_CTL_2GHT40 = 7,
+	AR5K_CTL_5GHT40 = 8,
+	AR5K_CTL_MODE_M = 15,
+};
+
+/* Default CTL ids for the 3 main reg domains.
+ * Atheros only uses these by default but vendors
+ * can have up to 32 different CTLs for different
+ * scenarios. Note that theese values are ORed with
+ * the mode id (above) so we can have up to 24 CTL
+ * datasets out of these 3 main regdomains. That leaves
+ * 8 ids that can be used by vendors and since 0x20 is
+ * missing from HAL sources i guess this is the set of
+ * custom CTLs vendors can use. */
+#define	AR5K_CTL_FCC	0x10
+#define	AR5K_CTL_CUSTOM	0x20
+#define	AR5K_CTL_ETSI	0x30
+#define	AR5K_CTL_MKK	0x40
+
+/* Indicates a CTL with only mode set and
+ * no reg domain mapping, such CTLs are used
+ * for world roaming domains or simply when
+ * a reg domain is not set */
+#define	AR5K_CTL_NO_REGDOMAIN	0xf0
+
+/* Indicates an empty (invalid) CTL */
+#define AR5K_CTL_NO_CTL		0xff
+
+/* Per channel calibration data, used for power table setup */
+struct ath5k_chan_pcal_info_rf5111 {
+	/* Power levels in half dbm units
+	 * for one power curve. */
+	u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
+	/* PCDAC table steps
+	 * for the above values */
+	u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
+	/* Starting PCDAC step */
+	u8 pcdac_min;
+	/* Final PCDAC step */
+	u8 pcdac_max;
+};
+
+struct ath5k_chan_pcal_info_rf5112 {
+	/* Power levels in quarter dBm units
+	 * for lower (0) and higher (3)
+	 * level curves in 0.25dB units */
+	s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
+	s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
+	/* PCDAC table steps
+	 * for the above values */
+	u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
+	u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
+};
+
+struct ath5k_chan_pcal_info_rf2413 {
+	/* Starting pwr/pddac values */
+	s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
+	u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
+	/* (pwr,pddac) points
+	 * power levels in 0.5dB units */
+	s8 pwr[AR5K_EEPROM_N_PD_GAINS]
+		[AR5K_EEPROM_N_PD_POINTS];
+	u8 pddac[AR5K_EEPROM_N_PD_GAINS]
+		[AR5K_EEPROM_N_PD_POINTS];
+};
+
+enum ath5k_powertable_type {
+	AR5K_PWRTABLE_PWR_TO_PCDAC = 0,
+	AR5K_PWRTABLE_LINEAR_PCDAC = 1,
+	AR5K_PWRTABLE_PWR_TO_PDADC = 2,
+};
+
+struct ath5k_pdgain_info {
+	u8 pd_points;
+	u8 *pd_step;
+	/* Power values are in
+	 * 0.25dB units */
+	s16 *pd_pwr;
+};
+
+struct ath5k_chan_pcal_info {
+	/* Frequency */
+	u16	freq;
+	/* Tx power boundaries */
+	s16	max_pwr;
+	s16	min_pwr;
+	union {
+		struct ath5k_chan_pcal_info_rf5111 rf5111_info;
+		struct ath5k_chan_pcal_info_rf5112 rf5112_info;
+		struct ath5k_chan_pcal_info_rf2413 rf2413_info;
+	};
+	/* Raw values used by phy code
+	 * Curves are stored in order from lower
+	 * gain to higher gain (max txpower -> min txpower) */
+	struct ath5k_pdgain_info *pd_curves;
+};
+
+/* Per rate calibration data for each mode,
+ * used for rate power table setup.
+ * Note: Values in 0.5dB units */
+struct ath5k_rate_pcal_info {
+	u16	freq; /* Frequency */
+	/* Power level for 6-24Mbit/s rates or
+	 * 1Mb rate */
+	u16	target_power_6to24;
+	/* Power level for 36Mbit rate or
+	 * 2Mb rate */
+	u16	target_power_36;
+	/* Power level for 48Mbit rate or
+	 * 5.5Mbit rate */
+	u16	target_power_48;
+	/* Power level for 54Mbit rate or
+	 * 11Mbit rate */
+	u16	target_power_54;
+};
+
+/* Power edges for conformance test limits */
+struct ath5k_edge_power {
+	u16 freq;
+	u16 edge; /* in half dBm */
+	int flag;
+};
+
+/* EEPROM calibration data */
+struct ath5k_eeprom_info {
+
+	/* Header information */
+	u16	ee_magic;
+	u16	ee_protect;
+	u16	ee_regdomain;
+	u16	ee_version;
+	u16	ee_header;
+	u16	ee_ant_gain;
+	u8	ee_rfkill_pin;
+	int	ee_rfkill_pol;
+	int	ee_is_hb63;
+	u16	ee_misc0;
+	u16	ee_misc1;
+	u16	ee_misc2;
+	u16	ee_misc3;
+	u16	ee_misc4;
+	u16	ee_misc5;
+	u16	ee_misc6;
+	u16	ee_cck_ofdm_gain_delta;
+	u16	ee_cck_ofdm_power_delta;
+	u16	ee_scaled_cck_delta;
+
+	/* RF Calibration settings (reset, rfregs) */
+	u16	ee_i_cal[AR5K_EEPROM_N_MODES];
+	u16	ee_q_cal[AR5K_EEPROM_N_MODES];
+	u16	ee_fixed_bias[AR5K_EEPROM_N_MODES];
+	u16	ee_turbo_max_power[AR5K_EEPROM_N_MODES];
+	u16	ee_xr_power[AR5K_EEPROM_N_MODES];
+	u16	ee_switch_settling[AR5K_EEPROM_N_MODES];
+	u16	ee_atn_tx_rx[AR5K_EEPROM_N_MODES];
+	u16	ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC];
+	u16	ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
+	u16	ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
+	u16	ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES];
+	u16	ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES];
+	u16	ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES];
+	u16	ee_thr_62[AR5K_EEPROM_N_MODES];
+	u16	ee_xlna_gain[AR5K_EEPROM_N_MODES];
+	u16	ee_xpd[AR5K_EEPROM_N_MODES];
+	u16	ee_x_gain[AR5K_EEPROM_N_MODES];
+	u16	ee_i_gain[AR5K_EEPROM_N_MODES];
+	u16	ee_margin_tx_rx[AR5K_EEPROM_N_MODES];
+	u16	ee_switch_settling_turbo[AR5K_EEPROM_N_MODES];
+	u16	ee_margin_tx_rx_turbo[AR5K_EEPROM_N_MODES];
+	u16	ee_atn_tx_rx_turbo[AR5K_EEPROM_N_MODES];
+
+	/* Power calibration data */
+	u16	ee_false_detect[AR5K_EEPROM_N_MODES];
+
+	/* Number of pd gain curves per mode */
+	u8	ee_pd_gains[AR5K_EEPROM_N_MODES];
+	/* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */
+	u8	ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS];
+
+	u8	ee_n_piers[AR5K_EEPROM_N_MODES];
+	struct ath5k_chan_pcal_info	ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
+	struct ath5k_chan_pcal_info	ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+	struct ath5k_chan_pcal_info	ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+
+	/* Per rate target power levels */
+	u8	ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
+	struct ath5k_rate_pcal_info	ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
+	struct ath5k_rate_pcal_info	ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+	struct ath5k_rate_pcal_info	ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+
+	/* Conformance test limits (Unused) */
+	u8	ee_ctls;
+	u8	ee_ctl[AR5K_EEPROM_MAX_CTLS];
+	struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
+
+	/* Noise Floor Calibration settings */
+	s16	ee_noise_floor_thr[AR5K_EEPROM_N_MODES];
+	s8	ee_adc_desired_size[AR5K_EEPROM_N_MODES];
+	s8	ee_pga_desired_size[AR5K_EEPROM_N_MODES];
+	s8	ee_adc_desired_size_turbo[AR5K_EEPROM_N_MODES];
+	s8	ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES];
+	s8	ee_pd_gain_overlap;
+
+	u32	ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
+};
+
diff --git a/gpxe/src/drivers/net/ath5k/reg.h b/gpxe/src/drivers/net/ath5k/reg.h
new file mode 100644
index 0000000..7070d15
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/reg.h
@@ -0,0 +1,2589 @@
+/*
+ * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm at gmail.com>
+ * Copyright (c) 2004-2008 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2007-2008 Michael Taylor <mike.taylor at apprion.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * Register values for Atheros 5210/5211/5212 cards from OpenBSD's ar5k
+ * maintained by Reyk Floeter
+ *
+ * I tried to document those registers by looking at ar5k code, some
+ * 802.11 (802.11e mostly) papers and by reading various public available
+ * Atheros presentations and papers like these:
+ *
+ * 5210 - http://nova.stanford.edu/~bbaas/ps/isscc2002_slides.pdf
+ *        http://www.it.iitb.ac.in/~janak/wifire/01222734.pdf
+ *
+ * 5211 - http://www.hotchips.org/archives/hc14/3_Tue/16_mcfarland.pdf
+ *
+ * This file also contains register values found on a memory dump of
+ * Atheros's ART program (Atheros Radio Test), on ath9k, on legacy-hal
+ * released by Atheros and on various debug messages found on the net.
+ */
+
+
+
+/*====MAC DMA REGISTERS====*/
+
+/*
+ * AR5210-Specific TXDP registers
+ * 5210 has only 2 transmit queues so no DCU/QCU, just
+ * 2 transmit descriptor pointers...
+ */
+#define AR5K_NOQCU_TXDP0	0x0000		/* Queue 0 - data */
+#define AR5K_NOQCU_TXDP1	0x0004		/* Queue 1 - beacons */
+
+/*
+ * Mac Control Register
+ */
+#define	AR5K_CR		0x0008			/* Register Address */
+#define AR5K_CR_TXE0	0x00000001	/* TX Enable for queue 0 on 5210 */
+#define AR5K_CR_TXE1	0x00000002	/* TX Enable for queue 1 on 5210 */
+#define	AR5K_CR_RXE	0x00000004	/* RX Enable */
+#define AR5K_CR_TXD0	0x00000008	/* TX Disable for queue 0 on 5210 */
+#define AR5K_CR_TXD1	0x00000010	/* TX Disable for queue 1 on 5210 */
+#define	AR5K_CR_RXD	0x00000020	/* RX Disable */
+#define	AR5K_CR_SWI	0x00000040	/* Software Interrupt */
+
+/*
+ * RX Descriptor Pointer register
+ */
+#define	AR5K_RXDP	0x000c
+
+/*
+ * Configuration and status register
+ */
+#define	AR5K_CFG		0x0014			/* Register Address */
+#define	AR5K_CFG_SWTD		0x00000001	/* Byte-swap TX descriptor (for big endian archs) */
+#define	AR5K_CFG_SWTB		0x00000002	/* Byte-swap TX buffer */
+#define	AR5K_CFG_SWRD		0x00000004	/* Byte-swap RX descriptor */
+#define	AR5K_CFG_SWRB		0x00000008	/* Byte-swap RX buffer */
+#define	AR5K_CFG_SWRG		0x00000010	/* Byte-swap Register access */
+#define AR5K_CFG_IBSS		0x00000020 	/* 0-BSS, 1-IBSS [5211+] */
+#define AR5K_CFG_PHY_OK		0x00000100	/* [5211+] */
+#define AR5K_CFG_EEBS		0x00000200	/* EEPROM is busy */
+#define	AR5K_CFG_CLKGD		0x00000400	/* Clock gated (Disable dynamic clock) */
+#define AR5K_CFG_TXCNT		0x00007800	/* Tx frame count (?) [5210] */
+#define AR5K_CFG_TXCNT_S	11
+#define AR5K_CFG_TXFSTAT	0x00008000	/* Tx frame status (?) [5210] */
+#define AR5K_CFG_TXFSTRT	0x00010000	/* [5210] */
+#define	AR5K_CFG_PCI_THRES	0x00060000	/* PCI Master req q threshold [5211+] */
+#define	AR5K_CFG_PCI_THRES_S	17
+
+/*
+ * Interrupt enable register
+ */
+#define AR5K_IER		0x0024		/* Register Address */
+#define AR5K_IER_DISABLE	0x00000000	/* Disable card interrupts */
+#define AR5K_IER_ENABLE		0x00000001	/* Enable card interrupts */
+
+
+/*
+ * 0x0028 is Beacon Control Register on 5210
+ * and first RTS duration register on 5211
+ */
+
+/*
+ * Beacon control register [5210]
+ */
+#define AR5K_BCR		0x0028		/* Register Address */
+#define AR5K_BCR_AP		0x00000000	/* AP mode */
+#define AR5K_BCR_ADHOC		0x00000001	/* Ad-Hoc mode */
+#define AR5K_BCR_BDMAE		0x00000002	/* DMA enable */
+#define AR5K_BCR_TQ1FV		0x00000004	/* Use Queue1 for CAB traffic */
+#define AR5K_BCR_TQ1V		0x00000008	/* Use Queue1 for Beacon traffic */
+#define AR5K_BCR_BCGET		0x00000010
+
+/*
+ * First RTS duration register [5211]
+ */
+#define AR5K_RTSD0		0x0028		/* Register Address */
+#define	AR5K_RTSD0_6		0x000000ff	/* 6Mb RTS duration mask (?) */
+#define	AR5K_RTSD0_6_S		0		/* 6Mb RTS duration shift (?) */
+#define	AR5K_RTSD0_9		0x0000ff00	/* 9Mb*/
+#define	AR5K_RTSD0_9_S		8
+#define	AR5K_RTSD0_12		0x00ff0000	/* 12Mb*/
+#define	AR5K_RTSD0_12_S		16
+#define	AR5K_RTSD0_18		0xff000000	/* 16Mb*/
+#define	AR5K_RTSD0_18_S		24
+
+
+/*
+ * 0x002c is Beacon Status Register on 5210
+ * and second RTS duration register on 5211
+ */
+
+/*
+ * Beacon status register [5210]
+ *
+ * As i can see in ar5k_ar5210_tx_start Reyk uses some of the values of BCR
+ * for this register, so i guess TQ1V,TQ1FV and BDMAE have the same meaning
+ * here and SNP/SNAP means "snapshot" (so this register gets synced with BCR).
+ * So SNAPPEDBCRVALID sould also stand for "snapped BCR -values- valid", so i
+ * renamed it to SNAPSHOTSVALID to make more sense. I realy have no idea what
+ * else can it be. I also renamed SNPBCMD to SNPADHOC to match BCR.
+ */
+#define AR5K_BSR		0x002c			/* Register Address */
+#define AR5K_BSR_BDLYSW		0x00000001	/* SW Beacon delay (?) */
+#define AR5K_BSR_BDLYDMA	0x00000002	/* DMA Beacon delay (?) */
+#define AR5K_BSR_TXQ1F		0x00000004	/* Beacon queue (1) finished */
+#define AR5K_BSR_ATIMDLY	0x00000008	/* ATIM delay (?) */
+#define AR5K_BSR_SNPADHOC	0x00000100	/* Ad-hoc mode set (?) */
+#define AR5K_BSR_SNPBDMAE	0x00000200	/* Beacon DMA enabled (?) */
+#define AR5K_BSR_SNPTQ1FV	0x00000400	/* Queue1 is used for CAB traffic (?) */
+#define AR5K_BSR_SNPTQ1V	0x00000800	/* Queue1 is used for Beacon traffic (?) */
+#define AR5K_BSR_SNAPSHOTSVALID	0x00001000	/* BCR snapshots are valid (?) */
+#define AR5K_BSR_SWBA_CNT	0x00ff0000
+
+/*
+ * Second RTS duration register [5211]
+ */
+#define AR5K_RTSD1		0x002c			/* Register Address */
+#define	AR5K_RTSD1_24		0x000000ff	/* 24Mb */
+#define	AR5K_RTSD1_24_S		0
+#define	AR5K_RTSD1_36		0x0000ff00	/* 36Mb */
+#define	AR5K_RTSD1_36_S		8
+#define	AR5K_RTSD1_48		0x00ff0000	/* 48Mb */
+#define	AR5K_RTSD1_48_S		16
+#define	AR5K_RTSD1_54		0xff000000	/* 54Mb */
+#define	AR5K_RTSD1_54_S		24
+
+
+/*
+ * Transmit configuration register
+ */
+#define AR5K_TXCFG			0x0030			/* Register Address */
+#define AR5K_TXCFG_SDMAMR		0x00000007	/* DMA size (read) */
+#define AR5K_TXCFG_SDMAMR_S		0
+#define AR5K_TXCFG_B_MODE		0x00000008	/* Set b mode for 5111 (enable 2111) */
+#define AR5K_TXCFG_TXFSTP		0x00000008	/* TX DMA full Stop [5210] */
+#define AR5K_TXCFG_TXFULL		0x000003f0	/* TX Triger level mask */
+#define AR5K_TXCFG_TXFULL_S		4
+#define AR5K_TXCFG_TXFULL_0B		0x00000000
+#define AR5K_TXCFG_TXFULL_64B		0x00000010
+#define AR5K_TXCFG_TXFULL_128B		0x00000020
+#define AR5K_TXCFG_TXFULL_192B		0x00000030
+#define AR5K_TXCFG_TXFULL_256B		0x00000040
+#define AR5K_TXCFG_TXCONT_EN		0x00000080
+#define AR5K_TXCFG_DMASIZE		0x00000100	/* Flag for passing DMA size [5210] */
+#define AR5K_TXCFG_JUMBO_DESC_EN	0x00000400	/* Enable jumbo tx descriptors [5211+] */
+#define AR5K_TXCFG_ADHOC_BCN_ATIM	0x00000800	/* Adhoc Beacon ATIM Policy */
+#define AR5K_TXCFG_ATIM_WINDOW_DEF_DIS	0x00001000	/* Disable ATIM window defer [5211+] */
+#define AR5K_TXCFG_RTSRND		0x00001000	/* [5211+] */
+#define AR5K_TXCFG_FRMPAD_DIS		0x00002000	/* [5211+] */
+#define AR5K_TXCFG_RDY_CBR_DIS		0x00004000	/* Ready time CBR disable [5211+] */
+#define AR5K_TXCFG_JUMBO_FRM_MODE	0x00008000	/* Jumbo frame mode [5211+] */
+#define	AR5K_TXCFG_DCU_DBL_BUF_DIS	0x00008000	/* Disable double buffering on DCU */
+#define AR5K_TXCFG_DCU_CACHING_DIS	0x00010000	/* Disable DCU caching */
+
+/*
+ * Receive configuration register
+ */
+#define AR5K_RXCFG		0x0034			/* Register Address */
+#define AR5K_RXCFG_SDMAMW	0x00000007	/* DMA size (write) */
+#define AR5K_RXCFG_SDMAMW_S	0
+#define AR5K_RXCFG_ZLFDMA	0x00000008	/* Enable Zero-length frame DMA */
+#define	AR5K_RXCFG_DEF_ANTENNA	0x00000010	/* Default antenna (?) */
+#define AR5K_RXCFG_JUMBO_RXE	0x00000020	/* Enable jumbo rx descriptors [5211+] */
+#define AR5K_RXCFG_JUMBO_WRAP	0x00000040	/* Wrap jumbo frames [5211+] */
+#define AR5K_RXCFG_SLE_ENTRY	0x00000080	/* Sleep entry policy */
+
+/*
+ * Receive jumbo descriptor last address register
+ * Only found in 5211 (?)
+ */
+#define AR5K_RXJLA		0x0038
+
+/*
+ * MIB control register
+ */
+#define AR5K_MIBC		0x0040			/* Register Address */
+#define AR5K_MIBC_COW		0x00000001	/* Warn test indicator */
+#define AR5K_MIBC_FMC		0x00000002	/* Freeze MIB Counters  */
+#define AR5K_MIBC_CMC		0x00000004	/* Clean MIB Counters  */
+#define AR5K_MIBC_MCS		0x00000008	/* MIB counter strobe */
+
+/*
+ * Timeout prescale register
+ */
+#define AR5K_TOPS		0x0044
+#define	AR5K_TOPS_M		0x0000ffff
+
+/*
+ * Receive timeout register (no frame received)
+ */
+#define AR5K_RXNOFRM		0x0048
+#define	AR5K_RXNOFRM_M		0x000003ff
+
+/*
+ * Transmit timeout register (no frame sent)
+ */
+#define AR5K_TXNOFRM		0x004c
+#define	AR5K_TXNOFRM_M		0x000003ff
+#define	AR5K_TXNOFRM_QCU	0x000ffc00
+#define	AR5K_TXNOFRM_QCU_S	10
+
+/*
+ * Receive frame gap timeout register
+ */
+#define AR5K_RPGTO		0x0050
+#define AR5K_RPGTO_M		0x000003ff
+
+/*
+ * Receive frame count limit register
+ */
+#define AR5K_RFCNT		0x0054
+#define AR5K_RFCNT_M		0x0000001f	/* [5211+] (?) */
+#define AR5K_RFCNT_RFCL		0x0000000f	/* [5210] */
+
+/*
+ * Misc settings register
+ * (reserved0-3)
+ */
+#define AR5K_MISC		0x0058			/* Register Address */
+#define	AR5K_MISC_DMA_OBS_M	0x000001e0
+#define	AR5K_MISC_DMA_OBS_S	5
+#define	AR5K_MISC_MISC_OBS_M	0x00000e00
+#define	AR5K_MISC_MISC_OBS_S	9
+#define	AR5K_MISC_MAC_OBS_LSB_M	0x00007000
+#define	AR5K_MISC_MAC_OBS_LSB_S	12
+#define	AR5K_MISC_MAC_OBS_MSB_M	0x00038000
+#define	AR5K_MISC_MAC_OBS_MSB_S	15
+#define AR5K_MISC_LED_DECAY	0x001c0000	/* [5210] */
+#define AR5K_MISC_LED_BLINK	0x00e00000	/* [5210] */
+
+/*
+ * QCU/DCU clock gating register (5311)
+ * (reserved4-5)
+ */
+#define	AR5K_QCUDCU_CLKGT	0x005c			/* Register Address (?) */
+#define	AR5K_QCUDCU_CLKGT_QCU	0x0000ffff	/* Mask for QCU clock */
+#define	AR5K_QCUDCU_CLKGT_DCU	0x07ff0000	/* Mask for DCU clock */
+
+/*
+ * Interrupt Status Registers
+ *
+ * For 5210 there is only one status register but for
+ * 5211/5212 we have one primary and 4 secondary registers.
+ * So we have AR5K_ISR for 5210 and AR5K_PISR /SISRx for 5211/5212.
+ * Most of these bits are common for all chipsets.
+ */
+#define AR5K_ISR		0x001c			/* Register Address [5210] */
+#define AR5K_PISR		0x0080			/* Register Address [5211+] */
+#define AR5K_ISR_RXOK		0x00000001	/* Frame successfuly recieved */
+#define AR5K_ISR_RXDESC		0x00000002	/* RX descriptor request */
+#define AR5K_ISR_RXERR		0x00000004	/* Receive error */
+#define AR5K_ISR_RXNOFRM	0x00000008	/* No frame received (receive timeout) */
+#define AR5K_ISR_RXEOL		0x00000010	/* Empty RX descriptor */
+#define AR5K_ISR_RXORN		0x00000020	/* Receive FIFO overrun */
+#define AR5K_ISR_TXOK		0x00000040	/* Frame successfuly transmited */
+#define AR5K_ISR_TXDESC		0x00000080	/* TX descriptor request */
+#define AR5K_ISR_TXERR		0x00000100	/* Transmit error */
+#define AR5K_ISR_TXNOFRM	0x00000200	/* No frame transmited (transmit timeout) */
+#define AR5K_ISR_TXEOL		0x00000400	/* Empty TX descriptor */
+#define AR5K_ISR_TXURN		0x00000800	/* Transmit FIFO underrun */
+#define AR5K_ISR_MIB		0x00001000	/* Update MIB counters */
+#define AR5K_ISR_SWI		0x00002000	/* Software interrupt */
+#define AR5K_ISR_RXPHY		0x00004000	/* PHY error */
+#define AR5K_ISR_RXKCM		0x00008000	/* RX Key cache miss */
+#define AR5K_ISR_SWBA		0x00010000	/* Software beacon alert */
+#define AR5K_ISR_BRSSI		0x00020000	/* Beacon rssi below threshold (?) */
+#define AR5K_ISR_BMISS		0x00040000	/* Beacon missed */
+#define AR5K_ISR_HIUERR		0x00080000	/* Host Interface Unit error [5211+] */
+#define AR5K_ISR_BNR		0x00100000 	/* Beacon not ready [5211+] */
+#define AR5K_ISR_MCABT		0x00100000	/* Master Cycle Abort [5210] */
+#define AR5K_ISR_RXCHIRP	0x00200000	/* CHIRP Received [5212+] */
+#define AR5K_ISR_SSERR		0x00200000	/* Signaled System Error [5210] */
+#define AR5K_ISR_DPERR		0x00400000	/* Det par Error (?) [5210] */
+#define AR5K_ISR_RXDOPPLER	0x00400000	/* Doppler chirp received [5212+] */
+#define AR5K_ISR_TIM		0x00800000	/* [5211+] */
+#define AR5K_ISR_BCNMISC	0x00800000	/* 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT,
+						CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */
+#define AR5K_ISR_GPIO		0x01000000	/* GPIO (rf kill) */
+#define AR5K_ISR_QCBRORN	0x02000000	/* QCU CBR overrun [5211+] */
+#define AR5K_ISR_QCBRURN	0x04000000	/* QCU CBR underrun [5211+] */
+#define AR5K_ISR_QTRIG		0x08000000	/* QCU scheduling trigger [5211+] */
+
+/*
+ * Secondary status registers [5211+] (0 - 4)
+ *
+ * These give the status for each QCU, only QCUs 0-9 are
+ * represented.
+ */
+#define AR5K_SISR0		0x0084			/* Register Address [5211+] */
+#define AR5K_SISR0_QCU_TXOK	0x000003ff	/* Mask for QCU_TXOK */
+#define AR5K_SISR0_QCU_TXOK_S	0
+#define AR5K_SISR0_QCU_TXDESC	0x03ff0000	/* Mask for QCU_TXDESC */
+#define AR5K_SISR0_QCU_TXDESC_S	16
+
+#define AR5K_SISR1		0x0088			/* Register Address [5211+] */
+#define AR5K_SISR1_QCU_TXERR	0x000003ff	/* Mask for QCU_TXERR */
+#define AR5K_SISR1_QCU_TXERR_S	0
+#define AR5K_SISR1_QCU_TXEOL	0x03ff0000	/* Mask for QCU_TXEOL */
+#define AR5K_SISR1_QCU_TXEOL_S	16
+
+#define AR5K_SISR2		0x008c			/* Register Address [5211+] */
+#define AR5K_SISR2_QCU_TXURN	0x000003ff	/* Mask for QCU_TXURN */
+#define	AR5K_SISR2_QCU_TXURN_S	0
+#define	AR5K_SISR2_MCABT	0x00100000	/* Master Cycle Abort */
+#define	AR5K_SISR2_SSERR	0x00200000	/* Signaled System Error */
+#define	AR5K_SISR2_DPERR	0x00400000	/* Bus parity error */
+#define	AR5K_SISR2_TIM		0x01000000	/* [5212+] */
+#define	AR5K_SISR2_CAB_END	0x02000000	/* [5212+] */
+#define	AR5K_SISR2_DTIM_SYNC	0x04000000	/* DTIM sync lost [5212+] */
+#define	AR5K_SISR2_BCN_TIMEOUT	0x08000000	/* Beacon Timeout [5212+] */
+#define	AR5K_SISR2_CAB_TIMEOUT	0x10000000	/* CAB Timeout [5212+] */
+#define	AR5K_SISR2_DTIM		0x20000000	/* [5212+] */
+#define	AR5K_SISR2_TSFOOR	0x80000000	/* TSF OOR (?) */
+
+#define AR5K_SISR3		0x0090			/* Register Address [5211+] */
+#define AR5K_SISR3_QCBRORN	0x000003ff	/* Mask for QCBRORN */
+#define AR5K_SISR3_QCBRORN_S	0
+#define AR5K_SISR3_QCBRURN	0x03ff0000	/* Mask for QCBRURN */
+#define AR5K_SISR3_QCBRURN_S	16
+
+#define AR5K_SISR4		0x0094			/* Register Address [5211+] */
+#define AR5K_SISR4_QTRIG	0x000003ff	/* Mask for QTRIG */
+#define AR5K_SISR4_QTRIG_S	0
+
+/*
+ * Shadow read-and-clear interrupt status registers [5211+]
+ */
+#define AR5K_RAC_PISR		0x00c0		/* Read and clear PISR */
+#define AR5K_RAC_SISR0		0x00c4		/* Read and clear SISR0 */
+#define AR5K_RAC_SISR1		0x00c8		/* Read and clear SISR1 */
+#define AR5K_RAC_SISR2		0x00cc		/* Read and clear SISR2 */
+#define AR5K_RAC_SISR3		0x00d0		/* Read and clear SISR3 */
+#define AR5K_RAC_SISR4		0x00d4		/* Read and clear SISR4 */
+
+/*
+ * Interrupt Mask Registers
+ *
+ * As whith ISRs 5210 has one IMR (AR5K_IMR) and 5211/5212 has one primary
+ * (AR5K_PIMR) and 4 secondary IMRs (AR5K_SIMRx). Note that ISR/IMR flags match.
+ */
+#define	AR5K_IMR		0x0020			/* Register Address [5210] */
+#define AR5K_PIMR		0x00a0			/* Register Address [5211+] */
+#define AR5K_IMR_RXOK		0x00000001	/* Frame successfuly recieved*/
+#define AR5K_IMR_RXDESC		0x00000002	/* RX descriptor request*/
+#define AR5K_IMR_RXERR		0x00000004	/* Receive error*/
+#define AR5K_IMR_RXNOFRM	0x00000008	/* No frame received (receive timeout)*/
+#define AR5K_IMR_RXEOL		0x00000010	/* Empty RX descriptor*/
+#define AR5K_IMR_RXORN		0x00000020	/* Receive FIFO overrun*/
+#define AR5K_IMR_TXOK		0x00000040	/* Frame successfuly transmited*/
+#define AR5K_IMR_TXDESC		0x00000080	/* TX descriptor request*/
+#define AR5K_IMR_TXERR		0x00000100	/* Transmit error*/
+#define AR5K_IMR_TXNOFRM	0x00000200	/* No frame transmited (transmit timeout)*/
+#define AR5K_IMR_TXEOL		0x00000400	/* Empty TX descriptor*/
+#define AR5K_IMR_TXURN		0x00000800	/* Transmit FIFO underrun*/
+#define AR5K_IMR_MIB		0x00001000	/* Update MIB counters*/
+#define AR5K_IMR_SWI		0x00002000	/* Software interrupt */
+#define AR5K_IMR_RXPHY		0x00004000	/* PHY error*/
+#define AR5K_IMR_RXKCM		0x00008000	/* RX Key cache miss */
+#define AR5K_IMR_SWBA		0x00010000	/* Software beacon alert*/
+#define AR5K_IMR_BRSSI		0x00020000	/* Beacon rssi below threshold (?) */
+#define AR5K_IMR_BMISS		0x00040000	/* Beacon missed*/
+#define AR5K_IMR_HIUERR		0x00080000	/* Host Interface Unit error [5211+] */
+#define AR5K_IMR_BNR		0x00100000 	/* Beacon not ready [5211+] */
+#define AR5K_IMR_MCABT		0x00100000	/* Master Cycle Abort [5210] */
+#define AR5K_IMR_RXCHIRP	0x00200000	/* CHIRP Received [5212+]*/
+#define AR5K_IMR_SSERR		0x00200000	/* Signaled System Error [5210] */
+#define AR5K_IMR_DPERR		0x00400000	/* Det par Error (?) [5210] */
+#define AR5K_IMR_RXDOPPLER	0x00400000	/* Doppler chirp received [5212+] */
+#define AR5K_IMR_TIM		0x00800000	/* [5211+] */
+#define AR5K_IMR_BCNMISC	0x00800000	/* 'or' of TIM, CAB_END, DTIM_SYNC, BCN_TIMEOUT,
+						CAB_TIMEOUT and DTIM bits from SISR2 [5212+] */
+#define AR5K_IMR_GPIO		0x01000000	/* GPIO (rf kill)*/
+#define AR5K_IMR_QCBRORN	0x02000000	/* QCU CBR overrun (?) [5211+] */
+#define AR5K_IMR_QCBRURN	0x04000000	/* QCU CBR underrun (?) [5211+] */
+#define AR5K_IMR_QTRIG		0x08000000	/* QCU scheduling trigger [5211+] */
+
+/*
+ * Secondary interrupt mask registers [5211+] (0 - 4)
+ */
+#define AR5K_SIMR0		0x00a4			/* Register Address [5211+] */
+#define AR5K_SIMR0_QCU_TXOK	0x000003ff	/* Mask for QCU_TXOK */
+#define AR5K_SIMR0_QCU_TXOK_S	0
+#define AR5K_SIMR0_QCU_TXDESC	0x03ff0000	/* Mask for QCU_TXDESC */
+#define AR5K_SIMR0_QCU_TXDESC_S	16
+
+#define AR5K_SIMR1		0x00a8			/* Register Address [5211+] */
+#define AR5K_SIMR1_QCU_TXERR	0x000003ff	/* Mask for QCU_TXERR */
+#define AR5K_SIMR1_QCU_TXERR_S	0
+#define AR5K_SIMR1_QCU_TXEOL	0x03ff0000	/* Mask for QCU_TXEOL */
+#define AR5K_SIMR1_QCU_TXEOL_S	16
+
+#define AR5K_SIMR2		0x00ac			/* Register Address [5211+] */
+#define AR5K_SIMR2_QCU_TXURN	0x000003ff	/* Mask for QCU_TXURN */
+#define AR5K_SIMR2_QCU_TXURN_S	0
+#define	AR5K_SIMR2_MCABT	0x00100000	/* Master Cycle Abort */
+#define	AR5K_SIMR2_SSERR	0x00200000	/* Signaled System Error */
+#define	AR5K_SIMR2_DPERR	0x00400000	/* Bus parity error */
+#define	AR5K_SIMR2_TIM		0x01000000	/* [5212+] */
+#define	AR5K_SIMR2_CAB_END	0x02000000	/* [5212+] */
+#define	AR5K_SIMR2_DTIM_SYNC	0x04000000	/* DTIM Sync lost [5212+] */
+#define	AR5K_SIMR2_BCN_TIMEOUT	0x08000000	/* Beacon Timeout [5212+] */
+#define	AR5K_SIMR2_CAB_TIMEOUT	0x10000000	/* CAB Timeout [5212+] */
+#define	AR5K_SIMR2_DTIM		0x20000000	/* [5212+] */
+#define	AR5K_SIMR2_TSFOOR	0x80000000	/* TSF OOR (?) */
+
+#define AR5K_SIMR3		0x00b0			/* Register Address [5211+] */
+#define AR5K_SIMR3_QCBRORN	0x000003ff	/* Mask for QCBRORN */
+#define AR5K_SIMR3_QCBRORN_S	0
+#define AR5K_SIMR3_QCBRURN	0x03ff0000	/* Mask for QCBRURN */
+#define AR5K_SIMR3_QCBRURN_S	16
+
+#define AR5K_SIMR4		0x00b4			/* Register Address [5211+] */
+#define AR5K_SIMR4_QTRIG	0x000003ff	/* Mask for QTRIG */
+#define AR5K_SIMR4_QTRIG_S	0
+
+/*
+ * DMA Debug registers 0-7
+ * 0xe0 - 0xfc
+ */
+
+/*
+ * Decompression mask registers [5212+]
+ */
+#define AR5K_DCM_ADDR		0x0400		/*Decompression mask address (index) */
+#define AR5K_DCM_DATA		0x0404		/*Decompression mask data */
+
+/*
+ * Wake On Wireless pattern control register [5212+]
+ */
+#define	AR5K_WOW_PCFG			0x0410			/* Register Address */
+#define	AR5K_WOW_PCFG_PAT_MATCH_EN	0x00000001	/* Pattern match enable */
+#define	AR5K_WOW_PCFG_LONG_FRAME_POL	0x00000002	/* Long frame policy */
+#define	AR5K_WOW_PCFG_WOBMISS		0x00000004	/* Wake on bea(con) miss (?) */
+#define	AR5K_WOW_PCFG_PAT_0_EN		0x00000100	/* Enable pattern 0 */
+#define	AR5K_WOW_PCFG_PAT_1_EN		0x00000200	/* Enable pattern 1 */
+#define	AR5K_WOW_PCFG_PAT_2_EN		0x00000400	/* Enable pattern 2 */
+#define	AR5K_WOW_PCFG_PAT_3_EN		0x00000800	/* Enable pattern 3 */
+#define	AR5K_WOW_PCFG_PAT_4_EN		0x00001000	/* Enable pattern 4 */
+#define	AR5K_WOW_PCFG_PAT_5_EN		0x00002000	/* Enable pattern 5 */
+
+/*
+ * Wake On Wireless pattern index register (?) [5212+]
+ */
+#define	AR5K_WOW_PAT_IDX	0x0414
+
+/*
+ * Wake On Wireless pattern data register [5212+]
+ */
+#define	AR5K_WOW_PAT_DATA	0x0418			/* Register Address */
+#define	AR5K_WOW_PAT_DATA_0_3_V	0x00000001	/* Pattern 0, 3 value */
+#define	AR5K_WOW_PAT_DATA_1_4_V	0x00000100	/* Pattern 1, 4 value */
+#define	AR5K_WOW_PAT_DATA_2_5_V	0x00010000	/* Pattern 2, 5 value */
+#define	AR5K_WOW_PAT_DATA_0_3_M	0x01000000	/* Pattern 0, 3 mask */
+#define	AR5K_WOW_PAT_DATA_1_4_M	0x04000000	/* Pattern 1, 4 mask */
+#define	AR5K_WOW_PAT_DATA_2_5_M	0x10000000	/* Pattern 2, 5 mask */
+
+/*
+ * Decompression configuration registers [5212+]
+ */
+#define AR5K_DCCFG		0x0420			/* Register Address */
+#define AR5K_DCCFG_GLOBAL_EN	0x00000001	/* Enable decompression on all queues */
+#define AR5K_DCCFG_BYPASS_EN	0x00000002	/* Bypass decompression */
+#define AR5K_DCCFG_BCAST_EN	0x00000004	/* Enable decompression for bcast frames */
+#define AR5K_DCCFG_MCAST_EN	0x00000008	/* Enable decompression for mcast frames */
+
+/*
+ * Compression configuration registers [5212+]
+ */
+#define AR5K_CCFG		0x0600			/* Register Address */
+#define	AR5K_CCFG_WINDOW_SIZE	0x00000007	/* Compression window size */
+#define	AR5K_CCFG_CPC_EN	0x00000008	/* Enable performance counters */
+
+#define AR5K_CCFG_CCU		0x0604			/* Register Address */
+#define AR5K_CCFG_CCU_CUP_EN	0x00000001	/* CCU Catchup enable */
+#define AR5K_CCFG_CCU_CREDIT	0x00000002	/* CCU Credit (field) */
+#define AR5K_CCFG_CCU_CD_THRES	0x00000080	/* CCU Cyc(lic?) debt threshold (field) */
+#define AR5K_CCFG_CCU_CUP_LCNT	0x00010000	/* CCU Catchup lit(?) count */
+#define	AR5K_CCFG_CCU_INIT	0x00100200	/* Initial value during reset */
+
+/*
+ * Compression performance counter registers [5212+]
+ */
+#define AR5K_CPC0		0x0610		/* Compression performance counter 0 */
+#define AR5K_CPC1		0x0614		/* Compression performance counter 1*/
+#define AR5K_CPC2		0x0618		/* Compression performance counter 2 */
+#define AR5K_CPC3		0x061c		/* Compression performance counter 3 */
+#define AR5K_CPCOVF		0x0620		/* Compression performance overflow */
+
+
+/*
+ * Queue control unit (QCU) registers [5211+]
+ *
+ * Card has 12 TX Queues but i see that only 0-9 are used (?)
+ * both in binary HAL (see ah.h) and ar5k. Each queue has it's own
+ * TXDP at addresses 0x0800 - 0x082c, a CBR (Constant Bit Rate)
+ * configuration register (0x08c0 - 0x08ec), a ready time configuration
+ * register (0x0900 - 0x092c), a misc configuration register (0x09c0 -
+ * 0x09ec) and a status register (0x0a00 - 0x0a2c). We also have some
+ * global registers, QCU transmit enable/disable and "one shot arm (?)"
+ * set/clear, which contain status for all queues (we shift by 1 for each
+ * queue). To access these registers easily we define some macros here
+ * that are used inside HAL. For more infos check out *_tx_queue functs.
+ */
+
+/*
+ * Generic QCU Register access macros
+ */
+#define	AR5K_QUEUE_REG(_r, _q)		(((_q) << 2) + _r)
+#define AR5K_QCU_GLOBAL_READ(_r, _q)	(AR5K_REG_READ(_r) & (1 << _q))
+#define AR5K_QCU_GLOBAL_WRITE(_r, _q)	AR5K_REG_WRITE(_r, (1 << _q))
+
+/*
+ * QCU Transmit descriptor pointer registers
+ */
+#define AR5K_QCU_TXDP_BASE	0x0800		/* Register Address - Queue0 TXDP */
+#define AR5K_QUEUE_TXDP(_q)	AR5K_QUEUE_REG(AR5K_QCU_TXDP_BASE, _q)
+
+/*
+ * QCU Transmit enable register
+ */
+#define AR5K_QCU_TXE		0x0840
+#define AR5K_ENABLE_QUEUE(_q)	AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXE, _q)
+#define AR5K_QUEUE_ENABLED(_q)	AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXE, _q)
+
+/*
+ * QCU Transmit disable register
+ */
+#define AR5K_QCU_TXD		0x0880
+#define AR5K_DISABLE_QUEUE(_q)	AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXD, _q)
+#define AR5K_QUEUE_DISABLED(_q)	AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXD, _q)
+
+/*
+ * QCU Constant Bit Rate configuration registers
+ */
+#define	AR5K_QCU_CBRCFG_BASE		0x08c0	/* Register Address - Queue0 CBRCFG */
+#define	AR5K_QCU_CBRCFG_INTVAL		0x00ffffff	/* CBR Interval mask */
+#define AR5K_QCU_CBRCFG_INTVAL_S	0
+#define	AR5K_QCU_CBRCFG_ORN_THRES	0xff000000	/* CBR overrun threshold mask */
+#define AR5K_QCU_CBRCFG_ORN_THRES_S	24
+#define	AR5K_QUEUE_CBRCFG(_q)		AR5K_QUEUE_REG(AR5K_QCU_CBRCFG_BASE, _q)
+
+/*
+ * QCU Ready time configuration registers
+ */
+#define	AR5K_QCU_RDYTIMECFG_BASE	0x0900	/* Register Address - Queue0 RDYTIMECFG */
+#define	AR5K_QCU_RDYTIMECFG_INTVAL	0x00ffffff	/* Ready time interval mask */
+#define AR5K_QCU_RDYTIMECFG_INTVAL_S	0
+#define	AR5K_QCU_RDYTIMECFG_ENABLE	0x01000000	/* Ready time enable mask */
+#define AR5K_QUEUE_RDYTIMECFG(_q)	AR5K_QUEUE_REG(AR5K_QCU_RDYTIMECFG_BASE, _q)
+
+/*
+ * QCU one shot arm set registers
+ */
+#define	AR5K_QCU_ONESHOTARM_SET		0x0940	/* Register Address -QCU "one shot arm set (?)" */
+#define	AR5K_QCU_ONESHOTARM_SET_M	0x0000ffff
+
+/*
+ * QCU one shot arm clear registers
+ */
+#define	AR5K_QCU_ONESHOTARM_CLEAR	0x0980	/* Register Address -QCU "one shot arm clear (?)" */
+#define	AR5K_QCU_ONESHOTARM_CLEAR_M	0x0000ffff
+
+/*
+ * QCU misc registers
+ */
+#define AR5K_QCU_MISC_BASE		0x09c0			/* Register Address -Queue0 MISC */
+#define	AR5K_QCU_MISC_FRSHED_M		0x0000000f	/* Frame sheduling mask */
+#define	AR5K_QCU_MISC_FRSHED_ASAP		0	/* ASAP */
+#define	AR5K_QCU_MISC_FRSHED_CBR		1	/* Constant Bit Rate */
+#define	AR5K_QCU_MISC_FRSHED_DBA_GT		2	/* DMA Beacon alert gated */
+#define	AR5K_QCU_MISC_FRSHED_TIM_GT		3	/* TIMT gated */
+#define	AR5K_QCU_MISC_FRSHED_BCN_SENT_GT	4	/* Beacon sent gated */
+#define	AR5K_QCU_MISC_ONESHOT_ENABLE	0x00000010	/* Oneshot enable */
+#define	AR5K_QCU_MISC_CBREXP_DIS	0x00000020	/* Disable CBR expired counter (normal queue) */
+#define	AR5K_QCU_MISC_CBREXP_BCN_DIS	0x00000040	/* Disable CBR expired counter (beacon queue) */
+#define	AR5K_QCU_MISC_BCN_ENABLE	0x00000080	/* Enable Beacon use */
+#define	AR5K_QCU_MISC_CBR_THRES_ENABLE	0x00000100	/* CBR expired threshold enabled */
+#define	AR5K_QCU_MISC_RDY_VEOL_POLICY	0x00000200	/* TXE reset when RDYTIME expired or VEOL */
+#define	AR5K_QCU_MISC_CBR_RESET_CNT	0x00000400	/* CBR threshold (counter) reset */
+#define	AR5K_QCU_MISC_DCU_EARLY		0x00000800	/* DCU early termination */
+#define AR5K_QCU_MISC_DCU_CMP_EN	0x00001000	/* Enable frame compression */
+#define AR5K_QUEUE_MISC(_q)		AR5K_QUEUE_REG(AR5K_QCU_MISC_BASE, _q)
+
+
+/*
+ * QCU status registers
+ */
+#define AR5K_QCU_STS_BASE	0x0a00			/* Register Address - Queue0 STS */
+#define	AR5K_QCU_STS_FRMPENDCNT	0x00000003	/* Frames pending counter */
+#define	AR5K_QCU_STS_CBREXPCNT	0x0000ff00	/* CBR expired counter */
+#define	AR5K_QUEUE_STATUS(_q)	AR5K_QUEUE_REG(AR5K_QCU_STS_BASE, _q)
+
+/*
+ * QCU ready time shutdown register
+ */
+#define AR5K_QCU_RDYTIMESHDN	0x0a40
+#define AR5K_QCU_RDYTIMESHDN_M	0x000003ff
+
+/*
+ * QCU compression buffer base registers [5212+]
+ */
+#define AR5K_QCU_CBB_SELECT	0x0b00
+#define AR5K_QCU_CBB_ADDR	0x0b04
+#define AR5K_QCU_CBB_ADDR_S	9
+
+/*
+ * QCU compression buffer configuration register [5212+]
+ * (buffer size)
+ */
+#define AR5K_QCU_CBCFG		0x0b08
+
+
+
+/*
+ * Distributed Coordination Function (DCF) control unit (DCU)
+ * registers [5211+]
+ *
+ * These registers control the various characteristics of each queue
+ * for 802.11e (WME) combatibility so they go together with
+ * QCU registers in pairs. For each queue we have a QCU mask register,
+ * (0x1000 - 0x102c), a local-IFS settings register (0x1040 - 0x106c),
+ * a retry limit register (0x1080 - 0x10ac), a channel time register
+ * (0x10c0 - 0x10ec), a misc-settings register (0x1100 - 0x112c) and
+ * a sequence number register (0x1140 - 0x116c). It seems that "global"
+ * registers here afect all queues (see use of DCU_GBL_IFS_SLOT in ar5k).
+ * We use the same macros here for easier register access.
+ *
+ */
+
+/*
+ * DCU QCU mask registers
+ */
+#define AR5K_DCU_QCUMASK_BASE	0x1000		/* Register Address -Queue0 DCU_QCUMASK */
+#define AR5K_DCU_QCUMASK_M	0x000003ff
+#define AR5K_QUEUE_QCUMASK(_q)	AR5K_QUEUE_REG(AR5K_DCU_QCUMASK_BASE, _q)
+
+/*
+ * DCU local Inter Frame Space settings register
+ */
+#define AR5K_DCU_LCL_IFS_BASE		0x1040			/* Register Address -Queue0 DCU_LCL_IFS */
+#define	AR5K_DCU_LCL_IFS_CW_MIN	        0x000003ff	/* Minimum Contention Window */
+#define	AR5K_DCU_LCL_IFS_CW_MIN_S	0
+#define	AR5K_DCU_LCL_IFS_CW_MAX	        0x000ffc00	/* Maximum Contention Window */
+#define	AR5K_DCU_LCL_IFS_CW_MAX_S	10
+#define	AR5K_DCU_LCL_IFS_AIFS		0x0ff00000	/* Arbitrated Interframe Space */
+#define	AR5K_DCU_LCL_IFS_AIFS_S		20
+#define	AR5K_DCU_LCL_IFS_AIFS_MAX	0xfc		/* Anything above that can cause DCU to hang */
+#define	AR5K_QUEUE_DFS_LOCAL_IFS(_q)	AR5K_QUEUE_REG(AR5K_DCU_LCL_IFS_BASE, _q)
+
+/*
+ * DCU retry limit registers
+ */
+#define AR5K_DCU_RETRY_LMT_BASE		0x1080			/* Register Address -Queue0 DCU_RETRY_LMT */
+#define AR5K_DCU_RETRY_LMT_SH_RETRY	0x0000000f	/* Short retry limit mask */
+#define AR5K_DCU_RETRY_LMT_SH_RETRY_S	0
+#define AR5K_DCU_RETRY_LMT_LG_RETRY	0x000000f0	/* Long retry limit mask */
+#define AR5K_DCU_RETRY_LMT_LG_RETRY_S	4
+#define AR5K_DCU_RETRY_LMT_SSH_RETRY	0x00003f00	/* Station short retry limit mask (?) */
+#define AR5K_DCU_RETRY_LMT_SSH_RETRY_S	8
+#define AR5K_DCU_RETRY_LMT_SLG_RETRY	0x000fc000	/* Station long retry limit mask (?) */
+#define AR5K_DCU_RETRY_LMT_SLG_RETRY_S	14
+#define	AR5K_QUEUE_DFS_RETRY_LIMIT(_q)	AR5K_QUEUE_REG(AR5K_DCU_RETRY_LMT_BASE, _q)
+
+/*
+ * DCU channel time registers
+ */
+#define AR5K_DCU_CHAN_TIME_BASE		0x10c0			/* Register Address -Queue0 DCU_CHAN_TIME */
+#define	AR5K_DCU_CHAN_TIME_DUR		0x000fffff	/* Channel time duration */
+#define	AR5K_DCU_CHAN_TIME_DUR_S	0
+#define	AR5K_DCU_CHAN_TIME_ENABLE	0x00100000	/* Enable channel time */
+#define AR5K_QUEUE_DFS_CHANNEL_TIME(_q)	AR5K_QUEUE_REG(AR5K_DCU_CHAN_TIME_BASE, _q)
+
+/*
+ * DCU misc registers [5211+]
+ *
+ * Note: Arbiter lockout control controls the
+ * behaviour on low priority queues when we have multiple queues
+ * with pending frames. Intra-frame lockout means we wait until
+ * the queue's current frame transmits (with post frame backoff and bursting)
+ * before we transmit anything else and global lockout means we
+ * wait for the whole queue to finish before higher priority queues
+ * can transmit (this is used on beacon and CAB queues).
+ * No lockout means there is no special handling.
+ */
+#define AR5K_DCU_MISC_BASE		0x1100			/* Register Address -Queue0 DCU_MISC */
+#define	AR5K_DCU_MISC_BACKOFF		0x0000003f	/* Mask for backoff threshold */
+#define	AR5K_DCU_MISC_ETS_RTS_POL	0x00000040	/* End of transmission series
+							station RTS/data failure count
+							reset policy (?) */
+#define AR5K_DCU_MISC_ETS_CW_POL	0x00000080	/* End of transmission series
+							CW reset policy */
+#define	AR5K_DCU_MISC_FRAG_WAIT		0x00000100	/* Wait for next fragment */
+#define AR5K_DCU_MISC_BACKOFF_FRAG	0x00000200	/* Enable backoff while bursting */
+#define	AR5K_DCU_MISC_HCFPOLL_ENABLE	0x00000800	/* CF - Poll enable */
+#define	AR5K_DCU_MISC_BACKOFF_PERSIST	0x00001000	/* Persistent backoff */
+#define	AR5K_DCU_MISC_FRMPRFTCH_ENABLE	0x00002000	/* Enable frame pre-fetch */
+#define	AR5K_DCU_MISC_VIRTCOL		0x0000c000	/* Mask for Virtual Collision (?) */
+#define	AR5K_DCU_MISC_VIRTCOL_NORMAL	0
+#define	AR5K_DCU_MISC_VIRTCOL_IGNORE	1
+#define	AR5K_DCU_MISC_BCN_ENABLE	0x00010000	/* Enable Beacon use */
+#define	AR5K_DCU_MISC_ARBLOCK_CTL	0x00060000	/* Arbiter lockout control mask */
+#define	AR5K_DCU_MISC_ARBLOCK_CTL_S	17
+#define	AR5K_DCU_MISC_ARBLOCK_CTL_NONE		0	/* No arbiter lockout */
+#define	AR5K_DCU_MISC_ARBLOCK_CTL_INTFRM	1	/* Intra-frame lockout */
+#define	AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL	2	/* Global lockout */
+#define	AR5K_DCU_MISC_ARBLOCK_IGNORE	0x00080000	/* Ignore Arbiter lockout */
+#define	AR5K_DCU_MISC_SEQ_NUM_INCR_DIS	0x00100000	/* Disable sequence number increment */
+#define	AR5K_DCU_MISC_POST_FR_BKOFF_DIS	0x00200000	/* Disable post-frame backoff */
+#define	AR5K_DCU_MISC_VIRT_COLL_POLICY	0x00400000	/* Virtual Collision cw policy */
+#define	AR5K_DCU_MISC_BLOWN_IFS_POLICY	0x00800000	/* Blown IFS policy (?) */
+#define	AR5K_DCU_MISC_SEQNUM_CTL	0x01000000	/* Sequence number control (?) */
+#define AR5K_QUEUE_DFS_MISC(_q)		AR5K_QUEUE_REG(AR5K_DCU_MISC_BASE, _q)
+
+/*
+ * DCU frame sequence number registers
+ */
+#define AR5K_DCU_SEQNUM_BASE		0x1140
+#define	AR5K_DCU_SEQNUM_M		0x00000fff
+#define	AR5K_QUEUE_DCU_SEQNUM(_q)	AR5K_QUEUE_REG(AR5K_DCU_SEQNUM_BASE, _q)
+
+/*
+ * DCU global IFS SIFS register
+ */
+#define AR5K_DCU_GBL_IFS_SIFS	0x1030
+#define AR5K_DCU_GBL_IFS_SIFS_M	0x0000ffff
+
+/*
+ * DCU global IFS slot interval register
+ */
+#define AR5K_DCU_GBL_IFS_SLOT	0x1070
+#define AR5K_DCU_GBL_IFS_SLOT_M	0x0000ffff
+
+/*
+ * DCU global IFS EIFS register
+ */
+#define AR5K_DCU_GBL_IFS_EIFS	0x10b0
+#define AR5K_DCU_GBL_IFS_EIFS_M	0x0000ffff
+
+/*
+ * DCU global IFS misc register
+ *
+ * LFSR stands for Linear Feedback Shift Register
+ * and it's used for generating pseudo-random
+ * number sequences.
+ *
+ * (If i understand corectly, random numbers are
+ * used for idle sensing -multiplied with cwmin/max etc-)
+ */
+#define AR5K_DCU_GBL_IFS_MISC			0x10f0			/* Register Address */
+#define	AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE	0x00000007	/* LFSR Slice Select */
+#define	AR5K_DCU_GBL_IFS_MISC_TURBO_MODE	0x00000008	/* Turbo mode */
+#define	AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC	0x000003f0	/* SIFS Duration mask */
+#define	AR5K_DCU_GBL_IFS_MISC_USEC_DUR		0x000ffc00	/* USEC Duration mask */
+#define	AR5K_DCU_GBL_IFS_MISC_USEC_DUR_S	10
+#define	AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY	0x00300000	/* DCU Arbiter delay mask */
+#define AR5K_DCU_GBL_IFS_MISC_SIFS_CNT_RST	0x00400000	/* SIFS cnt reset policy (?) */
+#define AR5K_DCU_GBL_IFS_MISC_AIFS_CNT_RST	0x00800000	/* AIFS cnt reset policy (?) */
+#define AR5K_DCU_GBL_IFS_MISC_RND_LFSR_SL_DIS	0x01000000	/* Disable random LFSR slice */
+
+/*
+ * DCU frame prefetch control register
+ */
+#define AR5K_DCU_FP			0x1230			/* Register Address */
+#define AR5K_DCU_FP_NOBURST_DCU_EN	0x00000001	/* Enable non-burst prefetch on DCU (?) */
+#define AR5K_DCU_FP_NOBURST_EN		0x00000010	/* Enable non-burst prefetch (?) */
+#define AR5K_DCU_FP_BURST_DCU_EN	0x00000020	/* Enable burst prefetch on DCU (?) */
+
+/*
+ * DCU transmit pause control/status register
+ */
+#define AR5K_DCU_TXP		0x1270			/* Register Address */
+#define	AR5K_DCU_TXP_M		0x000003ff	/* Tx pause mask */
+#define	AR5K_DCU_TXP_STATUS	0x00010000	/* Tx pause status */
+
+/*
+ * DCU transmit filter table 0 (32 entries)
+ * each entry contains a 32bit slice of the
+ * 128bit tx filter for each DCU (4 slices per DCU)
+ */
+#define AR5K_DCU_TX_FILTER_0_BASE	0x1038
+#define	AR5K_DCU_TX_FILTER_0(_n)	(AR5K_DCU_TX_FILTER_0_BASE + (_n * 64))
+
+/*
+ * DCU transmit filter table 1 (16 entries)
+ */
+#define AR5K_DCU_TX_FILTER_1_BASE	0x103c
+#define	AR5K_DCU_TX_FILTER_1(_n)	(AR5K_DCU_TX_FILTER_1_BASE + (_n * 64))
+
+/*
+ * DCU clear transmit filter register
+ */
+#define AR5K_DCU_TX_FILTER_CLR	0x143c
+
+/*
+ * DCU set transmit filter register
+ */
+#define AR5K_DCU_TX_FILTER_SET	0x147c
+
+/*
+ * Reset control register
+ */
+#define AR5K_RESET_CTL		0x4000			/* Register Address */
+#define AR5K_RESET_CTL_PCU	0x00000001	/* Protocol Control Unit reset */
+#define AR5K_RESET_CTL_DMA	0x00000002	/* DMA (Rx/Tx) reset [5210] */
+#define	AR5K_RESET_CTL_BASEBAND	0x00000002	/* Baseband reset [5211+] */
+#define AR5K_RESET_CTL_MAC	0x00000004	/* MAC reset (PCU+Baseband ?) [5210] */
+#define AR5K_RESET_CTL_PHY	0x00000008	/* PHY reset [5210] */
+#define AR5K_RESET_CTL_PCI	0x00000010	/* PCI Core reset (interrupts etc) */
+
+/*
+ * Sleep control register
+ */
+#define AR5K_SLEEP_CTL			0x4004			/* Register Address */
+#define AR5K_SLEEP_CTL_SLDUR		0x0000ffff	/* Sleep duration mask */
+#define AR5K_SLEEP_CTL_SLDUR_S		0
+#define AR5K_SLEEP_CTL_SLE		0x00030000	/* Sleep enable mask */
+#define AR5K_SLEEP_CTL_SLE_S		16
+#define AR5K_SLEEP_CTL_SLE_WAKE		0x00000000	/* Force chip awake */
+#define AR5K_SLEEP_CTL_SLE_SLP		0x00010000	/* Force chip sleep */
+#define AR5K_SLEEP_CTL_SLE_ALLOW	0x00020000	/* Normal sleep policy */
+#define AR5K_SLEEP_CTL_SLE_UNITS	0x00000008	/* [5211+] */
+#define AR5K_SLEEP_CTL_DUR_TIM_POL	0x00040000	/* Sleep duration timing policy */
+#define AR5K_SLEEP_CTL_DUR_WRITE_POL	0x00080000	/* Sleep duration write policy */
+#define AR5K_SLEEP_CTL_SLE_POL		0x00100000	/* Sleep policy mode */
+
+/*
+ * Interrupt pending register
+ */
+#define AR5K_INTPEND	0x4008
+#define AR5K_INTPEND_M	0x00000001
+
+/*
+ * Sleep force register
+ */
+#define AR5K_SFR	0x400c
+#define AR5K_SFR_EN	0x00000001
+
+/*
+ * PCI configuration register
+ * TODO: Fix LED stuff
+ */
+#define AR5K_PCICFG			0x4010			/* Register Address */
+#define AR5K_PCICFG_EEAE		0x00000001	/* Eeprom access enable [5210] */
+#define AR5K_PCICFG_SLEEP_CLOCK_EN	0x00000002	/* Enable sleep clock */
+#define AR5K_PCICFG_CLKRUNEN		0x00000004	/* CLKRUN enable [5211+] */
+#define AR5K_PCICFG_EESIZE		0x00000018	/* Mask for EEPROM size [5211+] */
+#define AR5K_PCICFG_EESIZE_S		3
+#define AR5K_PCICFG_EESIZE_4K		0		/* 4K */
+#define AR5K_PCICFG_EESIZE_8K		1		/* 8K */
+#define AR5K_PCICFG_EESIZE_16K		2		/* 16K */
+#define AR5K_PCICFG_EESIZE_FAIL		3		/* Failed to get size [5211+] */
+#define AR5K_PCICFG_LED			0x00000060	/* Led status [5211+] */
+#define AR5K_PCICFG_LED_NONE		0x00000000	/* Default [5211+] */
+#define AR5K_PCICFG_LED_PEND		0x00000020	/* Scan / Auth pending */
+#define AR5K_PCICFG_LED_ASSOC		0x00000040	/* Associated */
+#define	AR5K_PCICFG_BUS_SEL		0x00000380	/* Mask for "bus select" [5211+] (?) */
+#define AR5K_PCICFG_CBEFIX_DIS		0x00000400	/* Disable CBE fix */
+#define AR5K_PCICFG_SL_INTEN		0x00000800	/* Enable interrupts when asleep */
+#define AR5K_PCICFG_LED_BCTL		0x00001000	/* Led blink (?) [5210] */
+#define AR5K_PCICFG_RETRY_FIX		0x00001000	/* Enable pci core retry fix */
+#define AR5K_PCICFG_SL_INPEN		0x00002000	/* Sleep even whith pending interrupts*/
+#define AR5K_PCICFG_SPWR_DN		0x00010000	/* Mask for power status */
+#define AR5K_PCICFG_LEDMODE		0x000e0000	/* Ledmode [5211+] */
+#define AR5K_PCICFG_LEDMODE_PROP	0x00000000	/* Blink on standard traffic [5211+] */
+#define AR5K_PCICFG_LEDMODE_PROM	0x00020000	/* Default mode (blink on any traffic) [5211+] */
+#define AR5K_PCICFG_LEDMODE_PWR		0x00040000	/* Some other blinking mode  (?) [5211+] */
+#define AR5K_PCICFG_LEDMODE_RAND	0x00060000	/* Random blinking (?) [5211+] */
+#define AR5K_PCICFG_LEDBLINK		0x00700000	/* Led blink rate */
+#define AR5K_PCICFG_LEDBLINK_S		20
+#define AR5K_PCICFG_LEDSLOW		0x00800000	/* Slowest led blink rate [5211+] */
+#define AR5K_PCICFG_LEDSTATE				\
+	(AR5K_PCICFG_LED | AR5K_PCICFG_LEDMODE |	\
+	AR5K_PCICFG_LEDBLINK | AR5K_PCICFG_LEDSLOW)
+#define	AR5K_PCICFG_SLEEP_CLOCK_RATE	0x03000000	/* Sleep clock rate */
+#define	AR5K_PCICFG_SLEEP_CLOCK_RATE_S	24
+
+/*
+ * "General Purpose Input/Output" (GPIO) control register
+ *
+ * I'm not sure about this but after looking at the code
+ * for all chipsets here is what i got.
+ *
+ * We have 6 GPIOs (pins), each GPIO has 4 modes (2 bits)
+ * Mode 0 -> always input
+ * Mode 1 -> output when GPIODO for this GPIO is set to 0
+ * Mode 2 -> output when GPIODO for this GPIO is set to 1
+ * Mode 3 -> always output
+ *
+ * For more infos check out get_gpio/set_gpio and
+ * set_gpio_input/set_gpio_output functs.
+ * For more infos on gpio interrupt check out set_gpio_intr.
+ */
+#define AR5K_NUM_GPIO	6
+
+#define AR5K_GPIOCR		0x4014				/* Register Address */
+#define AR5K_GPIOCR_INT_ENA	0x00008000		/* Enable GPIO interrupt */
+#define AR5K_GPIOCR_INT_SELL	0x00000000		/* Generate interrupt when pin is low */
+#define AR5K_GPIOCR_INT_SELH	0x00010000		/* Generate interrupt when pin is high */
+#define AR5K_GPIOCR_IN(n)	(0 << ((n) * 2))	/* Mode 0 for pin n */
+#define AR5K_GPIOCR_OUT0(n)	(1 << ((n) * 2))	/* Mode 1 for pin n */
+#define AR5K_GPIOCR_OUT1(n)	(2 << ((n) * 2))	/* Mode 2 for pin n */
+#define AR5K_GPIOCR_OUT(n)	(3 << ((n) * 2))	/* Mode 3 for pin n */
+#define AR5K_GPIOCR_INT_SEL(n)	((n) << 12)		/* Interrupt for GPIO pin n */
+
+/*
+ * "General Purpose Input/Output" (GPIO) data output register
+ */
+#define AR5K_GPIODO	0x4018
+
+/*
+ * "General Purpose Input/Output" (GPIO) data input register
+ */
+#define AR5K_GPIODI	0x401c
+#define AR5K_GPIODI_M	0x0000002f
+
+/*
+ * Silicon revision register
+ */
+#define AR5K_SREV		0x4020			/* Register Address */
+#define AR5K_SREV_REV		0x0000000f	/* Mask for revision */
+#define AR5K_SREV_REV_S		0
+#define AR5K_SREV_VER		0x000000ff	/* Mask for version */
+#define AR5K_SREV_VER_S		4
+
+/*
+ * TXE write posting register
+ */
+#define	AR5K_TXEPOST	0x4028
+
+/*
+ * QCU sleep mask
+ */
+#define	AR5K_QCU_SLEEP_MASK	0x402c
+
+/* 0x4068 is compression buffer configuration
+ * register on 5414 and pm configuration register
+ * on 5424 and newer pci-e chips. */
+
+/*
+ * Compression buffer configuration
+ * register (enable/disable) [5414]
+ */
+#define AR5K_5414_CBCFG		0x4068
+#define AR5K_5414_CBCFG_BUF_DIS	0x10	/* Disable buffer */
+
+/*
+ * PCI-E Power managment configuration
+ * and status register [5424+]
+ */
+#define	AR5K_PCIE_PM_CTL		0x4068			/* Register address */
+/* Only 5424 */
+#define	AR5K_PCIE_PM_CTL_L1_WHEN_D2	0x00000001	/* enable PCIe core enter L1
+							when d2_sleep_en is asserted */
+#define	AR5K_PCIE_PM_CTL_L0_L0S_CLEAR	0x00000002	/* Clear L0 and L0S counters */
+#define	AR5K_PCIE_PM_CTL_L0_L0S_EN	0x00000004	/* Start L0 nd L0S counters */
+#define	AR5K_PCIE_PM_CTL_LDRESET_EN	0x00000008	/* Enable reset when link goes
+							down */
+/* Wake On Wireless */
+#define	AR5K_PCIE_PM_CTL_PME_EN		0x00000010	/* PME Enable */
+#define	AR5K_PCIE_PM_CTL_AUX_PWR_DET	0x00000020	/* Aux power detect */
+#define	AR5K_PCIE_PM_CTL_PME_CLEAR	0x00000040	/* Clear PME */
+#define	AR5K_PCIE_PM_CTL_PSM_D0		0x00000080
+#define	AR5K_PCIE_PM_CTL_PSM_D1		0x00000100
+#define	AR5K_PCIE_PM_CTL_PSM_D2		0x00000200
+#define	AR5K_PCIE_PM_CTL_PSM_D3		0x00000400
+
+/*
+ * PCI-E Workaround enable register
+ */
+#define	AR5K_PCIE_WAEN	0x407c
+
+/*
+ * PCI-E Serializer/Desirializer
+ * registers
+ */
+#define	AR5K_PCIE_SERDES	0x4080
+#define	AR5K_PCIE_SERDES_RESET	0x4084
+
+/*====EEPROM REGISTERS====*/
+
+/*
+ * EEPROM access registers
+ *
+ * Here we got a difference between 5210/5211-12
+ * read data register for 5210 is at 0x6800 and
+ * status register is at 0x6c00. There is also
+ * no eeprom command register on 5210 and the
+ * offsets are different.
+ *
+ * To read eeprom data for a specific offset:
+ * 5210 - enable eeprom access (AR5K_PCICFG_EEAE)
+ *        read AR5K_EEPROM_BASE +(4 * offset)
+ *        check the eeprom status register
+ *        and read eeprom data register.
+ *
+ * 5211 - write offset to AR5K_EEPROM_BASE
+ * 5212   write AR5K_EEPROM_CMD_READ on AR5K_EEPROM_CMD
+ *        check the eeprom status register
+ *        and read eeprom data register.
+ *
+ * To write eeprom data for a specific offset:
+ * 5210 - enable eeprom access (AR5K_PCICFG_EEAE)
+ *        write data to AR5K_EEPROM_BASE +(4 * offset)
+ *        check the eeprom status register
+ * 5211 - write AR5K_EEPROM_CMD_RESET on AR5K_EEPROM_CMD
+ * 5212   write offset to AR5K_EEPROM_BASE
+ *        write data to data register
+ *	  write AR5K_EEPROM_CMD_WRITE on AR5K_EEPROM_CMD
+ *        check the eeprom status register
+ *
+ * For more infos check eeprom_* functs and the ar5k.c
+ * file posted in madwifi-devel mailing list.
+ * http://sourceforge.net/mailarchive/message.php?msg_id=8966525
+ *
+ */
+#define AR5K_EEPROM_BASE	0x6000
+
+/*
+ * EEPROM data register
+ */
+#define AR5K_EEPROM_DATA_5211	0x6004
+#define AR5K_EEPROM_DATA_5210	0x6800
+#define	AR5K_EEPROM_DATA	(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211)
+
+/*
+ * EEPROM command register
+ */
+#define AR5K_EEPROM_CMD		0x6008			/* Register Addres */
+#define AR5K_EEPROM_CMD_READ	0x00000001	/* EEPROM read */
+#define AR5K_EEPROM_CMD_WRITE	0x00000002	/* EEPROM write */
+#define AR5K_EEPROM_CMD_RESET	0x00000004	/* EEPROM reset */
+
+/*
+ * EEPROM status register
+ */
+#define AR5K_EEPROM_STAT_5210	0x6c00			/* Register Address [5210] */
+#define AR5K_EEPROM_STAT_5211	0x600c			/* Register Address [5211+] */
+#define	AR5K_EEPROM_STATUS	(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211)
+#define AR5K_EEPROM_STAT_RDERR	0x00000001	/* EEPROM read failed */
+#define AR5K_EEPROM_STAT_RDDONE	0x00000002	/* EEPROM read successful */
+#define AR5K_EEPROM_STAT_WRERR	0x00000004	/* EEPROM write failed */
+#define AR5K_EEPROM_STAT_WRDONE	0x00000008	/* EEPROM write successful */
+
+/*
+ * EEPROM config register
+ */
+#define AR5K_EEPROM_CFG			0x6010			/* Register Addres */
+#define AR5K_EEPROM_CFG_SIZE		0x00000003		/* Size determination override */
+#define AR5K_EEPROM_CFG_SIZE_AUTO	0
+#define AR5K_EEPROM_CFG_SIZE_4KBIT	1
+#define AR5K_EEPROM_CFG_SIZE_8KBIT	2
+#define AR5K_EEPROM_CFG_SIZE_16KBIT	3
+#define AR5K_EEPROM_CFG_WR_WAIT_DIS	0x00000004	/* Disable write wait */
+#define AR5K_EEPROM_CFG_CLK_RATE	0x00000018	/* Clock rate */
+#define AR5K_EEPROM_CFG_CLK_RATE_S		3
+#define AR5K_EEPROM_CFG_CLK_RATE_156KHZ	0
+#define AR5K_EEPROM_CFG_CLK_RATE_312KHZ	1
+#define AR5K_EEPROM_CFG_CLK_RATE_625KHZ	2
+#define AR5K_EEPROM_CFG_PROT_KEY	0x00ffff00      /* Protection key */
+#define AR5K_EEPROM_CFG_PROT_KEY_S	8
+#define AR5K_EEPROM_CFG_LIND_EN		0x01000000	/* Enable length indicator (?) */
+
+
+/*
+ * TODO: Wake On Wireless registers
+ * Range 0x7000 - 0x7ce0
+ */
+
+/*
+ * Protocol Control Unit (PCU) registers
+ */
+/*
+ * Used for checking initial register writes
+ * during channel reset (see reset func)
+ */
+#define AR5K_PCU_MIN	0x8000
+#define AR5K_PCU_MAX	0x8fff
+
+/*
+ * First station id register (Lower 32 bits of MAC address)
+ */
+#define AR5K_STA_ID0		0x8000
+#define	AR5K_STA_ID0_ARRD_L32	0xffffffff
+
+/*
+ * Second station id register (Upper 16 bits of MAC address + PCU settings)
+ */
+#define AR5K_STA_ID1			0x8004			/* Register Address */
+#define	AR5K_STA_ID1_ADDR_U16		0x0000ffff	/* Upper 16 bits of MAC addres */
+#define AR5K_STA_ID1_AP			0x00010000	/* Set AP mode */
+#define AR5K_STA_ID1_ADHOC		0x00020000	/* Set Ad-Hoc mode */
+#define AR5K_STA_ID1_PWR_SV		0x00040000	/* Power save reporting */
+#define AR5K_STA_ID1_NO_KEYSRCH		0x00080000	/* No key search */
+#define AR5K_STA_ID1_NO_PSPOLL		0x00100000	/* No power save polling [5210] */
+#define AR5K_STA_ID1_PCF_5211		0x00100000	/* Enable PCF on [5211+] */
+#define AR5K_STA_ID1_PCF_5210		0x00200000	/* Enable PCF on [5210]*/
+#define	AR5K_STA_ID1_PCF		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_STA_ID1_PCF_5210 : AR5K_STA_ID1_PCF_5211)
+#define AR5K_STA_ID1_DEFAULT_ANTENNA	0x00200000	/* Use default antenna */
+#define AR5K_STA_ID1_DESC_ANTENNA	0x00400000	/* Update antenna from descriptor */
+#define AR5K_STA_ID1_RTS_DEF_ANTENNA	0x00800000	/* Use default antenna for RTS */
+#define AR5K_STA_ID1_ACKCTS_6MB		0x01000000	/* Use 6Mbit/s for ACK/CTS */
+#define AR5K_STA_ID1_BASE_RATE_11B	0x02000000	/* Use 11b base rate for ACK/CTS [5211+] */
+#define AR5K_STA_ID1_SELFGEN_DEF_ANT	0x04000000	/* Use def. antenna for self generated frames */
+#define AR5K_STA_ID1_CRYPT_MIC_EN	0x08000000	/* Enable MIC */
+#define AR5K_STA_ID1_KEYSRCH_MODE	0x10000000	/* Look up key when key id != 0 */
+#define AR5K_STA_ID1_PRESERVE_SEQ_NUM	0x20000000	/* Preserve sequence number */
+#define AR5K_STA_ID1_CBCIV_ENDIAN	0x40000000	/* ??? */
+#define AR5K_STA_ID1_KEYSRCH_MCAST	0x80000000	/* Do key cache search for mcast frames */
+
+/*
+ * First BSSID register (MAC address, lower 32bits)
+ */
+#define AR5K_BSS_ID0	0x8008
+
+/*
+ * Second BSSID register (MAC address in upper 16 bits)
+ *
+ * AID: Association ID
+ */
+#define AR5K_BSS_ID1		0x800c
+#define AR5K_BSS_ID1_AID	0xffff0000
+#define AR5K_BSS_ID1_AID_S	16
+
+/*
+ * Backoff slot time register
+ */
+#define AR5K_SLOT_TIME	0x8010
+
+/*
+ * ACK/CTS timeout register
+ */
+#define AR5K_TIME_OUT		0x8014			/* Register Address */
+#define AR5K_TIME_OUT_ACK	0x00001fff	/* ACK timeout mask */
+#define AR5K_TIME_OUT_ACK_S	0
+#define AR5K_TIME_OUT_CTS	0x1fff0000	/* CTS timeout mask */
+#define AR5K_TIME_OUT_CTS_S	16
+
+/*
+ * RSSI threshold register
+ */
+#define AR5K_RSSI_THR			0x8018		/* Register Address */
+#define AR5K_RSSI_THR_M			0x000000ff	/* Mask for RSSI threshold [5211+] */
+#define AR5K_RSSI_THR_BMISS_5210	0x00000700	/* Mask for Beacon Missed threshold [5210] */
+#define AR5K_RSSI_THR_BMISS_5210_S	8
+#define AR5K_RSSI_THR_BMISS_5211	0x0000ff00	/* Mask for Beacon Missed threshold [5211+] */
+#define AR5K_RSSI_THR_BMISS_5211_S	8
+#define	AR5K_RSSI_THR_BMISS		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_RSSI_THR_BMISS_5210 : AR5K_RSSI_THR_BMISS_5211)
+#define	AR5K_RSSI_THR_BMISS_S		8
+
+/*
+ * 5210 has more PCU registers because there is no QCU/DCU
+ * so queue parameters are set here, this way a lot common
+ * registers have different address for 5210. To make things
+ * easier we define a macro based on ah->ah_version for common
+ * registers with different addresses and common flags.
+ */
+
+/*
+ * Retry limit register
+ *
+ * Retry limit register for 5210 (no QCU/DCU so it's done in PCU)
+ */
+#define AR5K_NODCU_RETRY_LMT		0x801c			/* Register Address */
+#define AR5K_NODCU_RETRY_LMT_SH_RETRY	0x0000000f	/* Short retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SH_RETRY_S	0
+#define AR5K_NODCU_RETRY_LMT_LG_RETRY	0x000000f0	/* Long retry mask */
+#define AR5K_NODCU_RETRY_LMT_LG_RETRY_S	4
+#define AR5K_NODCU_RETRY_LMT_SSH_RETRY	0x00003f00	/* Station short retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SSH_RETRY_S	8
+#define AR5K_NODCU_RETRY_LMT_SLG_RETRY	0x000fc000	/* Station long retry limit mask */
+#define AR5K_NODCU_RETRY_LMT_SLG_RETRY_S	14
+#define AR5K_NODCU_RETRY_LMT_CW_MIN	0x3ff00000	/* Minimum contention window mask */
+#define AR5K_NODCU_RETRY_LMT_CW_MIN_S	20
+
+/*
+ * Transmit latency register
+ */
+#define AR5K_USEC_5210			0x8020			/* Register Address [5210] */
+#define AR5K_USEC_5211			0x801c			/* Register Address [5211+] */
+#define AR5K_USEC			(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_USEC_5210 : AR5K_USEC_5211)
+#define AR5K_USEC_1			0x0000007f	/* clock cycles for 1us */
+#define AR5K_USEC_1_S			0
+#define AR5K_USEC_32			0x00003f80	/* clock cycles for 1us while on 32Mhz clock */
+#define AR5K_USEC_32_S			7
+#define AR5K_USEC_TX_LATENCY_5211	0x007fc000
+#define AR5K_USEC_TX_LATENCY_5211_S	14
+#define AR5K_USEC_RX_LATENCY_5211	0x1f800000
+#define AR5K_USEC_RX_LATENCY_5211_S	23
+#define AR5K_USEC_TX_LATENCY_5210	0x000fc000	/* also for 5311 */
+#define AR5K_USEC_TX_LATENCY_5210_S	14
+#define AR5K_USEC_RX_LATENCY_5210	0x03f00000	/* also for 5311 */
+#define AR5K_USEC_RX_LATENCY_5210_S	20
+
+/*
+ * PCU beacon control register
+ */
+#define AR5K_BEACON_5210	0x8024			/*Register Address [5210] */
+#define AR5K_BEACON_5211	0x8020			/*Register Address [5211+] */
+#define AR5K_BEACON		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_BEACON_5210 : AR5K_BEACON_5211)
+#define AR5K_BEACON_PERIOD	0x0000ffff	/* Mask for beacon period */
+#define AR5K_BEACON_PERIOD_S	0
+#define AR5K_BEACON_TIM		0x007f0000	/* Mask for TIM offset */
+#define AR5K_BEACON_TIM_S	16
+#define AR5K_BEACON_ENABLE	0x00800000	/* Enable beacons */
+#define AR5K_BEACON_RESET_TSF	0x01000000	/* Force TSF reset */
+
+/*
+ * CFP period register
+ */
+#define AR5K_CFP_PERIOD_5210	0x8028
+#define AR5K_CFP_PERIOD_5211	0x8024
+#define AR5K_CFP_PERIOD		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_CFP_PERIOD_5210 : AR5K_CFP_PERIOD_5211)
+
+/*
+ * Next beacon time register
+ */
+#define AR5K_TIMER0_5210	0x802c
+#define AR5K_TIMER0_5211	0x8028
+#define AR5K_TIMER0		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_TIMER0_5210 : AR5K_TIMER0_5211)
+
+/*
+ * Next DMA beacon alert register
+ */
+#define AR5K_TIMER1_5210	0x8030
+#define AR5K_TIMER1_5211	0x802c
+#define AR5K_TIMER1		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_TIMER1_5210 : AR5K_TIMER1_5211)
+
+/*
+ * Next software beacon alert register
+ */
+#define AR5K_TIMER2_5210	0x8034
+#define AR5K_TIMER2_5211	0x8030
+#define AR5K_TIMER2		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_TIMER2_5210 : AR5K_TIMER2_5211)
+
+/*
+ * Next ATIM window time register
+ */
+#define AR5K_TIMER3_5210	0x8038
+#define AR5K_TIMER3_5211	0x8034
+#define AR5K_TIMER3		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_TIMER3_5210 : AR5K_TIMER3_5211)
+
+
+/*
+ * 5210 First inter frame spacing register (IFS)
+ */
+#define AR5K_IFS0		0x8040
+#define AR5K_IFS0_SIFS		0x000007ff
+#define AR5K_IFS0_SIFS_S	0
+#define AR5K_IFS0_DIFS		0x007ff800
+#define AR5K_IFS0_DIFS_S	11
+
+/*
+ * 5210 Second inter frame spacing register (IFS)
+ */
+#define AR5K_IFS1		0x8044
+#define AR5K_IFS1_PIFS		0x00000fff
+#define AR5K_IFS1_PIFS_S	0
+#define AR5K_IFS1_EIFS		0x03fff000
+#define AR5K_IFS1_EIFS_S	12
+#define AR5K_IFS1_CS_EN		0x04000000
+
+
+/*
+ * CFP duration register
+ */
+#define AR5K_CFP_DUR_5210	0x8048
+#define AR5K_CFP_DUR_5211	0x8038
+#define AR5K_CFP_DUR		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_CFP_DUR_5210 : AR5K_CFP_DUR_5211)
+
+/*
+ * Receive filter register
+ */
+#define AR5K_RX_FILTER_5210	0x804c			/* Register Address [5210] */
+#define AR5K_RX_FILTER_5211	0x803c			/* Register Address [5211+] */
+#define AR5K_RX_FILTER		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_RX_FILTER_5210 : AR5K_RX_FILTER_5211)
+#define	AR5K_RX_FILTER_UCAST 	0x00000001	/* Don't filter unicast frames */
+#define	AR5K_RX_FILTER_MCAST 	0x00000002	/* Don't filter multicast frames */
+#define	AR5K_RX_FILTER_BCAST 	0x00000004	/* Don't filter broadcast frames */
+#define	AR5K_RX_FILTER_CONTROL 	0x00000008	/* Don't filter control frames */
+#define	AR5K_RX_FILTER_BEACON 	0x00000010	/* Don't filter beacon frames */
+#define	AR5K_RX_FILTER_PROM 	0x00000020	/* Set promiscuous mode */
+#define	AR5K_RX_FILTER_XRPOLL 	0x00000040	/* Don't filter XR poll frame [5212+] */
+#define	AR5K_RX_FILTER_PROBEREQ 0x00000080	/* Don't filter probe requests [5212+] */
+#define	AR5K_RX_FILTER_PHYERR_5212	0x00000100	/* Don't filter phy errors [5212+] */
+#define	AR5K_RX_FILTER_RADARERR_5212 	0x00000200	/* Don't filter phy radar errors [5212+] */
+#define AR5K_RX_FILTER_PHYERR_5211	0x00000040	/* [5211] */
+#define AR5K_RX_FILTER_RADARERR_5211	0x00000080	/* [5211] */
+#define AR5K_RX_FILTER_PHYERR  \
+	((ah->ah_version == AR5K_AR5211 ? \
+	AR5K_RX_FILTER_PHYERR_5211 : AR5K_RX_FILTER_PHYERR_5212))
+#define        AR5K_RX_FILTER_RADARERR \
+	((ah->ah_version == AR5K_AR5211 ? \
+	AR5K_RX_FILTER_RADARERR_5211 : AR5K_RX_FILTER_RADARERR_5212))
+
+/*
+ * Multicast filter register (lower 32 bits)
+ */
+#define AR5K_MCAST_FILTER0_5210	0x8050
+#define AR5K_MCAST_FILTER0_5211	0x8040
+#define AR5K_MCAST_FILTER0	(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_MCAST_FILTER0_5210 : AR5K_MCAST_FILTER0_5211)
+
+/*
+ * Multicast filter register (higher 16 bits)
+ */
+#define AR5K_MCAST_FILTER1_5210	0x8054
+#define AR5K_MCAST_FILTER1_5211	0x8044
+#define AR5K_MCAST_FILTER1	(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_MCAST_FILTER1_5210 : AR5K_MCAST_FILTER1_5211)
+
+
+/*
+ * Transmit mask register (lower 32 bits) [5210]
+ */
+#define AR5K_TX_MASK0	0x8058
+
+/*
+ * Transmit mask register (higher 16 bits) [5210]
+ */
+#define AR5K_TX_MASK1	0x805c
+
+/*
+ * Clear transmit mask [5210]
+ */
+#define AR5K_CLR_TMASK	0x8060
+
+/*
+ * Trigger level register (before transmission) [5210]
+ */
+#define AR5K_TRIG_LVL	0x8064
+
+
+/*
+ * PCU control register
+ *
+ * Only DIS_RX is used in the code, the rest i guess are
+ * for tweaking/diagnostics.
+ */
+#define AR5K_DIAG_SW_5210		0x8068			/* Register Address [5210] */
+#define AR5K_DIAG_SW_5211		0x8048			/* Register Address [5211+] */
+#define AR5K_DIAG_SW			(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_5210 : AR5K_DIAG_SW_5211)
+#define AR5K_DIAG_SW_DIS_WEP_ACK	0x00000001	/* Disable ACKs if WEP key is invalid */
+#define AR5K_DIAG_SW_DIS_ACK		0x00000002	/* Disable ACKs */
+#define AR5K_DIAG_SW_DIS_CTS		0x00000004	/* Disable CTSs */
+#define AR5K_DIAG_SW_DIS_ENC		0x00000008	/* Disable encryption */
+#define AR5K_DIAG_SW_DIS_DEC		0x00000010	/* Disable decryption */
+#define AR5K_DIAG_SW_DIS_TX		0x00000020	/* Disable transmit [5210] */
+#define AR5K_DIAG_SW_DIS_RX_5210	0x00000040	/* Disable recieve */
+#define AR5K_DIAG_SW_DIS_RX_5211	0x00000020
+#define	AR5K_DIAG_SW_DIS_RX		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211)
+#define AR5K_DIAG_SW_LOOP_BACK_5210	0x00000080	/* Loopback (i guess it goes with DIS_TX) [5210] */
+#define AR5K_DIAG_SW_LOOP_BACK_5211	0x00000040
+#define AR5K_DIAG_SW_LOOP_BACK		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211)
+#define AR5K_DIAG_SW_CORR_FCS_5210	0x00000100	/* Corrupted FCS */
+#define AR5K_DIAG_SW_CORR_FCS_5211	0x00000080
+#define AR5K_DIAG_SW_CORR_FCS		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211)
+#define AR5K_DIAG_SW_CHAN_INFO_5210	0x00000200	/* Dump channel info */
+#define AR5K_DIAG_SW_CHAN_INFO_5211	0x00000100
+#define AR5K_DIAG_SW_CHAN_INFO		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211)
+#define AR5K_DIAG_SW_EN_SCRAM_SEED_5210	0x00000400	/* Enable fixed scrambler seed */
+#define AR5K_DIAG_SW_EN_SCRAM_SEED_5211	0x00000200
+#define AR5K_DIAG_SW_EN_SCRAM_SEED	(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_EN_SCRAM_SEED_5210 : AR5K_DIAG_SW_EN_SCRAM_SEED_5211)
+#define AR5K_DIAG_SW_ECO_ENABLE		0x00000400	/* [5211+] */
+#define AR5K_DIAG_SW_SCVRAM_SEED	0x0003f800	/* [5210] */
+#define AR5K_DIAG_SW_SCRAM_SEED_M	0x0001fc00	/* Scrambler seed mask */
+#define AR5K_DIAG_SW_SCRAM_SEED_S	10
+#define AR5K_DIAG_SW_DIS_SEQ_INC	0x00040000	/* Disable seqnum increment (?)[5210] */
+#define AR5K_DIAG_SW_FRAME_NV0_5210	0x00080000
+#define AR5K_DIAG_SW_FRAME_NV0_5211	0x00020000	/* Accept frames of non-zero protocol number */
+#define	AR5K_DIAG_SW_FRAME_NV0		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211)
+#define AR5K_DIAG_SW_OBSPT_M		0x000c0000	/* Observation point select (?) */
+#define AR5K_DIAG_SW_OBSPT_S		18
+#define AR5K_DIAG_SW_RX_CLEAR_HIGH	0x0010000	/* Force RX Clear high */
+#define AR5K_DIAG_SW_IGNORE_CARR_SENSE	0x0020000	/* Ignore virtual carrier sense */
+#define AR5K_DIAG_SW_CHANEL_IDLE_HIGH	0x0040000	/* Force channel idle high */
+#define AR5K_DIAG_SW_PHEAR_ME		0x0080000	/* ??? */
+
+/*
+ * TSF (clock) register (lower 32 bits)
+ */
+#define AR5K_TSF_L32_5210	0x806c
+#define AR5K_TSF_L32_5211	0x804c
+#define	AR5K_TSF_L32		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211)
+
+/*
+ * TSF (clock) register (higher 32 bits)
+ */
+#define AR5K_TSF_U32_5210	0x8070
+#define AR5K_TSF_U32_5211	0x8050
+#define	AR5K_TSF_U32		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211)
+
+/*
+ * Last beacon timestamp register (Read Only)
+ */
+#define AR5K_LAST_TSTP	0x8080
+
+/*
+ * ADDAC test register [5211+]
+ */
+#define AR5K_ADDAC_TEST			0x8054			/* Register Address */
+#define AR5K_ADDAC_TEST_TXCONT 		0x00000001	/* Test continuous tx */
+#define AR5K_ADDAC_TEST_TST_MODE	0x00000002	/* Test mode */
+#define AR5K_ADDAC_TEST_LOOP_EN		0x00000004	/* Enable loop */
+#define AR5K_ADDAC_TEST_LOOP_LEN	0x00000008	/* Loop length (field) */
+#define AR5K_ADDAC_TEST_USE_U8		0x00004000	/* Use upper 8 bits */
+#define AR5K_ADDAC_TEST_MSB		0x00008000	/* State of MSB */
+#define AR5K_ADDAC_TEST_TRIG_SEL	0x00010000	/* Trigger select */
+#define AR5K_ADDAC_TEST_TRIG_PTY	0x00020000	/* Trigger polarity */
+#define AR5K_ADDAC_TEST_RXCONT		0x00040000	/* Continuous capture */
+#define AR5K_ADDAC_TEST_CAPTURE		0x00080000	/* Begin capture */
+#define AR5K_ADDAC_TEST_TST_ARM		0x00100000	/* ARM rx buffer for capture */
+
+/*
+ * Default antenna register [5211+]
+ */
+#define AR5K_DEFAULT_ANTENNA	0x8058
+
+/*
+ * Frame control QoS mask register (?) [5211+]
+ * (FC_QOS_MASK)
+ */
+#define AR5K_FRAME_CTL_QOSM	0x805c
+
+/*
+ * Seq mask register (?) [5211+]
+ */
+#define AR5K_SEQ_MASK	0x8060
+
+/*
+ * Retry count register [5210]
+ */
+#define AR5K_RETRY_CNT		0x8084			/* Register Address [5210] */
+#define AR5K_RETRY_CNT_SSH	0x0000003f	/* Station short retry count (?) */
+#define AR5K_RETRY_CNT_SLG	0x00000fc0	/* Station long retry count (?) */
+
+/*
+ * Back-off status register [5210]
+ */
+#define AR5K_BACKOFF		0x8088			/* Register Address [5210] */
+#define AR5K_BACKOFF_CW		0x000003ff	/* Backoff Contention Window (?) */
+#define AR5K_BACKOFF_CNT	0x03ff0000	/* Backoff count (?) */
+
+
+
+/*
+ * NAV register (current)
+ */
+#define AR5K_NAV_5210		0x808c
+#define AR5K_NAV_5211		0x8084
+#define	AR5K_NAV		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_NAV_5210 : AR5K_NAV_5211)
+
+/*
+ * RTS success register
+ */
+#define AR5K_RTS_OK_5210	0x8090
+#define AR5K_RTS_OK_5211	0x8088
+#define	AR5K_RTS_OK		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211)
+
+/*
+ * RTS failure register
+ */
+#define AR5K_RTS_FAIL_5210	0x8094
+#define AR5K_RTS_FAIL_5211	0x808c
+#define	AR5K_RTS_FAIL		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211)
+
+/*
+ * ACK failure register
+ */
+#define AR5K_ACK_FAIL_5210	0x8098
+#define AR5K_ACK_FAIL_5211	0x8090
+#define	AR5K_ACK_FAIL		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211)
+
+/*
+ * FCS failure register
+ */
+#define AR5K_FCS_FAIL_5210	0x809c
+#define AR5K_FCS_FAIL_5211	0x8094
+#define	AR5K_FCS_FAIL		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_FCS_FAIL_5210 : AR5K_FCS_FAIL_5211)
+
+/*
+ * Beacon count register
+ */
+#define AR5K_BEACON_CNT_5210	0x80a0
+#define AR5K_BEACON_CNT_5211	0x8098
+#define	AR5K_BEACON_CNT		(ah->ah_version == AR5K_AR5210 ? \
+				AR5K_BEACON_CNT_5210 : AR5K_BEACON_CNT_5211)
+
+
+/*===5212 Specific PCU registers===*/
+
+/*
+ * Transmit power control register
+ */
+#define AR5K_TPC			0x80e8
+#define AR5K_TPC_ACK			0x0000003f	/* ack frames */
+#define AR5K_TPC_ACK_S			0
+#define AR5K_TPC_CTS			0x00003f00	/* cts frames */
+#define AR5K_TPC_CTS_S			8
+#define AR5K_TPC_CHIRP			0x003f0000	/* chirp frames */
+#define AR5K_TPC_CHIRP_S		16
+#define AR5K_TPC_DOPPLER		0x0f000000	/* doppler chirp span */
+#define AR5K_TPC_DOPPLER_S		24
+
+/*
+ * XR (eXtended Range) mode register
+ */
+#define AR5K_XRMODE			0x80c0			/* Register Address */
+#define	AR5K_XRMODE_POLL_TYPE_M		0x0000003f	/* Mask for Poll type (?) */
+#define	AR5K_XRMODE_POLL_TYPE_S		0
+#define	AR5K_XRMODE_POLL_SUBTYPE_M	0x0000003c	/* Mask for Poll subtype (?) */
+#define	AR5K_XRMODE_POLL_SUBTYPE_S	2
+#define	AR5K_XRMODE_POLL_WAIT_ALL	0x00000080	/* Wait for poll */
+#define	AR5K_XRMODE_SIFS_DELAY		0x000fff00	/* Mask for SIFS delay */
+#define	AR5K_XRMODE_FRAME_HOLD_M	0xfff00000	/* Mask for frame hold (?) */
+#define	AR5K_XRMODE_FRAME_HOLD_S	20
+
+/*
+ * XR delay register
+ */
+#define AR5K_XRDELAY			0x80c4			/* Register Address */
+#define AR5K_XRDELAY_SLOT_DELAY_M	0x0000ffff	/* Mask for slot delay */
+#define AR5K_XRDELAY_SLOT_DELAY_S	0
+#define AR5K_XRDELAY_CHIRP_DELAY_M	0xffff0000	/* Mask for CHIRP data delay */
+#define AR5K_XRDELAY_CHIRP_DELAY_S	16
+
+/*
+ * XR timeout register
+ */
+#define AR5K_XRTIMEOUT			0x80c8			/* Register Address */
+#define AR5K_XRTIMEOUT_CHIRP_M		0x0000ffff	/* Mask for CHIRP timeout */
+#define AR5K_XRTIMEOUT_CHIRP_S		0
+#define AR5K_XRTIMEOUT_POLL_M		0xffff0000	/* Mask for Poll timeout */
+#define AR5K_XRTIMEOUT_POLL_S		16
+
+/*
+ * XR chirp register
+ */
+#define AR5K_XRCHIRP			0x80cc			/* Register Address */
+#define AR5K_XRCHIRP_SEND		0x00000001	/* Send CHIRP */
+#define AR5K_XRCHIRP_GAP		0xffff0000	/* Mask for CHIRP gap (?) */
+
+/*
+ * XR stomp register
+ */
+#define AR5K_XRSTOMP			0x80d0			/* Register Address */
+#define AR5K_XRSTOMP_TX			0x00000001	/* Stomp Tx (?) */
+#define AR5K_XRSTOMP_RX			0x00000002	/* Stomp Rx (?) */
+#define AR5K_XRSTOMP_TX_RSSI		0x00000004	/* Stomp Tx RSSI (?) */
+#define AR5K_XRSTOMP_TX_BSSID		0x00000008	/* Stomp Tx BSSID (?) */
+#define AR5K_XRSTOMP_DATA		0x00000010	/* Stomp data (?)*/
+#define AR5K_XRSTOMP_RSSI_THRES		0x0000ff00	/* Mask for XR RSSI threshold */
+
+/*
+ * First enhanced sleep register
+ */
+#define AR5K_SLEEP0			0x80d4			/* Register Address */
+#define AR5K_SLEEP0_NEXT_DTIM		0x0007ffff	/* Mask for next DTIM (?) */
+#define AR5K_SLEEP0_NEXT_DTIM_S		0
+#define AR5K_SLEEP0_ASSUME_DTIM		0x00080000	/* Assume DTIM */
+#define AR5K_SLEEP0_ENH_SLEEP_EN	0x00100000	/* Enable enchanced sleep control */
+#define AR5K_SLEEP0_CABTO		0xff000000	/* Mask for CAB Time Out */
+#define AR5K_SLEEP0_CABTO_S		24
+
+/*
+ * Second enhanced sleep register
+ */
+#define AR5K_SLEEP1			0x80d8			/* Register Address */
+#define AR5K_SLEEP1_NEXT_TIM		0x0007ffff	/* Mask for next TIM (?) */
+#define AR5K_SLEEP1_NEXT_TIM_S		0
+#define AR5K_SLEEP1_BEACON_TO		0xff000000	/* Mask for Beacon Time Out */
+#define AR5K_SLEEP1_BEACON_TO_S		24
+
+/*
+ * Third enhanced sleep register
+ */
+#define AR5K_SLEEP2			0x80dc			/* Register Address */
+#define AR5K_SLEEP2_TIM_PER		0x0000ffff	/* Mask for TIM period (?) */
+#define AR5K_SLEEP2_TIM_PER_S		0
+#define AR5K_SLEEP2_DTIM_PER		0xffff0000	/* Mask for DTIM period (?) */
+#define AR5K_SLEEP2_DTIM_PER_S		16
+
+/*
+ * BSSID mask registers
+ */
+#define AR5K_BSS_IDM0			0x80e0	/* Upper bits */
+#define AR5K_BSS_IDM1			0x80e4	/* Lower bits */
+
+/*
+ * TX power control (TPC) register
+ *
+ * XXX: PCDAC steps (0.5dbm) or DBM ?
+ *
+ */
+#define AR5K_TXPC			0x80e8			/* Register Address */
+#define AR5K_TXPC_ACK_M			0x0000003f	/* ACK tx power */
+#define AR5K_TXPC_ACK_S			0
+#define AR5K_TXPC_CTS_M			0x00003f00	/* CTS tx power */
+#define AR5K_TXPC_CTS_S			8
+#define AR5K_TXPC_CHIRP_M		0x003f0000	/* CHIRP tx power */
+#define AR5K_TXPC_CHIRP_S		16
+#define AR5K_TXPC_DOPPLER		0x0f000000	/* Doppler chirp span (?) */
+#define AR5K_TXPC_DOPPLER_S		24
+
+/*
+ * Profile count registers
+ */
+#define AR5K_PROFCNT_TX			0x80ec	/* Tx count */
+#define AR5K_PROFCNT_RX			0x80f0	/* Rx count */
+#define AR5K_PROFCNT_RXCLR		0x80f4	/* Clear Rx count */
+#define AR5K_PROFCNT_CYCLE		0x80f8	/* Cycle count (?) */
+
+/*
+ * Quiet period control registers
+ */
+#define AR5K_QUIET_CTL1			0x80fc			/* Register Address */
+#define AR5K_QUIET_CTL1_NEXT_QT_TSF	0x0000ffff	/* Next quiet period TSF (TU) */
+#define AR5K_QUIET_CTL1_NEXT_QT_TSF_S	0
+#define AR5K_QUIET_CTL1_QT_EN		0x00010000	/* Enable quiet period */
+#define AR5K_QUIET_CTL1_ACK_CTS_EN	0x00020000	/* Send ACK/CTS during quiet period */
+
+#define AR5K_QUIET_CTL2			0x8100			/* Register Address */
+#define AR5K_QUIET_CTL2_QT_PER		0x0000ffff	/* Mask for quiet period periodicity */
+#define AR5K_QUIET_CTL2_QT_PER_S	0
+#define AR5K_QUIET_CTL2_QT_DUR		0xffff0000	/* Mask for quiet period duration */
+#define AR5K_QUIET_CTL2_QT_DUR_S	16
+
+/*
+ * TSF parameter register
+ */
+#define AR5K_TSF_PARM			0x8104			/* Register Address */
+#define AR5K_TSF_PARM_INC		0x000000ff	/* Mask for TSF increment */
+#define AR5K_TSF_PARM_INC_S		0
+
+/*
+ * QoS NOACK policy
+ */
+#define AR5K_QOS_NOACK			0x8108			/* Register Address */
+#define AR5K_QOS_NOACK_2BIT_VALUES	0x0000000f	/* ??? */
+#define AR5K_QOS_NOACK_2BIT_VALUES_S	0
+#define AR5K_QOS_NOACK_BIT_OFFSET	0x00000070	/* ??? */
+#define AR5K_QOS_NOACK_BIT_OFFSET_S	4
+#define AR5K_QOS_NOACK_BYTE_OFFSET	0x00000180	/* ??? */
+#define AR5K_QOS_NOACK_BYTE_OFFSET_S	7
+
+/*
+ * PHY error filter register
+ */
+#define AR5K_PHY_ERR_FIL		0x810c
+#define AR5K_PHY_ERR_FIL_RADAR		0x00000020	/* Radar signal */
+#define AR5K_PHY_ERR_FIL_OFDM		0x00020000	/* OFDM false detect (ANI) */
+#define AR5K_PHY_ERR_FIL_CCK		0x02000000	/* CCK false detect (ANI) */
+
+/*
+ * XR latency register
+ */
+#define AR5K_XRLAT_TX		0x8110
+
+/*
+ * ACK SIFS register
+ */
+#define AR5K_ACKSIFS		0x8114			/* Register Address */
+#define AR5K_ACKSIFS_INC	0x00000000	/* ACK SIFS Increment (field) */
+
+/*
+ * MIC QoS control register (?)
+ */
+#define	AR5K_MIC_QOS_CTL		0x8118			/* Register Address */
+#define	AR5K_MIC_QOS_CTL_OFF(_n)	(1 << (_n * 2))
+#define	AR5K_MIC_QOS_CTL_MQ_EN		0x00010000	/* Enable MIC QoS */
+
+/*
+ * MIC QoS select register (?)
+ */
+#define	AR5K_MIC_QOS_SEL		0x811c
+#define	AR5K_MIC_QOS_SEL_OFF(_n)	(1 << (_n * 4))
+
+/*
+ * Misc mode control register (?)
+ */
+#define	AR5K_MISC_MODE			0x8120			/* Register Address */
+#define	AR5K_MISC_MODE_FBSSID_MATCH	0x00000001	/* Force BSSID match */
+#define	AR5K_MISC_MODE_ACKSIFS_MEM	0x00000002	/* ACK SIFS memory (?) */
+#define	AR5K_MISC_MODE_COMBINED_MIC	0x00000004	/* use rx/tx MIC key */
+/* more bits */
+
+/*
+ * OFDM Filter counter
+ */
+#define	AR5K_OFDM_FIL_CNT		0x8124
+
+/*
+ * CCK Filter counter
+ */
+#define	AR5K_CCK_FIL_CNT		0x8128
+
+/*
+ * PHY Error Counters (?)
+ */
+#define	AR5K_PHYERR_CNT1		0x812c
+#define	AR5K_PHYERR_CNT1_MASK		0x8130
+
+#define	AR5K_PHYERR_CNT2		0x8134
+#define	AR5K_PHYERR_CNT2_MASK		0x8138
+
+/*
+ * TSF Threshold register (?)
+ */
+#define	AR5K_TSF_THRES			0x813c
+
+/*
+ * TODO: Wake On Wireless registers
+ * Range: 0x8147 - 0x818c
+ */
+
+/*
+ * Rate -> ACK SIFS mapping table (32 entries)
+ */
+#define	AR5K_RATE_ACKSIFS_BASE		0x8680			/* Register Address */
+#define	AR5K_RATE_ACKSIFS(_n)		(AR5K_RATE_ACKSIFS_BSE + ((_n) << 2))
+#define	AR5K_RATE_ACKSIFS_NORMAL	0x00000001	/* Normal SIFS (field) */
+#define	AR5K_RATE_ACKSIFS_TURBO		0x00000400	/* Turbo SIFS (field) */
+
+/*
+ * Rate -> duration mapping table (32 entries)
+ */
+#define AR5K_RATE_DUR_BASE		0x8700
+#define AR5K_RATE_DUR(_n)		(AR5K_RATE_DUR_BASE + ((_n) << 2))
+
+/*
+ * Rate -> db mapping table
+ * (8 entries, each one has 4 8bit fields)
+ */
+#define AR5K_RATE2DB_BASE		0x87c0
+#define AR5K_RATE2DB(_n)		(AR5K_RATE2DB_BASE + ((_n) << 2))
+
+/*
+ * db -> Rate mapping table
+ * (8 entries, each one has 4 8bit fields)
+ */
+#define AR5K_DB2RATE_BASE		0x87e0
+#define AR5K_DB2RATE(_n)		(AR5K_DB2RATE_BASE + ((_n) << 2))
+
+/*===5212 end===*/
+
+/*
+ * Key table (WEP) register
+ */
+#define AR5K_KEYTABLE_0_5210		0x9000
+#define AR5K_KEYTABLE_0_5211		0x8800
+#define AR5K_KEYTABLE_5210(_n)		(AR5K_KEYTABLE_0_5210 + ((_n) << 5))
+#define AR5K_KEYTABLE_5211(_n)		(AR5K_KEYTABLE_0_5211 + ((_n) << 5))
+#define	AR5K_KEYTABLE(_n)		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_KEYTABLE_5210(_n) : AR5K_KEYTABLE_5211(_n))
+#define AR5K_KEYTABLE_OFF(_n, x)	(AR5K_KEYTABLE(_n) + (x << 2))
+#define AR5K_KEYTABLE_TYPE(_n)		AR5K_KEYTABLE_OFF(_n, 5)
+#define AR5K_KEYTABLE_TYPE_40		0x00000000
+#define AR5K_KEYTABLE_TYPE_104		0x00000001
+#define AR5K_KEYTABLE_TYPE_128		0x00000003
+#define AR5K_KEYTABLE_TYPE_TKIP		0x00000004	/* [5212+] */
+#define AR5K_KEYTABLE_TYPE_AES		0x00000005	/* [5211+] */
+#define AR5K_KEYTABLE_TYPE_CCM		0x00000006	/* [5212+] */
+#define AR5K_KEYTABLE_TYPE_NULL		0x00000007	/* [5211+] */
+#define AR5K_KEYTABLE_ANTENNA		0x00000008	/* [5212+] */
+#define AR5K_KEYTABLE_MAC0(_n)		AR5K_KEYTABLE_OFF(_n, 6)
+#define AR5K_KEYTABLE_MAC1(_n)		AR5K_KEYTABLE_OFF(_n, 7)
+#define AR5K_KEYTABLE_VALID		0x00008000
+
+/* If key type is TKIP and MIC is enabled
+ * MIC key goes in offset entry + 64 */
+#define	AR5K_KEYTABLE_MIC_OFFSET	64
+
+/* WEP 40-bit	= 40-bit  entered key + 24 bit IV = 64-bit
+ * WEP 104-bit	= 104-bit entered key + 24-bit IV = 128-bit
+ * WEP 128-bit	= 128-bit entered key + 24 bit IV = 152-bit
+ *
+ * Some vendors have introduced bigger WEP keys to address
+ * security vulnerabilities in WEP. This includes:
+ *
+ * WEP 232-bit = 232-bit entered key + 24 bit IV = 256-bit
+ *
+ * We can expand this if we find ar5k Atheros cards with a larger
+ * key table size.
+ */
+#define AR5K_KEYTABLE_SIZE_5210		64
+#define AR5K_KEYTABLE_SIZE_5211		128
+#define	AR5K_KEYTABLE_SIZE		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211)
+
+
+/*===PHY REGISTERS===*/
+
+/*
+ * PHY registers start
+ */
+#define	AR5K_PHY_BASE			0x9800
+#define	AR5K_PHY(_n)			(AR5K_PHY_BASE + ((_n) << 2))
+
+/*
+ * TST_2 (Misc config parameters)
+ */
+#define	AR5K_PHY_TST2			0x9800			/* Register Address */
+#define AR5K_PHY_TST2_TRIG_SEL		0x00000007	/* Trigger select (?)*/
+#define AR5K_PHY_TST2_TRIG		0x00000010	/* Trigger (?) */
+#define AR5K_PHY_TST2_CBUS_MODE		0x00000060	/* Cardbus mode (?) */
+#define AR5K_PHY_TST2_CLK32		0x00000400	/* CLK_OUT is CLK32 (32Khz external) */
+#define AR5K_PHY_TST2_CHANCOR_DUMP_EN	0x00000800	/* Enable Chancor dump (?) */
+#define AR5K_PHY_TST2_EVEN_CHANCOR_DUMP	0x00001000	/* Even Chancor dump (?) */
+#define AR5K_PHY_TST2_RFSILENT_EN	0x00002000	/* Enable RFSILENT */
+#define AR5K_PHY_TST2_ALT_RFDATA	0x00004000	/* Alternate RFDATA (5-2GHz switch ?) */
+#define AR5K_PHY_TST2_MINI_OBS_EN	0x00008000	/* Enable mini OBS (?) */
+#define AR5K_PHY_TST2_RX2_IS_RX5_INV	0x00010000	/* 2GHz rx path is the 5GHz path inverted (?) */
+#define AR5K_PHY_TST2_SLOW_CLK160	0x00020000	/* Slow CLK160 (?) */
+#define AR5K_PHY_TST2_AGC_OBS_SEL_3	0x00040000	/* AGC OBS Select 3 (?) */
+#define AR5K_PHY_TST2_BBB_OBS_SEL	0x00080000	/* BB OBS Select (field ?) */
+#define AR5K_PHY_TST2_ADC_OBS_SEL	0x00800000	/* ADC OBS Select (field ?) */
+#define AR5K_PHY_TST2_RX_CLR_SEL	0x08000000	/* RX Clear Select (?) */
+#define AR5K_PHY_TST2_FORCE_AGC_CLR	0x10000000	/* Force AGC clear (?) */
+#define AR5K_PHY_SHIFT_2GHZ		0x00004007	/* Used to access 2GHz radios */
+#define AR5K_PHY_SHIFT_5GHZ		0x00000007	/* Used to access 5GHz radios (default) */
+
+/*
+ * PHY frame control register [5110] /turbo mode register [5111+]
+ *
+ * There is another frame control register for [5111+]
+ * at address 0x9944 (see below) but the 2 first flags
+ * are common here between 5110 frame control register
+ * and [5111+] turbo mode register, so this also works as
+ * a "turbo mode register" for 5110. We treat this one as
+ * a frame control register for 5110 below.
+ */
+#define	AR5K_PHY_TURBO			0x9804			/* Register Address */
+#define	AR5K_PHY_TURBO_MODE		0x00000001	/* Enable turbo mode */
+#define	AR5K_PHY_TURBO_SHORT		0x00000002	/* Set short symbols to turbo mode */
+#define	AR5K_PHY_TURBO_MIMO		0x00000004	/* Set turbo for mimo mimo */
+
+/*
+ * PHY agility command register
+ * (aka TST_1)
+ */
+#define	AR5K_PHY_AGC			0x9808			/* Register Address */
+#define	AR5K_PHY_TST1			0x9808
+#define	AR5K_PHY_AGC_DISABLE		0x08000000	/* Disable AGC to A2 (?)*/
+#define	AR5K_PHY_TST1_TXHOLD		0x00003800	/* Set tx hold (?) */
+#define	AR5K_PHY_TST1_TXSRC_SRC		0x00000002	/* Used with bit 7 (?) */
+#define	AR5K_PHY_TST1_TXSRC_SRC_S	1
+#define	AR5K_PHY_TST1_TXSRC_ALT		0x00000080	/* Set input to tsdac (?) */
+#define	AR5K_PHY_TST1_TXSRC_ALT_S	7
+
+
+/*
+ * PHY timing register 3 [5112+]
+ */
+#define	AR5K_PHY_TIMING_3		0x9814
+#define	AR5K_PHY_TIMING_3_DSC_MAN	0xfffe0000
+#define	AR5K_PHY_TIMING_3_DSC_MAN_S	17
+#define	AR5K_PHY_TIMING_3_DSC_EXP	0x0001e000
+#define	AR5K_PHY_TIMING_3_DSC_EXP_S	13
+
+/*
+ * PHY chip revision register
+ */
+#define	AR5K_PHY_CHIP_ID		0x9818
+
+/*
+ * PHY activation register
+ */
+#define	AR5K_PHY_ACT			0x981c			/* Register Address */
+#define	AR5K_PHY_ACT_ENABLE		0x00000001	/* Activate PHY */
+#define	AR5K_PHY_ACT_DISABLE		0x00000002	/* Deactivate PHY */
+
+/*
+ * PHY RF control registers
+ */
+#define AR5K_PHY_RF_CTL2		0x9824			/* Register Address */
+#define	AR5K_PHY_RF_CTL2_TXF2TXD_START	0x0000000f	/* TX frame to TX data start */
+#define	AR5K_PHY_RF_CTL2_TXF2TXD_START_S	0
+
+#define AR5K_PHY_RF_CTL3		0x9828			/* Register Address */
+#define AR5K_PHY_RF_CTL3_TXE2XLNA_ON	0x0000ff00	/* TX end to XLNA on */
+#define	AR5K_PHY_RF_CTL3_TXE2XLNA_ON_S	8
+
+#define	AR5K_PHY_ADC_CTL			0x982c
+#define	AR5K_PHY_ADC_CTL_INBUFGAIN_OFF		0x00000003
+#define	AR5K_PHY_ADC_CTL_INBUFGAIN_OFF_S	0
+#define	AR5K_PHY_ADC_CTL_PWD_DAC_OFF		0x00002000
+#define	AR5K_PHY_ADC_CTL_PWD_BAND_GAP_OFF	0x00004000
+#define	AR5K_PHY_ADC_CTL_PWD_ADC_OFF		0x00008000
+#define	AR5K_PHY_ADC_CTL_INBUFGAIN_ON		0x00030000
+#define	AR5K_PHY_ADC_CTL_INBUFGAIN_ON_S		16
+
+#define AR5K_PHY_RF_CTL4		0x9834			/* Register Address */
+#define AR5K_PHY_RF_CTL4_TXF2XPA_A_ON	0x00000001	/* TX frame to XPA A on (field) */
+#define AR5K_PHY_RF_CTL4_TXF2XPA_B_ON	0x00000100	/* TX frame to XPA B on (field) */
+#define	AR5K_PHY_RF_CTL4_TXE2XPA_A_OFF	0x00010000	/* TX end to XPA A off (field) */
+#define AR5K_PHY_RF_CTL4_TXE2XPA_B_OFF	0x01000000	/* TX end to XPA B off (field) */
+
+/*
+ * Pre-Amplifier control register
+ * (XPA -> external pre-amplifier)
+ */
+#define	AR5K_PHY_PA_CTL			0x9838			/* Register Address */
+#define	AR5K_PHY_PA_CTL_XPA_A_HI	0x00000001	/* XPA A high (?) */
+#define	AR5K_PHY_PA_CTL_XPA_B_HI	0x00000002	/* XPA B high (?) */
+#define	AR5K_PHY_PA_CTL_XPA_A_EN	0x00000004	/* Enable XPA A */
+#define	AR5K_PHY_PA_CTL_XPA_B_EN	0x00000008	/* Enable XPA B */
+
+/*
+ * PHY settling register
+ */
+#define AR5K_PHY_SETTLING		0x9844			/* Register Address */
+#define	AR5K_PHY_SETTLING_AGC		0x0000007f	/* AGC settling time */
+#define	AR5K_PHY_SETTLING_AGC_S		0
+#define	AR5K_PHY_SETTLING_SWITCH	0x00003f80	/* Switch settlig time */
+#define	AR5K_PHY_SETTLING_SWITCH_S	7
+
+/*
+ * PHY Gain registers
+ */
+#define AR5K_PHY_GAIN			0x9848			/* Register Address */
+#define	AR5K_PHY_GAIN_TXRX_ATTEN	0x0003f000	/* TX-RX Attenuation */
+#define	AR5K_PHY_GAIN_TXRX_ATTEN_S	12
+#define	AR5K_PHY_GAIN_TXRX_RF_MAX	0x007c0000
+#define	AR5K_PHY_GAIN_TXRX_RF_MAX_S	18
+
+#define	AR5K_PHY_GAIN_OFFSET		0x984c			/* Register Address */
+#define	AR5K_PHY_GAIN_OFFSET_RXTX_FLAG	0x00020000	/* RX-TX flag (?) */
+
+/*
+ * Desired ADC/PGA size register
+ * (for more infos read ANI patent)
+ */
+#define AR5K_PHY_DESIRED_SIZE		0x9850			/* Register Address */
+#define	AR5K_PHY_DESIRED_SIZE_ADC	0x000000ff	/* ADC desired size */
+#define	AR5K_PHY_DESIRED_SIZE_ADC_S	0
+#define	AR5K_PHY_DESIRED_SIZE_PGA	0x0000ff00	/* PGA desired size */
+#define	AR5K_PHY_DESIRED_SIZE_PGA_S	8
+#define	AR5K_PHY_DESIRED_SIZE_TOT	0x0ff00000	/* Total desired size */
+#define	AR5K_PHY_DESIRED_SIZE_TOT_S	20
+
+/*
+ * PHY signal register
+ * (for more infos read ANI patent)
+ */
+#define	AR5K_PHY_SIG			0x9858			/* Register Address */
+#define	AR5K_PHY_SIG_FIRSTEP		0x0003f000	/* FIRSTEP */
+#define	AR5K_PHY_SIG_FIRSTEP_S		12
+#define	AR5K_PHY_SIG_FIRPWR		0x03fc0000	/* FIPWR */
+#define	AR5K_PHY_SIG_FIRPWR_S		18
+
+/*
+ * PHY coarse agility control register
+ * (for more infos read ANI patent)
+ */
+#define	AR5K_PHY_AGCCOARSE		0x985c			/* Register Address */
+#define	AR5K_PHY_AGCCOARSE_LO		0x00007f80	/* AGC Coarse low */
+#define	AR5K_PHY_AGCCOARSE_LO_S		7
+#define	AR5K_PHY_AGCCOARSE_HI		0x003f8000	/* AGC Coarse high */
+#define	AR5K_PHY_AGCCOARSE_HI_S		15
+
+/*
+ * PHY agility control register
+ */
+#define	AR5K_PHY_AGCCTL			0x9860			/* Register address */
+#define	AR5K_PHY_AGCCTL_CAL		0x00000001	/* Enable PHY calibration */
+#define	AR5K_PHY_AGCCTL_NF		0x00000002	/* Enable Noise Floor calibration */
+#define	AR5K_PHY_AGCCTL_NF_EN		0x00008000	/* Enable nf calibration to happen (?) */
+#define	AR5K_PHY_AGCCTL_NF_NOUPDATE	0x00020000	/* Don't update nf automaticaly */
+
+/*
+ * PHY noise floor status register
+ */
+#define AR5K_PHY_NF			0x9864			/* Register address */
+#define AR5K_PHY_NF_M			0x000001ff	/* Noise floor mask */
+#define AR5K_PHY_NF_ACTIVE		0x00000100	/* Noise floor calibration still active */
+#define AR5K_PHY_NF_RVAL(_n)		(((_n) >> 19) & AR5K_PHY_NF_M)
+#define AR5K_PHY_NF_AVAL(_n)		(-((_n) ^ AR5K_PHY_NF_M) + 1)
+#define AR5K_PHY_NF_SVAL(_n)		(((_n) & AR5K_PHY_NF_M) | (1 << 9))
+#define	AR5K_PHY_NF_THRESH62		0x0007f000	/* Thresh62 -check ANI patent- (field) */
+#define	AR5K_PHY_NF_THRESH62_S		12
+#define	AR5K_PHY_NF_MINCCA_PWR		0x0ff80000	/* ??? */
+#define	AR5K_PHY_NF_MINCCA_PWR_S	19
+
+/*
+ * PHY ADC saturation register [5110]
+ */
+#define	AR5K_PHY_ADCSAT			0x9868
+#define	AR5K_PHY_ADCSAT_ICNT		0x0001f800
+#define	AR5K_PHY_ADCSAT_ICNT_S		11
+#define	AR5K_PHY_ADCSAT_THR		0x000007e0
+#define	AR5K_PHY_ADCSAT_THR_S		5
+
+/*
+ * PHY Weak ofdm signal detection threshold registers (ANI) [5212+]
+ */
+
+/* High thresholds */
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR		0x9868
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT	0x0000001f
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT_S	0
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1		0x00fe0000
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1_S	17
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2		0x7f000000
+#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_S	24
+
+/* Low thresholds */
+#define AR5K_PHY_WEAK_OFDM_LOW_THR 		0x986c
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN	0x00000001
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT	0x00003f00
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT_S	8
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M1		0x001fc000
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M1_S		14
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2		0x0fe00000
+#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_S		21
+
+
+/*
+ * PHY sleep registers [5112+]
+ */
+#define AR5K_PHY_SCR			0x9870
+
+#define AR5K_PHY_SLMT			0x9874
+#define AR5K_PHY_SLMT_32MHZ		0x0000007f
+
+#define AR5K_PHY_SCAL			0x9878
+#define AR5K_PHY_SCAL_32MHZ		0x0000000e
+#define	AR5K_PHY_SCAL_32MHZ_2417	0x0000000a
+#define	AR5K_PHY_SCAL_32MHZ_HB63	0x00000032
+
+/*
+ * PHY PLL (Phase Locked Loop) control register
+ */
+#define	AR5K_PHY_PLL			0x987c
+#define	AR5K_PHY_PLL_20MHZ		0x00000013	/* For half rate (?) */
+/* 40MHz -> 5GHz band */
+#define	AR5K_PHY_PLL_40MHZ_5211		0x00000018
+#define	AR5K_PHY_PLL_40MHZ_5212		0x000000aa
+#define	AR5K_PHY_PLL_40MHZ_5413		0x00000004
+#define	AR5K_PHY_PLL_40MHZ		(ah->ah_version == AR5K_AR5211 ? \
+					AR5K_PHY_PLL_40MHZ_5211 : AR5K_PHY_PLL_40MHZ_5212)
+/* 44MHz -> 2.4GHz band */
+#define	AR5K_PHY_PLL_44MHZ_5211		0x00000019
+#define	AR5K_PHY_PLL_44MHZ_5212		0x000000ab
+#define	AR5K_PHY_PLL_44MHZ		(ah->ah_version == AR5K_AR5211 ? \
+					AR5K_PHY_PLL_44MHZ_5211 : AR5K_PHY_PLL_44MHZ_5212)
+
+#define AR5K_PHY_PLL_RF5111		0x00000000
+#define AR5K_PHY_PLL_RF5112		0x00000040
+#define	AR5K_PHY_PLL_HALF_RATE		0x00000100
+#define	AR5K_PHY_PLL_QUARTER_RATE	0x00000200
+
+/*
+ * RF Buffer register
+ *
+ * It's obvious from the code that 0x989c is the buffer register but
+ * for the other special registers that we write to after sending each
+ * packet, i have no idea. So i'll name them BUFFER_CONTROL_X registers
+ * for now. It's interesting that they are also used for some other operations.
+ */
+
+#define AR5K_RF_BUFFER			0x989c
+#define AR5K_RF_BUFFER_CONTROL_0	0x98c0	/* Channel on 5110 */
+#define AR5K_RF_BUFFER_CONTROL_1	0x98c4	/* Bank 7 on 5112 */
+#define AR5K_RF_BUFFER_CONTROL_2	0x98cc	/* Bank 7 on 5111 */
+
+#define AR5K_RF_BUFFER_CONTROL_3	0x98d0	/* Bank 2 on 5112 */
+						/* Channel set on 5111 */
+						/* Used to read radio revision*/
+
+#define AR5K_RF_BUFFER_CONTROL_4	0x98d4  /* RF Stage register on 5110 */
+						/* Bank 0,1,2,6 on 5111 */
+						/* Bank 1 on 5112 */
+						/* Used during activation on 5111 */
+
+#define AR5K_RF_BUFFER_CONTROL_5	0x98d8	/* Bank 3 on 5111 */
+						/* Used during activation on 5111 */
+						/* Channel on 5112 */
+						/* Bank 6 on 5112 */
+
+#define AR5K_RF_BUFFER_CONTROL_6	0x98dc	/* Bank 3 on 5112 */
+
+/*
+ * PHY RF stage register [5210]
+ */
+#define AR5K_PHY_RFSTG			0x98d4
+#define AR5K_PHY_RFSTG_DISABLE		0x00000021
+
+/*
+ * BIN masks (?)
+ */
+#define	AR5K_PHY_BIN_MASK_1	0x9900
+#define	AR5K_PHY_BIN_MASK_2	0x9904
+#define	AR5K_PHY_BIN_MASK_3	0x9908
+
+#define	AR5K_PHY_BIN_MASK_CTL		0x990c
+#define	AR5K_PHY_BIN_MASK_CTL_MASK_4	0x00003fff
+#define	AR5K_PHY_BIN_MASK_CTL_MASK_4_S	0
+#define	AR5K_PHY_BIN_MASK_CTL_RATE	0xff000000
+#define	AR5K_PHY_BIN_MASK_CTL_RATE_S	24
+
+/*
+ * PHY Antenna control register
+ */
+#define AR5K_PHY_ANT_CTL		0x9910			/* Register Address */
+#define	AR5K_PHY_ANT_CTL_TXRX_EN	0x00000001	/* Enable TX/RX (?) */
+#define	AR5K_PHY_ANT_CTL_SECTORED_ANT	0x00000004	/* Sectored Antenna */
+#define	AR5K_PHY_ANT_CTL_HITUNE5	0x00000008	/* Hitune5 (?) */
+#define	AR5K_PHY_ANT_CTL_SWTABLE_IDLE	0x000003f0	/* Switch table idle (?) */
+#define	AR5K_PHY_ANT_CTL_SWTABLE_IDLE_S	4
+
+/*
+ * PHY receiver delay register [5111+]
+ */
+#define	AR5K_PHY_RX_DELAY		0x9914			/* Register Address */
+#define	AR5K_PHY_RX_DELAY_M		0x00003fff	/* Mask for RX activate to receive delay (/100ns) */
+
+/*
+ * PHY max rx length register (?) [5111]
+ */
+#define	AR5K_PHY_MAX_RX_LEN		0x991c
+
+/*
+ * PHY timing register 4
+ * I(nphase)/Q(adrature) calibration register [5111+]
+ */
+#define	AR5K_PHY_IQ			0x9920			/* Register Address */
+#define	AR5K_PHY_IQ_CORR_Q_Q_COFF	0x0000001f	/* Mask for q correction info */
+#define	AR5K_PHY_IQ_CORR_Q_I_COFF	0x000007e0	/* Mask for i correction info */
+#define	AR5K_PHY_IQ_CORR_Q_I_COFF_S	5
+#define	AR5K_PHY_IQ_CORR_ENABLE		0x00000800	/* Enable i/q correction */
+#define	AR5K_PHY_IQ_CAL_NUM_LOG_MAX	0x0000f000	/* Mask for max number of samples in log scale */
+#define	AR5K_PHY_IQ_CAL_NUM_LOG_MAX_S	12
+#define	AR5K_PHY_IQ_RUN			0x00010000	/* Run i/q calibration */
+#define	AR5K_PHY_IQ_USE_PT_DF		0x00020000	/* Use pilot track df (?) */
+#define	AR5K_PHY_IQ_EARLY_TRIG_THR	0x00200000	/* Early trigger threshold (?) (field) */
+#define	AR5K_PHY_IQ_PILOT_MASK_EN	0x10000000	/* Enable pilot mask (?) */
+#define	AR5K_PHY_IQ_CHAN_MASK_EN	0x20000000	/* Enable channel mask (?) */
+#define	AR5K_PHY_IQ_SPUR_FILT_EN	0x40000000	/* Enable spur filter */
+#define	AR5K_PHY_IQ_SPUR_RSSI_EN	0x80000000	/* Enable spur rssi */
+
+/*
+ * PHY timing register 5
+ * OFDM Self-correlator Cyclic RSSI threshold params
+ * (Check out bb_cycpwr_thr1 on ANI patent)
+ */
+#define	AR5K_PHY_OFDM_SELFCORR			0x9924			/* Register Address */
+#define	AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_EN	0x00000001	/* Enable cyclic RSSI thr 1 */
+#define	AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1	0x000000fe	/* Mask for Cyclic RSSI threshold 1 */
+#define	AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_S	1
+#define	AR5K_PHY_OFDM_SELFCORR_CYPWR_THR3	0x00000100	/* Cyclic RSSI threshold 3 (field) (?) */
+#define	AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR_EN	0x00008000	/* Enable 1A RSSI threshold (?) */
+#define	AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR	0x00010000	/* 1A RSSI threshold (field) (?) */
+#define	AR5K_PHY_OFDM_SELFCORR_LSCTHR_HIRSSI	0x00800000	/* Long sc threshold hi rssi (?) */
+
+/*
+ * PHY-only warm reset register
+ */
+#define	AR5K_PHY_WARM_RESET		0x9928
+
+/*
+ * PHY-only control register
+ */
+#define AR5K_PHY_CTL			0x992c			/* Register Address */
+#define	AR5K_PHY_CTL_RX_DRAIN_RATE	0x00000001	/* RX drain rate (?) */
+#define	AR5K_PHY_CTL_LATE_TX_SIG_SYM	0x00000002	/* Late tx signal symbol (?) */
+#define	AR5K_PHY_CTL_GEN_SCRAMBLER	0x00000004	/* Generate scrambler */
+#define	AR5K_PHY_CTL_TX_ANT_SEL		0x00000008	/* TX antenna select */
+#define	AR5K_PHY_CTL_TX_ANT_STATIC	0x00000010	/* Static TX antenna */
+#define	AR5K_PHY_CTL_RX_ANT_SEL		0x00000020	/* RX antenna select */
+#define	AR5K_PHY_CTL_RX_ANT_STATIC	0x00000040	/* Static RX antenna */
+#define	AR5K_PHY_CTL_LOW_FREQ_SLE_EN	0x00000080	/* Enable low freq sleep */
+
+/*
+ * PHY PAPD probe register [5111+]
+ */
+#define	AR5K_PHY_PAPD_PROBE		0x9930
+#define	AR5K_PHY_PAPD_PROBE_SH_HI_PAR	0x00000001
+#define	AR5K_PHY_PAPD_PROBE_PCDAC_BIAS	0x00000002
+#define	AR5K_PHY_PAPD_PROBE_COMP_GAIN	0x00000040
+#define	AR5K_PHY_PAPD_PROBE_TXPOWER	0x00007e00
+#define	AR5K_PHY_PAPD_PROBE_TXPOWER_S	9
+#define	AR5K_PHY_PAPD_PROBE_TX_NEXT	0x00008000
+#define	AR5K_PHY_PAPD_PROBE_PREDIST_EN	0x00010000
+#define	AR5K_PHY_PAPD_PROBE_TYPE	0x01800000	/* [5112+] */
+#define	AR5K_PHY_PAPD_PROBE_TYPE_S	23
+#define	AR5K_PHY_PAPD_PROBE_TYPE_OFDM	0
+#define	AR5K_PHY_PAPD_PROBE_TYPE_XR	1
+#define	AR5K_PHY_PAPD_PROBE_TYPE_CCK	2
+#define	AR5K_PHY_PAPD_PROBE_GAINF	0xfe000000
+#define	AR5K_PHY_PAPD_PROBE_GAINF_S	25
+#define	AR5K_PHY_PAPD_PROBE_INI_5111	0x00004883	/* [5212+] */
+#define	AR5K_PHY_PAPD_PROBE_INI_5112	0x00004882	/* [5212+] */
+
+/*
+ * PHY TX rate power registers [5112+]
+ */
+#define	AR5K_PHY_TXPOWER_RATE1			0x9934
+#define	AR5K_PHY_TXPOWER_RATE2			0x9938
+#define	AR5K_PHY_TXPOWER_RATE_MAX		0x993c
+#define	AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE	0x00000040
+#define	AR5K_PHY_TXPOWER_RATE3			0xa234
+#define	AR5K_PHY_TXPOWER_RATE4			0xa238
+
+/*
+ * PHY frame control register [5111+]
+ */
+#define	AR5K_PHY_FRAME_CTL_5210		0x9804
+#define	AR5K_PHY_FRAME_CTL_5211		0x9944
+#define	AR5K_PHY_FRAME_CTL		(ah->ah_version == AR5K_AR5210 ? \
+					AR5K_PHY_FRAME_CTL_5210 : AR5K_PHY_FRAME_CTL_5211)
+/*---[5111+]---*/
+#define	AR5K_PHY_FRAME_CTL_TX_CLIP	0x00000038	/* Mask for tx clip (?) */
+#define	AR5K_PHY_FRAME_CTL_TX_CLIP_S	3
+#define	AR5K_PHY_FRAME_CTL_PREP_CHINFO	0x00010000	/* Prepend chan info */
+#define	AR5K_PHY_FRAME_CTL_EMU		0x80000000
+#define	AR5K_PHY_FRAME_CTL_EMU_S	31
+/*---[5110/5111]---*/
+#define	AR5K_PHY_FRAME_CTL_TIMING_ERR	0x01000000	/* PHY timing error */
+#define	AR5K_PHY_FRAME_CTL_PARITY_ERR	0x02000000	/* Parity error */
+#define	AR5K_PHY_FRAME_CTL_ILLRATE_ERR	0x04000000	/* Illegal rate */
+#define	AR5K_PHY_FRAME_CTL_ILLLEN_ERR	0x08000000	/* Illegal length */
+#define	AR5K_PHY_FRAME_CTL_SERVICE_ERR	0x20000000
+#define	AR5K_PHY_FRAME_CTL_TXURN_ERR	0x40000000	/* TX underrun */
+#define AR5K_PHY_FRAME_CTL_INI		AR5K_PHY_FRAME_CTL_SERVICE_ERR | \
+			AR5K_PHY_FRAME_CTL_TXURN_ERR | \
+			AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \
+			AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \
+			AR5K_PHY_FRAME_CTL_PARITY_ERR | \
+			AR5K_PHY_FRAME_CTL_TIMING_ERR
+
+/*
+ * PHY Tx Power adjustment register [5212A+]
+ */
+#define	AR5K_PHY_TX_PWR_ADJ			0x994c
+#define	AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA	0x00000fc0
+#define	AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA_S	6
+#define	AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX	0x00fc0000
+#define	AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX_S	18
+
+/*
+ * PHY radar detection register [5111+]
+ */
+#define	AR5K_PHY_RADAR			0x9954
+#define	AR5K_PHY_RADAR_ENABLE		0x00000001
+#define	AR5K_PHY_RADAR_DISABLE		0x00000000
+#define AR5K_PHY_RADAR_INBANDTHR    	0x0000003e	/* Inband threshold
+							5-bits, units unknown {0..31}
+							(? MHz ?) */
+#define AR5K_PHY_RADAR_INBANDTHR_S	1
+
+#define AR5K_PHY_RADAR_PRSSI_THR    	0x00000fc0	/* Pulse RSSI/SNR threshold
+							6-bits, dBm range {0..63}
+							in dBm units. */
+#define AR5K_PHY_RADAR_PRSSI_THR_S	6
+
+#define AR5K_PHY_RADAR_PHEIGHT_THR   	0x0003f000	/* Pulse height threshold
+							6-bits, dBm range {0..63}
+							in dBm units. */
+#define AR5K_PHY_RADAR_PHEIGHT_THR_S	12
+
+#define AR5K_PHY_RADAR_RSSI_THR    	0x00fc0000	/* Radar RSSI/SNR threshold.
+							6-bits, dBm range {0..63}
+							in dBm units. */
+#define AR5K_PHY_RADAR_RSSI_THR_S	18
+
+#define AR5K_PHY_RADAR_FIRPWR_THR	0x7f000000	/* Finite Impulse Response
+							filter power out threshold.
+							7-bits, standard power range
+							{0..127} in 1/2 dBm units. */
+#define AR5K_PHY_RADAR_FIRPWR_THRS	24
+
+/*
+ * PHY antenna switch table registers
+ */
+#define AR5K_PHY_ANT_SWITCH_TABLE_0	0x9960
+#define AR5K_PHY_ANT_SWITCH_TABLE_1	0x9964
+
+/*
+ * PHY Noise floor threshold
+ */
+#define AR5K_PHY_NFTHRES		0x9968
+
+/*
+ * Sigma Delta register (?) [5213]
+ */
+#define AR5K_PHY_SIGMA_DELTA		0x996C
+#define AR5K_PHY_SIGMA_DELTA_ADC_SEL	0x00000003
+#define AR5K_PHY_SIGMA_DELTA_ADC_SEL_S	0
+#define AR5K_PHY_SIGMA_DELTA_FILT2	0x000000f8
+#define AR5K_PHY_SIGMA_DELTA_FILT2_S	3
+#define AR5K_PHY_SIGMA_DELTA_FILT1	0x00001f00
+#define AR5K_PHY_SIGMA_DELTA_FILT1_S	8
+#define AR5K_PHY_SIGMA_DELTA_ADC_CLIP	0x01ffe000
+#define AR5K_PHY_SIGMA_DELTA_ADC_CLIP_S	13
+
+/*
+ * RF restart register [5112+] (?)
+ */
+#define AR5K_PHY_RESTART		0x9970		/* restart */
+#define AR5K_PHY_RESTART_DIV_GC		0x001c0000	/* Fast diversity gc_limit (?) */
+#define AR5K_PHY_RESTART_DIV_GC_S	18
+
+/*
+ * RF Bus access request register (for synth-oly channel switching)
+ */
+#define AR5K_PHY_RFBUS_REQ		0x997C
+#define AR5K_PHY_RFBUS_REQ_REQUEST	0x00000001
+
+/*
+ * Spur mitigation masks (?)
+ */
+#define AR5K_PHY_TIMING_7		0x9980
+#define AR5K_PHY_TIMING_8		0x9984
+#define AR5K_PHY_TIMING_8_PILOT_MASK_2		0x000fffff
+#define AR5K_PHY_TIMING_8_PILOT_MASK_2_S	0
+
+#define AR5K_PHY_BIN_MASK2_1		0x9988
+#define AR5K_PHY_BIN_MASK2_2		0x998c
+#define AR5K_PHY_BIN_MASK2_3		0x9990
+
+#define AR5K_PHY_BIN_MASK2_4		0x9994
+#define AR5K_PHY_BIN_MASK2_4_MASK_4	0x00003fff
+#define AR5K_PHY_BIN_MASK2_4_MASK_4_S	0
+
+#define AR5K_PHY_TIMING_9			0x9998
+#define AR5K_PHY_TIMING_10			0x999c
+#define AR5K_PHY_TIMING_10_PILOT_MASK_2		0x000fffff
+#define AR5K_PHY_TIMING_10_PILOT_MASK_2_S	0
+
+/*
+ * Spur mitigation control
+ */
+#define AR5K_PHY_TIMING_11			0x99a0		/* Register address */
+#define AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE	0x000fffff	/* Spur delta phase */
+#define AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE_S	0
+#define AR5K_PHY_TIMING_11_SPUR_FREQ_SD		0x3ff00000	/* Freq sigma delta */
+#define AR5K_PHY_TIMING_11_SPUR_FREQ_SD_S	20
+#define AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC	0x40000000	/* Spur filter in AGC detector */
+#define AR5K_PHY_TIMING_11_USE_SPUR_IN_SELFCOR	0x80000000	/* Spur filter in OFDM self correlator */
+
+/*
+ * Gain tables
+ */
+#define	AR5K_BB_GAIN_BASE		0x9b00	/* BaseBand Amplifier Gain table base address */
+#define AR5K_BB_GAIN(_n)		(AR5K_BB_GAIN_BASE + ((_n) << 2))
+#define	AR5K_RF_GAIN_BASE		0x9a00	/* RF Amplrifier Gain table base address */
+#define AR5K_RF_GAIN(_n)		(AR5K_RF_GAIN_BASE + ((_n) << 2))
+
+/*
+ * PHY timing IQ calibration result register [5111+]
+ */
+#define	AR5K_PHY_IQRES_CAL_PWR_I	0x9c10	/* I (Inphase) power value */
+#define	AR5K_PHY_IQRES_CAL_PWR_Q	0x9c14	/* Q (Quadrature) power value */
+#define	AR5K_PHY_IQRES_CAL_CORR		0x9c18	/* I/Q Correlation */
+
+/*
+ * PHY current RSSI register [5111+]
+ */
+#define	AR5K_PHY_CURRENT_RSSI	0x9c1c
+
+/*
+ * PHY RF Bus grant register
+ */
+#define	AR5K_PHY_RFBUS_GRANT	0x9c20
+#define	AR5K_PHY_RFBUS_GRANT_OK	0x00000001
+
+/*
+ * PHY ADC test register
+ */
+#define	AR5K_PHY_ADC_TEST	0x9c24
+#define	AR5K_PHY_ADC_TEST_I	0x00000001
+#define	AR5K_PHY_ADC_TEST_Q	0x00000200
+
+/*
+ * PHY DAC test register
+ */
+#define	AR5K_PHY_DAC_TEST	0x9c28
+#define	AR5K_PHY_DAC_TEST_I	0x00000001
+#define	AR5K_PHY_DAC_TEST_Q	0x00000200
+
+/*
+ * PHY PTAT register (?)
+ */
+#define	AR5K_PHY_PTAT		0x9c2c
+
+/*
+ * PHY Illegal TX rate register [5112+]
+ */
+#define	AR5K_PHY_BAD_TX_RATE	0x9c30
+
+/*
+ * PHY SPUR Power register [5112+]
+ */
+#define	AR5K_PHY_SPUR_PWR	0x9c34			/* Register Address */
+#define	AR5K_PHY_SPUR_PWR_I	0x00000001	/* SPUR Power estimate for I (field) */
+#define	AR5K_PHY_SPUR_PWR_Q	0x00000100	/* SPUR Power estimate for Q (field) */
+#define	AR5K_PHY_SPUR_PWR_FILT	0x00010000	/* Power with SPUR removed (field) */
+
+/*
+ * PHY Channel status register [5112+] (?)
+ */
+#define	AR5K_PHY_CHAN_STATUS		0x9c38
+#define	AR5K_PHY_CHAN_STATUS_BT_ACT	0x00000001
+#define	AR5K_PHY_CHAN_STATUS_RX_CLR_RAW	0x00000002
+#define	AR5K_PHY_CHAN_STATUS_RX_CLR_MAC	0x00000004
+#define	AR5K_PHY_CHAN_STATUS_RX_CLR_PAP	0x00000008
+
+/*
+ * Heavy clip enable register
+ */
+#define	AR5K_PHY_HEAVY_CLIP_ENABLE	0x99e0
+
+/*
+ * PHY clock sleep registers [5112+]
+ */
+#define AR5K_PHY_SCLOCK			0x99f0
+#define AR5K_PHY_SCLOCK_32MHZ		0x0000000c
+#define AR5K_PHY_SDELAY			0x99f4
+#define AR5K_PHY_SDELAY_32MHZ		0x000000ff
+#define AR5K_PHY_SPENDING		0x99f8
+
+
+/*
+ * PHY PAPD I (power?) table (?)
+ * (92! entries)
+ */
+#define	AR5K_PHY_PAPD_I_BASE	0xa000
+#define	AR5K_PHY_PAPD_I(_n)	(AR5K_PHY_PAPD_I_BASE + ((_n) << 2))
+
+/*
+ * PHY PCDAC TX power table
+ */
+#define	AR5K_PHY_PCDAC_TXPOWER_BASE	0xa180
+#define	AR5K_PHY_PCDAC_TXPOWER(_n)	(AR5K_PHY_PCDAC_TXPOWER_BASE + ((_n) << 2))
+
+/*
+ * PHY mode register [5111+]
+ */
+#define	AR5K_PHY_MODE			0x0a200			/* Register Address */
+#define	AR5K_PHY_MODE_MOD		0x00000001	/* PHY Modulation bit */
+#define AR5K_PHY_MODE_MOD_OFDM		0
+#define AR5K_PHY_MODE_MOD_CCK		1
+#define AR5K_PHY_MODE_FREQ		0x00000002	/* Freq mode bit */
+#define	AR5K_PHY_MODE_FREQ_5GHZ		0
+#define	AR5K_PHY_MODE_FREQ_2GHZ		2
+#define AR5K_PHY_MODE_MOD_DYN		0x00000004	/* Enable Dynamic OFDM/CCK mode [5112+] */
+#define AR5K_PHY_MODE_RAD		0x00000008	/* [5212+] */
+#define AR5K_PHY_MODE_RAD_RF5111	0
+#define AR5K_PHY_MODE_RAD_RF5112	8
+#define AR5K_PHY_MODE_XR		0x00000010	/* Enable XR mode [5112+] */
+#define	AR5K_PHY_MODE_HALF_RATE		0x00000020	/* Enable Half rate (test) */
+#define	AR5K_PHY_MODE_QUARTER_RATE	0x00000040	/* Enable Quarter rat (test) */
+
+/*
+ * PHY CCK transmit control register [5111+ (?)]
+ */
+#define AR5K_PHY_CCKTXCTL		0xa204
+#define AR5K_PHY_CCKTXCTL_WORLD		0x00000000
+#define AR5K_PHY_CCKTXCTL_JAPAN		0x00000010
+#define	AR5K_PHY_CCKTXCTL_SCRAMBLER_DIS	0x00000001
+#define	AR5K_PHY_CCKTXCTK_DAC_SCALE	0x00000004
+
+/*
+ * PHY CCK Cross-correlator Barker RSSI threshold register [5212+]
+ */
+#define AR5K_PHY_CCK_CROSSCORR			0xa208
+#define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR	0x0000000f
+#define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR_S	0
+
+/* Same address is used for antenna diversity activation */
+#define	AR5K_PHY_FAST_ANT_DIV		0xa208
+#define	AR5K_PHY_FAST_ANT_DIV_EN	0x00002000
+
+/*
+ * PHY 2GHz gain register [5111+]
+ */
+#define	AR5K_PHY_GAIN_2GHZ			0xa20c
+#define	AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX		0x00fc0000
+#define	AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX_S	18
+#define	AR5K_PHY_GAIN_2GHZ_INI_5111		0x6480416c
+
+#define	AR5K_PHY_CCK_RX_CTL_4			0xa21c
+#define	AR5K_PHY_CCK_RX_CTL_4_FREQ_EST_SHORT	0x01f80000
+#define	AR5K_PHY_CCK_RX_CTL_4_FREQ_EST_SHORT_S	19
+
+#define	AR5K_PHY_DAG_CCK_CTL			0xa228
+#define	AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR	0x00000200
+#define	AR5K_PHY_DAG_CCK_CTL_RSSI_THR		0x0001fc00
+#define	AR5K_PHY_DAG_CCK_CTL_RSSI_THR_S		10
+
+#define	AR5K_PHY_FAST_ADC	0xa24c
+
+#define	AR5K_PHY_BLUETOOTH	0xa254
+
+/*
+ * Transmit Power Control register
+ * [2413+]
+ */
+#define	AR5K_PHY_TPC_RG1		0xa258
+#define	AR5K_PHY_TPC_RG1_NUM_PD_GAIN	0x0000c000
+#define	AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S	14
+#define AR5K_PHY_TPC_RG1_PDGAIN_1	0x00030000
+#define AR5K_PHY_TPC_RG1_PDGAIN_1_S	16
+#define AR5K_PHY_TPC_RG1_PDGAIN_2	0x000c0000
+#define AR5K_PHY_TPC_RG1_PDGAIN_2_S	18
+#define AR5K_PHY_TPC_RG1_PDGAIN_3	0x00300000
+#define AR5K_PHY_TPC_RG1_PDGAIN_3_S	20
+
+#define	AR5K_PHY_TPC_RG5			0xa26C
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP	0x0000000F
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP_S	0
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1	0x000003F0
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1_S	4
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2	0x0000FC00
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2_S	10
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3	0x003F0000
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3_S	16
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4	0x0FC00000
+#define	AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4_S	22
+
+/*
+ * PHY PDADC Tx power table
+ */
+#define AR5K_PHY_PDADC_TXPOWER_BASE	0xa280
+#define	AR5K_PHY_PDADC_TXPOWER(_n)	(AR5K_PHY_PDADC_TXPOWER_BASE + ((_n) << 2))
diff --git a/gpxe/src/drivers/net/ath5k/rfbuffer.h b/gpxe/src/drivers/net/ath5k/rfbuffer.h
new file mode 100644
index 0000000..e50baff
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/rfbuffer.h
@@ -0,0 +1,1181 @@
+/*
+ * RF Buffer handling functions
+ *
+ * Copyright (c) 2009 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+
+/*
+ * There are some special registers on the RF chip
+ * that control various operation settings related mostly to
+ * the analog parts (channel, gain adjustment etc).
+ *
+ * We don't write on those registers directly but
+ * we send a data packet on the chip, using a special register,
+ * that holds all the settings we need. After we 've sent the
+ * data packet, we write on another special register to notify hw
+ * to apply the settings. This is done so that control registers
+ * can be dynamicaly programmed during operation and the settings
+ * are applied faster on the hw.
+ *
+ * We call each data packet an "RF Bank" and all the data we write
+ * (all RF Banks) "RF Buffer". This file holds initial RF Buffer
+ * data for the different RF chips, and various info to match RF
+ * Buffer offsets with specific RF registers so that we can access
+ * them. We tweak these settings on rfregs_init function.
+ *
+ * Also check out reg.h and U.S. Patent 6677779 B1 (about buffer
+ * registers and control registers):
+ *
+ * http://www.google.com/patents?id=qNURAAAAEBAJ
+ */
+
+
+/*
+ * Struct to hold default mode specific RF
+ * register values (RF Banks)
+ */
+struct ath5k_ini_rfbuffer {
+	u8	rfb_bank;		/* RF Bank number */
+	u16	rfb_ctrl_register;	/* RF Buffer control register */
+	u32	rfb_mode_data[5];	/* RF Buffer data for each mode */
+};
+
+/*
+ * Struct to hold RF Buffer field
+ * infos used to access certain RF
+ * analog registers
+ */
+struct ath5k_rfb_field {
+	u8	len;	/* Field length */
+	u16	pos;	/* Offset on the raw packet */
+	u8	col;	/* Column -used for shifting */
+};
+
+/*
+ * RF analog register definition
+ */
+struct ath5k_rf_reg {
+	u8			bank;	/* RF Buffer Bank number */
+	u8			index;	/* Register's index on rf_regs_idx */
+	struct ath5k_rfb_field	field;	/* RF Buffer field for this register */
+};
+
+/* Map RF registers to indexes
+ * We do this to handle common bits and make our
+ * life easier by using an index for each register
+ * instead of a full rfb_field */
+enum ath5k_rf_regs_idx {
+	/* BANK 6 */
+	AR5K_RF_OB_2GHZ = 0,
+	AR5K_RF_OB_5GHZ,
+	AR5K_RF_DB_2GHZ,
+	AR5K_RF_DB_5GHZ,
+	AR5K_RF_FIXED_BIAS_A,
+	AR5K_RF_FIXED_BIAS_B,
+	AR5K_RF_PWD_XPD,
+	AR5K_RF_XPD_SEL,
+	AR5K_RF_XPD_GAIN,
+	AR5K_RF_PD_GAIN_LO,
+	AR5K_RF_PD_GAIN_HI,
+	AR5K_RF_HIGH_VC_CP,
+	AR5K_RF_MID_VC_CP,
+	AR5K_RF_LOW_VC_CP,
+	AR5K_RF_PUSH_UP,
+	AR5K_RF_PAD2GND,
+	AR5K_RF_XB2_LVL,
+	AR5K_RF_XB5_LVL,
+	AR5K_RF_PWD_ICLOBUF_2G,
+	AR5K_RF_PWD_84,
+	AR5K_RF_PWD_90,
+	AR5K_RF_PWD_130,
+	AR5K_RF_PWD_131,
+	AR5K_RF_PWD_132,
+	AR5K_RF_PWD_136,
+	AR5K_RF_PWD_137,
+	AR5K_RF_PWD_138,
+	AR5K_RF_PWD_166,
+	AR5K_RF_PWD_167,
+	AR5K_RF_DERBY_CHAN_SEL_MODE,
+	/* BANK 7 */
+	AR5K_RF_GAIN_I,
+	AR5K_RF_PLO_SEL,
+	AR5K_RF_RFGAIN_SEL,
+	AR5K_RF_RFGAIN_STEP,
+	AR5K_RF_WAIT_S,
+	AR5K_RF_WAIT_I,
+	AR5K_RF_MAX_TIME,
+	AR5K_RF_MIXVGA_OVR,
+	AR5K_RF_MIXGAIN_OVR,
+	AR5K_RF_MIXGAIN_STEP,
+	AR5K_RF_PD_DELAY_A,
+	AR5K_RF_PD_DELAY_B,
+	AR5K_RF_PD_DELAY_XR,
+	AR5K_RF_PD_PERIOD_A,
+	AR5K_RF_PD_PERIOD_B,
+	AR5K_RF_PD_PERIOD_XR,
+};
+
+
+/*******************\
+* RF5111 (Sombrero) *
+\*******************/
+
+/* BANK 6				len  pos col */
+#define	AR5K_RF5111_OB_2GHZ		{ 3, 119, 0 }
+#define	AR5K_RF5111_DB_2GHZ		{ 3, 122, 0 }
+
+#define	AR5K_RF5111_OB_5GHZ		{ 3, 104, 0 }
+#define	AR5K_RF5111_DB_5GHZ		{ 3, 107, 0 }
+
+#define	AR5K_RF5111_PWD_XPD		{ 1, 95,  0 }
+#define	AR5K_RF5111_XPD_GAIN		{ 4, 96,  0 }
+
+/* Access to PWD registers */
+#define AR5K_RF5111_PWD(_n)		{ 1, (135 - _n), 3 }
+
+/* BANK 7				len  pos col */
+#define	AR5K_RF5111_GAIN_I		{ 6, 29,  0 }
+#define	AR5K_RF5111_PLO_SEL		{ 1, 4,   0 }
+#define	AR5K_RF5111_RFGAIN_SEL		{ 1, 36,  0 }
+#define AR5K_RF5111_RFGAIN_STEP		{ 6, 37,  0 }
+/* Only on AR5212 BaseBand and up */
+#define	AR5K_RF5111_WAIT_S		{ 5, 19,  0 }
+#define	AR5K_RF5111_WAIT_I		{ 5, 24,  0 }
+#define	AR5K_RF5111_MAX_TIME		{ 2, 49,  0 }
+
+static const struct ath5k_rf_reg rf_regs_5111[] = {
+	{6, AR5K_RF_OB_2GHZ,		AR5K_RF5111_OB_2GHZ},
+	{6, AR5K_RF_DB_2GHZ,		AR5K_RF5111_DB_2GHZ},
+	{6, AR5K_RF_OB_5GHZ,		AR5K_RF5111_OB_5GHZ},
+	{6, AR5K_RF_DB_5GHZ,		AR5K_RF5111_DB_5GHZ},
+	{6, AR5K_RF_PWD_XPD,		AR5K_RF5111_PWD_XPD},
+	{6, AR5K_RF_XPD_GAIN,		AR5K_RF5111_XPD_GAIN},
+	{6, AR5K_RF_PWD_84,		AR5K_RF5111_PWD(84)},
+	{6, AR5K_RF_PWD_90,		AR5K_RF5111_PWD(90)},
+	{7, AR5K_RF_GAIN_I,		AR5K_RF5111_GAIN_I},
+	{7, AR5K_RF_PLO_SEL,		AR5K_RF5111_PLO_SEL},
+	{7, AR5K_RF_RFGAIN_SEL,		AR5K_RF5111_RFGAIN_SEL},
+	{7, AR5K_RF_RFGAIN_STEP,	AR5K_RF5111_RFGAIN_STEP},
+	{7, AR5K_RF_WAIT_S,		AR5K_RF5111_WAIT_S},
+	{7, AR5K_RF_WAIT_I,		AR5K_RF5111_WAIT_I},
+	{7, AR5K_RF_MAX_TIME,		AR5K_RF5111_MAX_TIME}
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_5111[] = {
+	{ 0, 0x989c,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00380000, 0x00380000, 0x00380000, 0x00380000, 0x00380000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0, 0x989c,
+	    { 0x00000000, 0x00000000, 0x000000c0, 0x00000080, 0x00000080 } },
+	{ 0, 0x989c,
+	    { 0x000400f9, 0x000400f9, 0x000400ff, 0x000400fd, 0x000400fd } },
+	{ 0, 0x98d4,
+	    { 0x00000000, 0x00000000, 0x00000004, 0x00000004, 0x00000004 } },
+	{ 1, 0x98d4,
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d4,
+	    { 0x00000010, 0x00000014, 0x00000010, 0x00000010, 0x00000014 } },
+	{ 3, 0x98d8,
+	    { 0x00601068, 0x00601068, 0x00601068, 0x00601068, 0x00601068 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } },
+	{ 6, 0x989c,
+	    { 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x0a000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x003800c0, 0x00380080, 0x023800c0, 0x003800c0, 0x003800c0 } },
+	{ 6, 0x989c,
+	    { 0x00020006, 0x00020006, 0x00000006, 0x00020006, 0x00020006 } },
+	{ 6, 0x989c,
+	    { 0x00000089, 0x00000089, 0x00000089, 0x00000089, 0x00000089 } },
+	{ 6, 0x989c,
+	    { 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0 } },
+	{ 6, 0x989c,
+	    { 0x00040007, 0x00040007, 0x00040007, 0x00040007, 0x00040007 } },
+	{ 6, 0x98d4,
+	    { 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a } },
+	{ 7, 0x989c,
+	    { 0x00000040, 0x00000048, 0x00000040, 0x00000040, 0x00000040 } },
+	{ 7, 0x989c,
+	    { 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 } },
+	{ 7, 0x989c,
+	    { 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008 } },
+	{ 7, 0x989c,
+	    { 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f } },
+	{ 7, 0x989c,
+	    { 0x000000f1, 0x000000f1, 0x00000061, 0x000000f1, 0x000000f1 } },
+	{ 7, 0x989c,
+	    { 0x0000904f, 0x0000904f, 0x0000904c, 0x0000904f, 0x0000904f } },
+	{ 7, 0x989c,
+	    { 0x0000125a, 0x0000125a, 0x0000129a, 0x0000125a, 0x0000125a } },
+	{ 7, 0x98cc,
+	    { 0x0000000e, 0x0000000e, 0x0000000f, 0x0000000e, 0x0000000e } },
+};
+
+
+
+/***********************\
+* RF5112/RF2112 (Derby) *
+\***********************/
+
+/* BANK 7 (Common)			len  pos col */
+#define	AR5K_RF5112X_GAIN_I		{ 6, 14,  0 }
+#define	AR5K_RF5112X_MIXVGA_OVR		{ 1, 36,  0 }
+#define	AR5K_RF5112X_MIXGAIN_OVR	{ 2, 37,  0 }
+#define AR5K_RF5112X_MIXGAIN_STEP	{ 4, 32,  0 }
+#define	AR5K_RF5112X_PD_DELAY_A		{ 4, 58,  0 }
+#define	AR5K_RF5112X_PD_DELAY_B		{ 4, 62,  0 }
+#define	AR5K_RF5112X_PD_DELAY_XR	{ 4, 66,  0 }
+#define	AR5K_RF5112X_PD_PERIOD_A	{ 4, 70,  0 }
+#define	AR5K_RF5112X_PD_PERIOD_B	{ 4, 74,  0 }
+#define	AR5K_RF5112X_PD_PERIOD_XR	{ 4, 78,  0 }
+
+/* RFX112 (Derby 1) */
+
+/* BANK 6 				len  pos col */
+#define	AR5K_RF5112_OB_2GHZ		{ 3, 269, 0 }
+#define	AR5K_RF5112_DB_2GHZ		{ 3, 272, 0 }
+
+#define	AR5K_RF5112_OB_5GHZ		{ 3, 261, 0 }
+#define	AR5K_RF5112_DB_5GHZ		{ 3, 264, 0 }
+
+#define	AR5K_RF5112_FIXED_BIAS_A	{ 1, 260, 0 }
+#define	AR5K_RF5112_FIXED_BIAS_B	{ 1, 259, 0 }
+
+#define	AR5K_RF5112_XPD_SEL		{ 1, 284, 0 }
+#define	AR5K_RF5112_XPD_GAIN		{ 2, 252, 0 }
+
+/* Access to PWD registers */
+#define AR5K_RF5112_PWD(_n)		{ 1, (302 - _n), 3 }
+
+static const struct ath5k_rf_reg rf_regs_5112[] = {
+	{6, AR5K_RF_OB_2GHZ,		AR5K_RF5112_OB_2GHZ},
+	{6, AR5K_RF_DB_2GHZ,		AR5K_RF5112_DB_2GHZ},
+	{6, AR5K_RF_OB_5GHZ,		AR5K_RF5112_OB_5GHZ},
+	{6, AR5K_RF_DB_5GHZ,		AR5K_RF5112_DB_5GHZ},
+	{6, AR5K_RF_FIXED_BIAS_A,	AR5K_RF5112_FIXED_BIAS_A},
+	{6, AR5K_RF_FIXED_BIAS_B,	AR5K_RF5112_FIXED_BIAS_B},
+	{6, AR5K_RF_XPD_SEL,		AR5K_RF5112_XPD_SEL},
+	{6, AR5K_RF_XPD_GAIN,		AR5K_RF5112_XPD_GAIN},
+	{6, AR5K_RF_PWD_130,		AR5K_RF5112_PWD(130)},
+	{6, AR5K_RF_PWD_131,		AR5K_RF5112_PWD(131)},
+	{6, AR5K_RF_PWD_132,		AR5K_RF5112_PWD(132)},
+	{6, AR5K_RF_PWD_136,		AR5K_RF5112_PWD(136)},
+	{6, AR5K_RF_PWD_137,		AR5K_RF5112_PWD(137)},
+	{6, AR5K_RF_PWD_138,		AR5K_RF5112_PWD(138)},
+	{7, AR5K_RF_GAIN_I,		AR5K_RF5112X_GAIN_I},
+	{7, AR5K_RF_MIXVGA_OVR,		AR5K_RF5112X_MIXVGA_OVR},
+	{7, AR5K_RF_MIXGAIN_OVR,	AR5K_RF5112X_MIXGAIN_OVR},
+	{7, AR5K_RF_MIXGAIN_STEP,	AR5K_RF5112X_MIXGAIN_STEP},
+	{7, AR5K_RF_PD_DELAY_A,		AR5K_RF5112X_PD_DELAY_A},
+	{7, AR5K_RF_PD_DELAY_B,		AR5K_RF5112X_PD_DELAY_B},
+	{7, AR5K_RF_PD_DELAY_XR,	AR5K_RF5112X_PD_DELAY_XR},
+	{7, AR5K_RF_PD_PERIOD_A,	AR5K_RF5112X_PD_PERIOD_A},
+	{7, AR5K_RF_PD_PERIOD_B,	AR5K_RF5112X_PD_PERIOD_B},
+	{7, AR5K_RF_PD_PERIOD_XR,	AR5K_RF5112X_PD_PERIOD_XR},
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_5112[] = {
+	{ 1, 0x98d4,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } },
+	{ 3, 0x98dc,
+	    { 0x00a0c0c0, 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0, 0x00e0c0c0 } },
+	{ 6, 0x989c,
+	    { 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000 } },
+	{ 6, 0x989c,
+	    { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00660000, 0x00660000, 0x00660000, 0x00660000, 0x00660000 } },
+	{ 6, 0x989c,
+	    { 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000 } },
+	{ 6, 0x989c,
+	    { 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000 } },
+	{ 6, 0x989c,
+	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } },
+	{ 6, 0x989c,
+	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } },
+	{ 6, 0x989c,
+	    { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000 } },
+	{ 6, 0x989c,
+	    { 0x00600000, 0x00600000, 0x00600000, 0x00600000, 0x00600000 } },
+	{ 6, 0x989c,
+	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } },
+	{ 6, 0x989c,
+	    { 0x00840000, 0x00840000, 0x00840000, 0x00840000, 0x00840000 } },
+	{ 6, 0x989c,
+	    { 0x00640000, 0x00640000, 0x00640000, 0x00640000, 0x00640000 } },
+	{ 6, 0x989c,
+	    { 0x00200000, 0x00200000, 0x00200000, 0x00200000, 0x00200000 } },
+	{ 6, 0x989c,
+	    { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } },
+	{ 6, 0x989c,
+	    { 0x00250000, 0x00250000, 0x00250000, 0x00250000, 0x00250000 } },
+	{ 6, 0x989c,
+	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } },
+	{ 6, 0x989c,
+	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } },
+	{ 6, 0x989c,
+	    { 0x00510000, 0x00510000, 0x00510000, 0x00510000, 0x00510000 } },
+	{ 6, 0x989c,
+	    { 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000 } },
+	{ 6, 0x989c,
+	    { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } },
+	{ 6, 0x989c,
+	    { 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000 } },
+	{ 6, 0x989c,
+	    { 0x00400000, 0x00400000, 0x00400000, 0x00400000, 0x00400000 } },
+	{ 6, 0x989c,
+	    { 0x03090000, 0x03090000, 0x03090000, 0x03090000, 0x03090000 } },
+	{ 6, 0x989c,
+	    { 0x06000000, 0x06000000, 0x06000000, 0x06000000, 0x06000000 } },
+	{ 6, 0x989c,
+	    { 0x000000b0, 0x000000b0, 0x000000a8, 0x000000a8, 0x000000a8 } },
+	{ 6, 0x989c,
+	    { 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e } },
+	{ 6, 0x989c,
+	    { 0x006c4a41, 0x006c4a41, 0x006c4af1, 0x006c4a61, 0x006c4a61 } },
+	{ 6, 0x989c,
+	    { 0x0050892a, 0x0050892a, 0x0050892b, 0x0050892b, 0x0050892b } },
+	{ 6, 0x989c,
+	    { 0x00842400, 0x00842400, 0x00842400, 0x00842400, 0x00842400 } },
+	{ 6, 0x989c,
+	    { 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200 } },
+	{ 6, 0x98d0,
+	    { 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c } },
+	{ 7, 0x989c,
+	    { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } },
+	{ 7, 0x989c,
+	    { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } },
+	{ 7, 0x989c,
+	    { 0x0000000a, 0x0000000a, 0x00000012, 0x00000012, 0x00000012 } },
+	{ 7, 0x989c,
+	    { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } },
+	{ 7, 0x989c,
+	    { 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1 } },
+	{ 7, 0x989c,
+	    { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } },
+	{ 7, 0x989c,
+	    { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } },
+	{ 7, 0x989c,
+	    { 0x00000022, 0x00000022, 0x00000022, 0x00000022, 0x00000022 } },
+	{ 7, 0x989c,
+	    { 0x00000092, 0x00000092, 0x00000092, 0x00000092, 0x00000092 } },
+	{ 7, 0x989c,
+	    { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } },
+	{ 7, 0x989c,
+	    { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } },
+	{ 7, 0x989c,
+	    { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } },
+	{ 7, 0x98c4,
+	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } },
+};
+
+/* RFX112A (Derby 2) */
+
+/* BANK 6				len  pos col */
+#define	AR5K_RF5112A_OB_2GHZ		{ 3, 287, 0 }
+#define	AR5K_RF5112A_DB_2GHZ		{ 3, 290, 0 }
+
+#define	AR5K_RF5112A_OB_5GHZ		{ 3, 279, 0 }
+#define	AR5K_RF5112A_DB_5GHZ		{ 3, 282, 0 }
+
+#define	AR5K_RF5112A_FIXED_BIAS_A	{ 1, 278, 0 }
+#define	AR5K_RF5112A_FIXED_BIAS_B	{ 1, 277, 0 }
+
+#define	AR5K_RF5112A_XPD_SEL		{ 1, 302, 0 }
+#define	AR5K_RF5112A_PDGAINLO		{ 2, 270, 0 }
+#define	AR5K_RF5112A_PDGAINHI		{ 2, 257, 0 }
+
+/* Access to PWD registers */
+#define AR5K_RF5112A_PWD(_n)		{ 1, (306 - _n), 3 }
+
+/* Voltage regulators */
+#define	AR5K_RF5112A_HIGH_VC_CP		{ 2, 90,  2 }
+#define	AR5K_RF5112A_MID_VC_CP		{ 2, 92,  2 }
+#define	AR5K_RF5112A_LOW_VC_CP		{ 2, 94,  2 }
+#define	AR5K_RF5112A_PUSH_UP		{ 1, 254,  2 }
+
+/* Power consumption */
+#define	AR5K_RF5112A_PAD2GND		{ 1, 281, 1 }
+#define	AR5K_RF5112A_XB2_LVL		{ 2, 1,	  3 }
+#define	AR5K_RF5112A_XB5_LVL		{ 2, 3,	  3 }
+
+static const struct ath5k_rf_reg rf_regs_5112a[] = {
+	{6, AR5K_RF_OB_2GHZ,		AR5K_RF5112A_OB_2GHZ},
+	{6, AR5K_RF_DB_2GHZ,		AR5K_RF5112A_DB_2GHZ},
+	{6, AR5K_RF_OB_5GHZ,		AR5K_RF5112A_OB_5GHZ},
+	{6, AR5K_RF_DB_5GHZ,		AR5K_RF5112A_DB_5GHZ},
+	{6, AR5K_RF_FIXED_BIAS_A,	AR5K_RF5112A_FIXED_BIAS_A},
+	{6, AR5K_RF_FIXED_BIAS_B,	AR5K_RF5112A_FIXED_BIAS_B},
+	{6, AR5K_RF_XPD_SEL,		AR5K_RF5112A_XPD_SEL},
+	{6, AR5K_RF_PD_GAIN_LO,		AR5K_RF5112A_PDGAINLO},
+	{6, AR5K_RF_PD_GAIN_HI,		AR5K_RF5112A_PDGAINHI},
+	{6, AR5K_RF_PWD_130,		AR5K_RF5112A_PWD(130)},
+	{6, AR5K_RF_PWD_131,		AR5K_RF5112A_PWD(131)},
+	{6, AR5K_RF_PWD_132,		AR5K_RF5112A_PWD(132)},
+	{6, AR5K_RF_PWD_136,		AR5K_RF5112A_PWD(136)},
+	{6, AR5K_RF_PWD_137,		AR5K_RF5112A_PWD(137)},
+	{6, AR5K_RF_PWD_138,		AR5K_RF5112A_PWD(138)},
+	{6, AR5K_RF_PWD_166,		AR5K_RF5112A_PWD(166)},
+	{6, AR5K_RF_PWD_167,		AR5K_RF5112A_PWD(167)},
+	{6, AR5K_RF_HIGH_VC_CP,		AR5K_RF5112A_HIGH_VC_CP},
+	{6, AR5K_RF_MID_VC_CP,		AR5K_RF5112A_MID_VC_CP},
+	{6, AR5K_RF_LOW_VC_CP,		AR5K_RF5112A_LOW_VC_CP},
+	{6, AR5K_RF_PUSH_UP,		AR5K_RF5112A_PUSH_UP},
+	{6, AR5K_RF_PAD2GND,		AR5K_RF5112A_PAD2GND},
+	{6, AR5K_RF_XB2_LVL,		AR5K_RF5112A_XB2_LVL},
+	{6, AR5K_RF_XB5_LVL,		AR5K_RF5112A_XB5_LVL},
+	{7, AR5K_RF_GAIN_I,		AR5K_RF5112X_GAIN_I},
+	{7, AR5K_RF_MIXVGA_OVR,		AR5K_RF5112X_MIXVGA_OVR},
+	{7, AR5K_RF_MIXGAIN_OVR,	AR5K_RF5112X_MIXGAIN_OVR},
+	{7, AR5K_RF_MIXGAIN_STEP,	AR5K_RF5112X_MIXGAIN_STEP},
+	{7, AR5K_RF_PD_DELAY_A,		AR5K_RF5112X_PD_DELAY_A},
+	{7, AR5K_RF_PD_DELAY_B,		AR5K_RF5112X_PD_DELAY_B},
+	{7, AR5K_RF_PD_DELAY_XR,	AR5K_RF5112X_PD_DELAY_XR},
+	{7, AR5K_RF_PD_PERIOD_A,	AR5K_RF5112X_PD_PERIOD_A},
+	{7, AR5K_RF_PD_PERIOD_B,	AR5K_RF5112X_PD_PERIOD_B},
+	{7, AR5K_RF_PD_PERIOD_XR,	AR5K_RF5112X_PD_PERIOD_XR},
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_5112a[] = {
+	{ 1, 0x98d4,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } },
+	{ 3, 0x98dc,
+	    { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } },
+	{ 6, 0x989c,
+	    { 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00800000, 0x00800000, 0x00800000, 0x00800000, 0x00800000 } },
+	{ 6, 0x989c,
+	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } },
+	{ 6, 0x989c,
+	    { 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00180000, 0x00180000, 0x00180000, 0x00180000, 0x00180000 } },
+	{ 6, 0x989c,
+	    { 0x00600000, 0x00600000, 0x006e0000, 0x006e0000, 0x006e0000 } },
+	{ 6, 0x989c,
+	    { 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000 } },
+	{ 6, 0x989c,
+	    { 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000 } },
+	{ 6, 0x989c,
+	    { 0x04480000, 0x04480000, 0x04480000, 0x04480000, 0x04480000 } },
+	{ 6, 0x989c,
+	    { 0x004c0000, 0x004c0000, 0x004c0000, 0x004c0000, 0x004c0000 } },
+	{ 6, 0x989c,
+	    { 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000 } },
+	{ 6, 0x989c,
+	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } },
+	{ 6, 0x989c,
+	    { 0x02190000, 0x02190000, 0x02190000, 0x02190000, 0x02190000 } },
+	{ 6, 0x989c,
+	    { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } },
+	{ 6, 0x989c,
+	    { 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000 } },
+	{ 6, 0x989c,
+	    { 0x00990000, 0x00990000, 0x00990000, 0x00990000, 0x00990000 } },
+	{ 6, 0x989c,
+	    { 0x00500000, 0x00500000, 0x00500000, 0x00500000, 0x00500000 } },
+	{ 6, 0x989c,
+	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } },
+	{ 6, 0x989c,
+	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } },
+	{ 6, 0x989c,
+	    { 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000 } },
+	{ 6, 0x989c,
+	    { 0x01740000, 0x01740000, 0x01740000, 0x01740000, 0x01740000 } },
+	{ 6, 0x989c,
+	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } },
+	{ 6, 0x989c,
+	    { 0x86280000, 0x86280000, 0x86280000, 0x86280000, 0x86280000 } },
+	{ 6, 0x989c,
+	    { 0x31840000, 0x31840000, 0x31840000, 0x31840000, 0x31840000 } },
+	{ 6, 0x989c,
+	    { 0x00f20080, 0x00f20080, 0x00f20080, 0x00f20080, 0x00f20080 } },
+	{ 6, 0x989c,
+	    { 0x00270019, 0x00270019, 0x00270019, 0x00270019, 0x00270019 } },
+	{ 6, 0x989c,
+	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2 } },
+	{ 6, 0x989c,
+	    { 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084 } },
+	{ 6, 0x989c,
+	    { 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4 } },
+	{ 6, 0x989c,
+	    { 0x00119220, 0x00119220, 0x00119220, 0x00119220, 0x00119220 } },
+	{ 6, 0x989c,
+	    { 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800 } },
+	{ 6, 0x98d8,
+	    { 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230 } },
+	{ 7, 0x989c,
+	    { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } },
+	{ 7, 0x989c,
+	    { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } },
+	{ 7, 0x989c,
+	    { 0x00000012, 0x00000012, 0x00000012, 0x00000012, 0x00000012 } },
+	{ 7, 0x989c,
+	    { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } },
+	{ 7, 0x989c,
+	    { 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9 } },
+	{ 7, 0x989c,
+	    { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } },
+	{ 7, 0x989c,
+	    { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } },
+	{ 7, 0x989c,
+	    { 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2 } },
+	{ 7, 0x989c,
+	    { 0x00000052, 0x00000052, 0x00000052, 0x00000052, 0x00000052 } },
+	{ 7, 0x989c,
+	    { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } },
+	{ 7, 0x989c,
+	    { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } },
+	{ 7, 0x989c,
+	    { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } },
+	{ 7, 0x98c4,
+	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } },
+};
+
+
+
+/******************\
+* RF2413 (Griffin) *
+\******************/
+
+/* BANK 6 				len  pos col */
+#define	AR5K_RF2413_OB_2GHZ		{ 3, 168, 0 }
+#define	AR5K_RF2413_DB_2GHZ		{ 3, 165, 0 }
+
+static const struct ath5k_rf_reg rf_regs_2413[] = {
+	{6, AR5K_RF_OB_2GHZ,		AR5K_RF2413_OB_2GHZ},
+	{6, AR5K_RF_DB_2GHZ,		AR5K_RF2413_DB_2GHZ},
+};
+
+/* Default mode specific settings
+ * XXX: a/aTurbo ???
+ */
+static const struct ath5k_ini_rfbuffer rfb_2413[] = {
+	{ 1, 0x98d4,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x02001408, 0x02011408, 0x02001408, 0x02001408, 0x02011408 } },
+	{ 3, 0x98dc,
+	    { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } },
+	{ 6, 0x989c,
+	    { 0xf0000000, 0xf0000000, 0xf0000000, 0xf0000000, 0xf0000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x40400000, 0x40400000, 0x40400000, 0x40400000, 0x40400000 } },
+	{ 6, 0x989c,
+	    { 0x65050000, 0x65050000, 0x65050000, 0x65050000, 0x65050000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00420000, 0x00420000, 0x00420000, 0x00420000, 0x00420000 } },
+	{ 6, 0x989c,
+	    { 0x00b50000, 0x00b50000, 0x00b50000, 0x00b50000, 0x00b50000 } },
+	{ 6, 0x989c,
+	    { 0x00030000, 0x00030000, 0x00030000, 0x00030000, 0x00030000 } },
+	{ 6, 0x989c,
+	    { 0x00f70000, 0x00f70000, 0x00f70000, 0x00f70000, 0x00f70000 } },
+	{ 6, 0x989c,
+	    { 0x009d0000, 0x009d0000, 0x009d0000, 0x009d0000, 0x009d0000 } },
+	{ 6, 0x989c,
+	    { 0x00220000, 0x00220000, 0x00220000, 0x00220000, 0x00220000 } },
+	{ 6, 0x989c,
+	    { 0x04220000, 0x04220000, 0x04220000, 0x04220000, 0x04220000 } },
+	{ 6, 0x989c,
+	    { 0x00230018, 0x00230018, 0x00230018, 0x00230018, 0x00230018 } },
+	{ 6, 0x989c,
+	    { 0x00280000, 0x00280000, 0x00280060, 0x00280060, 0x00280060 } },
+	{ 6, 0x989c,
+	    { 0x005000c0, 0x005000c0, 0x005000c3, 0x005000c3, 0x005000c3 } },
+	{ 6, 0x989c,
+	    { 0x0004007f, 0x0004007f, 0x0004007f, 0x0004007f, 0x0004007f } },
+	{ 6, 0x989c,
+	    { 0x00000458, 0x00000458, 0x00000458, 0x00000458, 0x00000458 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x0000c000, 0x0000c000, 0x0000c000, 0x0000c000, 0x0000c000 } },
+	{ 6, 0x98d8,
+	    { 0x00400230, 0x00400230, 0x00400230, 0x00400230, 0x00400230 } },
+	{ 7, 0x989c,
+	    { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } },
+	{ 7, 0x989c,
+	    { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } },
+	{ 7, 0x98cc,
+	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+
+
+/***************************\
+* RF2315/RF2316 (Cobra SoC) *
+\***************************/
+
+/* BANK 6				len  pos col */
+#define	AR5K_RF2316_OB_2GHZ		{ 3, 178, 0 }
+#define	AR5K_RF2316_DB_2GHZ		{ 3, 175, 0 }
+
+static const struct ath5k_rf_reg rf_regs_2316[] = {
+	{6, AR5K_RF_OB_2GHZ,		AR5K_RF2316_OB_2GHZ},
+	{6, AR5K_RF_DB_2GHZ,		AR5K_RF2316_DB_2GHZ},
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_2316[] = {
+	{ 1, 0x98d4,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x02001408, 0x02011408, 0x02001408, 0x02001408, 0x02011408 } },
+	{ 3, 0x98dc,
+	    { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000 } },
+	{ 6, 0x989c,
+	    { 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000 } },
+	{ 6, 0x989c,
+	    { 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0xf8000000, 0xf8000000, 0xf8000000, 0xf8000000, 0xf8000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x95150000, 0x95150000, 0x95150000, 0x95150000, 0x95150000 } },
+	{ 6, 0x989c,
+	    { 0xc1000000, 0xc1000000, 0xc1000000, 0xc1000000, 0xc1000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00080000, 0x00080000, 0x00080000, 0x00080000, 0x00080000 } },
+	{ 6, 0x989c,
+	    { 0x00d50000, 0x00d50000, 0x00d50000, 0x00d50000, 0x00d50000 } },
+	{ 6, 0x989c,
+	    { 0x000e0000, 0x000e0000, 0x000e0000, 0x000e0000, 0x000e0000 } },
+	{ 6, 0x989c,
+	    { 0x00dc0000, 0x00dc0000, 0x00dc0000, 0x00dc0000, 0x00dc0000 } },
+	{ 6, 0x989c,
+	    { 0x00770000, 0x00770000, 0x00770000, 0x00770000, 0x00770000 } },
+	{ 6, 0x989c,
+	    { 0x008a0000, 0x008a0000, 0x008a0000, 0x008a0000, 0x008a0000 } },
+	{ 6, 0x989c,
+	    { 0x10880000, 0x10880000, 0x10880000, 0x10880000, 0x10880000 } },
+	{ 6, 0x989c,
+	    { 0x008c0060, 0x008c0060, 0x008c0060, 0x008c0060, 0x008c0060 } },
+	{ 6, 0x989c,
+	    { 0x00a00000, 0x00a00000, 0x00a00080, 0x00a00080, 0x00a00080 } },
+	{ 6, 0x989c,
+	    { 0x00400000, 0x00400000, 0x0040000d, 0x0040000d, 0x0040000d } },
+	{ 6, 0x989c,
+	    { 0x00110400, 0x00110400, 0x00110400, 0x00110400, 0x00110400 } },
+	{ 6, 0x989c,
+	    { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } },
+	{ 6, 0x989c,
+	    { 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } },
+	{ 6, 0x989c,
+	    { 0x00000b00, 0x00000b00, 0x00000b00, 0x00000b00, 0x00000b00 } },
+	{ 6, 0x989c,
+	    { 0x00000be8, 0x00000be8, 0x00000be8, 0x00000be8, 0x00000be8 } },
+	{ 6, 0x98c0,
+	    { 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000 } },
+	{ 7, 0x989c,
+	    { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } },
+	{ 7, 0x989c,
+	    { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } },
+	{ 7, 0x98cc,
+	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+
+
+/******************************\
+* RF5413/RF5424 (Eagle/Condor) *
+\******************************/
+
+/* BANK 6				len  pos col */
+#define	AR5K_RF5413_OB_2GHZ		{ 3, 241, 0 }
+#define	AR5K_RF5413_DB_2GHZ		{ 3, 238, 0 }
+
+#define	AR5K_RF5413_OB_5GHZ		{ 3, 247, 0 }
+#define	AR5K_RF5413_DB_5GHZ		{ 3, 244, 0 }
+
+#define	AR5K_RF5413_PWD_ICLOBUF2G	{ 3, 131, 3 }
+#define	AR5K_RF5413_DERBY_CHAN_SEL_MODE	{ 1, 291, 2 }
+
+static const struct ath5k_rf_reg rf_regs_5413[] = {
+	{6, AR5K_RF_OB_2GHZ,		 AR5K_RF5413_OB_2GHZ},
+	{6, AR5K_RF_DB_2GHZ,		 AR5K_RF5413_DB_2GHZ},
+	{6, AR5K_RF_OB_5GHZ,		 AR5K_RF5413_OB_5GHZ},
+	{6, AR5K_RF_DB_5GHZ,		 AR5K_RF5413_DB_5GHZ},
+	{6, AR5K_RF_PWD_ICLOBUF_2G,	 AR5K_RF5413_PWD_ICLOBUF2G},
+	{6, AR5K_RF_DERBY_CHAN_SEL_MODE, AR5K_RF5413_DERBY_CHAN_SEL_MODE},
+};
+
+/* Default mode specific settings */
+static const struct ath5k_ini_rfbuffer rfb_5413[] = {
+	{ 1, 0x98d4,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008 } },
+	{ 3, 0x98dc,
+	    { 0x00a000c0, 0x00a000c0, 0x00e000c0, 0x00e000c0, 0x00e000c0 } },
+	{ 6, 0x989c,
+	    { 0x33000000, 0x33000000, 0x33000000, 0x33000000, 0x33000000 } },
+	{ 6, 0x989c,
+	    { 0x01000000, 0x01000000, 0x01000000, 0x01000000, 0x01000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x1f000000, 0x1f000000, 0x1f000000, 0x1f000000, 0x1f000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00b80000, 0x00b80000, 0x00b80000, 0x00b80000, 0x00b80000 } },
+	{ 6, 0x989c,
+	    { 0x00b70000, 0x00b70000, 0x00b70000, 0x00b70000, 0x00b70000 } },
+	{ 6, 0x989c,
+	    { 0x00840000, 0x00840000, 0x00840000, 0x00840000, 0x00840000 } },
+	{ 6, 0x989c,
+	    { 0x00980000, 0x00980000, 0x00980000, 0x00980000, 0x00980000 } },
+	{ 6, 0x989c,
+	    { 0x00c00000, 0x00c00000, 0x00c00000, 0x00c00000, 0x00c00000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
+	{ 6, 0x989c,
+	    { 0x00d70000, 0x00d70000, 0x00d70000, 0x00d70000, 0x00d70000 } },
+	{ 6, 0x989c,
+	    { 0x00610000, 0x00610000, 0x00610000, 0x00610000, 0x00610000 } },
+	{ 6, 0x989c,
+	    { 0x00fe0000, 0x00fe0000, 0x00fe0000, 0x00fe0000, 0x00fe0000 } },
+	{ 6, 0x989c,
+	    { 0x00de0000, 0x00de0000, 0x00de0000, 0x00de0000, 0x00de0000 } },
+	{ 6, 0x989c,
+	    { 0x007f0000, 0x007f0000, 0x007f0000, 0x007f0000, 0x007f0000 } },
+	{ 6, 0x989c,
+	    { 0x043d0000, 0x043d0000, 0x043d0000, 0x043d0000, 0x043d0000 } },
+	{ 6, 0x989c,
+	    { 0x00770000, 0x00770000, 0x00770000, 0x00770000, 0x00770000 } },
+	{ 6, 0x989c,
+	    { 0x00440000, 0x00440000, 0x00440000, 0x00440000, 0x00440000 } },
+	{ 6, 0x989c,
+	    { 0x00980000, 0x00980000, 0x00980000, 0x00980000, 0x00980000 } },
+	{ 6, 0x989c,
+	    { 0x00100080, 0x00100080, 0x00100080, 0x00100080, 0x00100080 } },
+	{ 6, 0x989c,
+	    { 0x0005c034, 0x0005c034, 0x0005c034, 0x0005c034, 0x0005c034 } },
+	{ 6, 0x989c,
+	    { 0x003100f0, 0x003100f0, 0x003100f0, 0x003100f0, 0x003100f0 } },
+	{ 6, 0x989c,
+	    { 0x000c011f, 0x000c011f, 0x000c011f, 0x000c011f, 0x000c011f } },
+	{ 6, 0x989c,
+	    { 0x00510040, 0x00510040, 0x00510040, 0x00510040, 0x00510040 } },
+	{ 6, 0x989c,
+	    { 0x005000da, 0x005000da, 0x005000da, 0x005000da, 0x005000da } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00004044, 0x00004044, 0x00004044, 0x00004044, 0x00004044 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x000060c0, 0x000060c0, 0x000060c0, 0x000060c0, 0x000060c0 } },
+	{ 6, 0x989c,
+	    { 0x00002c00, 0x00002c00, 0x00003600, 0x00003600, 0x00002c00 } },
+	{ 6, 0x98c8,
+	    { 0x00000403, 0x00000403, 0x00040403, 0x00040403, 0x00040403 } },
+	{ 7, 0x989c,
+	    { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } },
+	{ 7, 0x989c,
+	    { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } },
+	{ 7, 0x98cc,
+	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+
+
+/***************************\
+* RF2425/RF2417 (Swan/Nala) *
+* AR2317 (Spider SoC)       *
+\***************************/
+
+/* BANK 6				len  pos col */
+#define	AR5K_RF2425_OB_2GHZ		{ 3, 193, 0 }
+#define	AR5K_RF2425_DB_2GHZ		{ 3, 190, 0 }
+
+static const struct ath5k_rf_reg rf_regs_2425[] = {
+	{6, AR5K_RF_OB_2GHZ,		AR5K_RF2425_OB_2GHZ},
+	{6, AR5K_RF_DB_2GHZ,		AR5K_RF2425_DB_2GHZ},
+};
+
+/* Default mode specific settings
+ * XXX: a/aTurbo ?
+ */
+static const struct ath5k_ini_rfbuffer rfb_2425[] = {
+	{ 1, 0x98d4,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x02001408, 0x02001408, 0x02001408, 0x02001408, 0x02001408 } },
+	{ 3, 0x98dc,
+	    { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } },
+	{ 6, 0x989c,
+	    { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000 } },
+	{ 6, 0x989c,
+	    { 0x00020000, 0x00020000, 0x00020000, 0x00020000, 0x00020000 } },
+	{ 6, 0x989c,
+	    { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } },
+	{ 6, 0x989c,
+	    { 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000 } },
+	{ 6, 0x989c,
+	    { 0x00e70000, 0x00e70000, 0x00e70000, 0x00e70000, 0x00e70000 } },
+	{ 6, 0x989c,
+	    { 0x00140000, 0x00140000, 0x00140000, 0x00140000, 0x00140000 } },
+	{ 6, 0x989c,
+	    { 0x00910040, 0x00910040, 0x00910040, 0x00910040, 0x00910040 } },
+	{ 6, 0x989c,
+	    { 0x0007001a, 0x0007001a, 0x0007001a, 0x0007001a, 0x0007001a } },
+	{ 6, 0x989c,
+	    { 0x00410000, 0x00410000, 0x00410000, 0x00410000, 0x00410000 } },
+	{ 6, 0x989c,
+	    { 0x00810000, 0x00810000, 0x00810060, 0x00810060, 0x00810060 } },
+	{ 6, 0x989c,
+	    { 0x00020800, 0x00020800, 0x00020803, 0x00020803, 0x00020803 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00001660, 0x00001660, 0x00001660, 0x00001660, 0x00001660 } },
+	{ 6, 0x989c,
+	    { 0x00001688, 0x00001688, 0x00001688, 0x00001688, 0x00001688 } },
+	{ 6, 0x98c4,
+	    { 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } },
+	{ 7, 0x989c,
+	    { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } },
+	{ 7, 0x989c,
+	    { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } },
+	{ 7, 0x98cc,
+	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+/*
+ * TODO: Handle the few differences with swan during
+ * bank modification and get rid of this
+ */
+static const struct ath5k_ini_rfbuffer rfb_2317[] = {
+	{ 1, 0x98d4,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x02001408, 0x02011408, 0x02001408, 0x02001408, 0x02011408 } },
+	{ 3, 0x98dc,
+	    { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } },
+	{ 6, 0x989c,
+	    { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000 } },
+	{ 6, 0x989c,
+	    { 0x00020000, 0x00020000, 0x00020000, 0x00020000, 0x00020000 } },
+	{ 6, 0x989c,
+	    { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } },
+	{ 6, 0x989c,
+	    { 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000 } },
+	{ 6, 0x989c,
+	    { 0x00e70000, 0x00e70000, 0x00e70000, 0x00e70000, 0x00e70000 } },
+	{ 6, 0x989c,
+	    { 0x00140100, 0x00140100, 0x00140100, 0x00140100, 0x00140100 } },
+	{ 6, 0x989c,
+	    { 0x00910040, 0x00910040, 0x00910040, 0x00910040, 0x00910040 } },
+	{ 6, 0x989c,
+	    { 0x0007001a, 0x0007001a, 0x0007001a, 0x0007001a, 0x0007001a } },
+	{ 6, 0x989c,
+	    { 0x00410000, 0x00410000, 0x00410000, 0x00410000, 0x00410000 } },
+	{ 6, 0x989c,
+	    { 0x00810000, 0x00810000, 0x00810060, 0x00810060, 0x00810060 } },
+	{ 6, 0x989c,
+	    { 0x00020800, 0x00020800, 0x00020803, 0x00020803, 0x00020803 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00001660, 0x00001660, 0x00001660, 0x00001660, 0x00001660 } },
+	{ 6, 0x989c,
+	    { 0x00009688, 0x00009688, 0x00009688, 0x00009688, 0x00009688 } },
+	{ 6, 0x98c4,
+	    { 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } },
+	{ 7, 0x989c,
+	    { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } },
+	{ 7, 0x989c,
+	    { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } },
+	{ 7, 0x98cc,
+	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
+};
+
+/*
+ * TODO: Handle the few differences with swan during
+ * bank modification and get rid of this
+ * XXX: a/aTurbo ?
+ */
+static const struct ath5k_ini_rfbuffer rfb_2417[] = {
+	{ 1, 0x98d4,
+	/*     mode a/XR  mode aTurbo    mode b     mode g    mode gTurbo */
+	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, 0x98d0,
+	    { 0x02001408, 0x02001408, 0x02001408, 0x02001408, 0x02001408 } },
+	{ 3, 0x98dc,
+	    { 0x00a020c0, 0x00a020c0, 0x00e020c0, 0x00e020c0, 0x00e020c0 } },
+	{ 6, 0x989c,
+	    { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000 } },
+	{ 6, 0x989c,
+	    { 0x00020000, 0x00020000, 0x00020000, 0x00020000, 0x00020000 } },
+	{ 6, 0x989c,
+	    { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } },
+	{ 6, 0x989c,
+	    { 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000, 0x00f80000 } },
+	{ 6, 0x989c,
+	    { 0x00e70000, 0x00e70000, 0x80e70000, 0x80e70000, 0x00e70000 } },
+	{ 6, 0x989c,
+	    { 0x00140000, 0x00140000, 0x00140000, 0x00140000, 0x00140000 } },
+	{ 6, 0x989c,
+	    { 0x00910040, 0x00910040, 0x00910040, 0x00910040, 0x00910040 } },
+	{ 6, 0x989c,
+	    { 0x0007001a, 0x0007001a, 0x0207001a, 0x0207001a, 0x0007001a } },
+	{ 6, 0x989c,
+	    { 0x00410000, 0x00410000, 0x00410000, 0x00410000, 0x00410000 } },
+	{ 6, 0x989c,
+	    { 0x00810000, 0x00810000, 0x00810060, 0x00810060, 0x00810060 } },
+	{ 6, 0x989c,
+	    { 0x00020800, 0x00020800, 0x00020803, 0x00020803, 0x00020803 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, 0x989c,
+	    { 0x00001660, 0x00001660, 0x00001660, 0x00001660, 0x00001660 } },
+	{ 6, 0x989c,
+	    { 0x00001688, 0x00001688, 0x00001688, 0x00001688, 0x00001688 } },
+	{ 6, 0x98c4,
+	    { 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } },
+	{ 7, 0x989c,
+	    { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } },
+	{ 7, 0x989c,
+	    { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } },
+	{ 7, 0x98cc,
+	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
+};
diff --git a/gpxe/src/drivers/net/ath5k/rfgain.h b/gpxe/src/drivers/net/ath5k/rfgain.h
new file mode 100644
index 0000000..1354d8c
--- /dev/null
+++ b/gpxe/src/drivers/net/ath5k/rfgain.h
@@ -0,0 +1,516 @@
+/*
+ * RF Gain optimization
+ *
+ * Copyright (c) 2004-2009 Reyk Floeter <reyk at openbsd.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm at gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * Mode-specific RF Gain table (64bytes) for RF5111/5112
+ * (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial
+ * RF Gain values are included in AR5K_AR5210_INI)
+ */
+struct ath5k_ini_rfgain {
+	u16	rfg_register;	/* RF Gain register address */
+	u32	rfg_value[2];	/* [freq (see below)] */
+};
+
+/* Initial RF Gain settings for RF5111 */
+static const struct ath5k_ini_rfgain rfgain_5111[] = {
+	/*			      5Ghz	2Ghz	*/
+	{ AR5K_RF_GAIN(0),	{ 0x000001a9, 0x00000000 } },
+	{ AR5K_RF_GAIN(1),	{ 0x000001e9, 0x00000040 } },
+	{ AR5K_RF_GAIN(2),	{ 0x00000029, 0x00000080 } },
+	{ AR5K_RF_GAIN(3),	{ 0x00000069, 0x00000150 } },
+	{ AR5K_RF_GAIN(4),	{ 0x00000199, 0x00000190 } },
+	{ AR5K_RF_GAIN(5),	{ 0x000001d9, 0x000001d0 } },
+	{ AR5K_RF_GAIN(6),	{ 0x00000019, 0x00000010 } },
+	{ AR5K_RF_GAIN(7),	{ 0x00000059, 0x00000044 } },
+	{ AR5K_RF_GAIN(8),	{ 0x00000099, 0x00000084 } },
+	{ AR5K_RF_GAIN(9),	{ 0x000001a5, 0x00000148 } },
+	{ AR5K_RF_GAIN(10),	{ 0x000001e5, 0x00000188 } },
+	{ AR5K_RF_GAIN(11),	{ 0x00000025, 0x000001c8 } },
+	{ AR5K_RF_GAIN(12),	{ 0x000001c8, 0x00000014 } },
+	{ AR5K_RF_GAIN(13),	{ 0x00000008, 0x00000042 } },
+	{ AR5K_RF_GAIN(14),	{ 0x00000048, 0x00000082 } },
+	{ AR5K_RF_GAIN(15),	{ 0x00000088, 0x00000178 } },
+	{ AR5K_RF_GAIN(16),	{ 0x00000198, 0x000001b8 } },
+	{ AR5K_RF_GAIN(17),	{ 0x000001d8, 0x000001f8 } },
+	{ AR5K_RF_GAIN(18),	{ 0x00000018, 0x00000012 } },
+	{ AR5K_RF_GAIN(19),	{ 0x00000058, 0x00000052 } },
+	{ AR5K_RF_GAIN(20),	{ 0x00000098, 0x00000092 } },
+	{ AR5K_RF_GAIN(21),	{ 0x000001a4, 0x0000017c } },
+	{ AR5K_RF_GAIN(22),	{ 0x000001e4, 0x000001bc } },
+	{ AR5K_RF_GAIN(23),	{ 0x00000024, 0x000001fc } },
+	{ AR5K_RF_GAIN(24),	{ 0x00000064, 0x0000000a } },
+	{ AR5K_RF_GAIN(25),	{ 0x000000a4, 0x0000004a } },
+	{ AR5K_RF_GAIN(26),	{ 0x000000e4, 0x0000008a } },
+	{ AR5K_RF_GAIN(27),	{ 0x0000010a, 0x0000015a } },
+	{ AR5K_RF_GAIN(28),	{ 0x0000014a, 0x0000019a } },
+	{ AR5K_RF_GAIN(29),	{ 0x0000018a, 0x000001da } },
+	{ AR5K_RF_GAIN(30),	{ 0x000001ca, 0x0000000e } },
+	{ AR5K_RF_GAIN(31),	{ 0x0000000a, 0x0000004e } },
+	{ AR5K_RF_GAIN(32),	{ 0x0000004a, 0x0000008e } },
+	{ AR5K_RF_GAIN(33),	{ 0x0000008a, 0x0000015e } },
+	{ AR5K_RF_GAIN(34),	{ 0x000001ba, 0x0000019e } },
+	{ AR5K_RF_GAIN(35),	{ 0x000001fa, 0x000001de } },
+	{ AR5K_RF_GAIN(36),	{ 0x0000003a, 0x00000009 } },
+	{ AR5K_RF_GAIN(37),	{ 0x0000007a, 0x00000049 } },
+	{ AR5K_RF_GAIN(38),	{ 0x00000186, 0x00000089 } },
+	{ AR5K_RF_GAIN(39),	{ 0x000001c6, 0x00000179 } },
+	{ AR5K_RF_GAIN(40),	{ 0x00000006, 0x000001b9 } },
+	{ AR5K_RF_GAIN(41),	{ 0x00000046, 0x000001f9 } },
+	{ AR5K_RF_GAIN(42),	{ 0x00000086, 0x00000039 } },
+	{ AR5K_RF_GAIN(43),	{ 0x000000c6, 0x00000079 } },
+	{ AR5K_RF_GAIN(44),	{ 0x000000c6, 0x000000b9 } },
+	{ AR5K_RF_GAIN(45),	{ 0x000000c6, 0x000001bd } },
+	{ AR5K_RF_GAIN(46),	{ 0x000000c6, 0x000001fd } },
+	{ AR5K_RF_GAIN(47),	{ 0x000000c6, 0x0000003d } },
+	{ AR5K_RF_GAIN(48),	{ 0x000000c6, 0x0000007d } },
+	{ AR5K_RF_GAIN(49),	{ 0x000000c6, 0x000000bd } },
+	{ AR5K_RF_GAIN(50),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(51),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(52),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(53),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(54),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(55),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(56),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(57),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(58),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(59),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(60),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(61),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(62),	{ 0x000000c6, 0x000000fd } },
+	{ AR5K_RF_GAIN(63),	{ 0x000000c6, 0x000000fd } },
+};
+
+/* Initial RF Gain settings for RF5112 */
+static const struct ath5k_ini_rfgain rfgain_5112[] = {
+	/*			      5Ghz	2Ghz	*/
+	{ AR5K_RF_GAIN(0),	{ 0x00000007, 0x00000007 } },
+	{ AR5K_RF_GAIN(1),	{ 0x00000047, 0x00000047 } },
+	{ AR5K_RF_GAIN(2),	{ 0x00000087, 0x00000087 } },
+	{ AR5K_RF_GAIN(3),	{ 0x000001a0, 0x000001a0 } },
+	{ AR5K_RF_GAIN(4),	{ 0x000001e0, 0x000001e0 } },
+	{ AR5K_RF_GAIN(5),	{ 0x00000020, 0x00000020 } },
+	{ AR5K_RF_GAIN(6),	{ 0x00000060, 0x00000060 } },
+	{ AR5K_RF_GAIN(7),	{ 0x000001a1, 0x000001a1 } },
+	{ AR5K_RF_GAIN(8),	{ 0x000001e1, 0x000001e1 } },
+	{ AR5K_RF_GAIN(9),	{ 0x00000021, 0x00000021 } },
+	{ AR5K_RF_GAIN(10),	{ 0x00000061, 0x00000061 } },
+	{ AR5K_RF_GAIN(11),	{ 0x00000162, 0x00000162 } },
+	{ AR5K_RF_GAIN(12),	{ 0x000001a2, 0x000001a2 } },
+	{ AR5K_RF_GAIN(13),	{ 0x000001e2, 0x000001e2 } },
+	{ AR5K_RF_GAIN(14),	{ 0x00000022, 0x00000022 } },
+	{ AR5K_RF_GAIN(15),	{ 0x00000062, 0x00000062 } },
+	{ AR5K_RF_GAIN(16),	{ 0x00000163, 0x00000163 } },
+	{ AR5K_RF_GAIN(17),	{ 0x000001a3, 0x000001a3 } },
+	{ AR5K_RF_GAIN(18),	{ 0x000001e3, 0x000001e3 } },
+	{ AR5K_RF_GAIN(19),	{ 0x00000023, 0x00000023 } },
+	{ AR5K_RF_GAIN(20),	{ 0x00000063, 0x00000063 } },
+	{ AR5K_RF_GAIN(21),	{ 0x00000184, 0x00000184 } },
+	{ AR5K_RF_GAIN(22),	{ 0x000001c4, 0x000001c4 } },
+	{ AR5K_RF_GAIN(23),	{ 0x00000004, 0x00000004 } },
+	{ AR5K_RF_GAIN(24),	{ 0x000001ea, 0x0000000b } },
+	{ AR5K_RF_GAIN(25),	{ 0x0000002a, 0x0000004b } },
+	{ AR5K_RF_GAIN(26),	{ 0x0000006a, 0x0000008b } },
+	{ AR5K_RF_GAIN(27),	{ 0x000000aa, 0x000001ac } },
+	{ AR5K_RF_GAIN(28),	{ 0x000001ab, 0x000001ec } },
+	{ AR5K_RF_GAIN(29),	{ 0x000001eb, 0x0000002c } },
+	{ AR5K_RF_GAIN(30),	{ 0x0000002b, 0x00000012 } },
+	{ AR5K_RF_GAIN(31),	{ 0x0000006b, 0x00000052 } },
+	{ AR5K_RF_GAIN(32),	{ 0x000000ab, 0x00000092 } },
+	{ AR5K_RF_GAIN(33),	{ 0x000001ac, 0x00000193 } },
+	{ AR5K_RF_GAIN(34),	{ 0x000001ec, 0x000001d3 } },
+	{ AR5K_RF_GAIN(35),	{ 0x0000002c, 0x00000013 } },
+	{ AR5K_RF_GAIN(36),	{ 0x0000003a, 0x00000053 } },
+	{ AR5K_RF_GAIN(37),	{ 0x0000007a, 0x00000093 } },
+	{ AR5K_RF_GAIN(38),	{ 0x000000ba, 0x00000194 } },
+	{ AR5K_RF_GAIN(39),	{ 0x000001bb, 0x000001d4 } },
+	{ AR5K_RF_GAIN(40),	{ 0x000001fb, 0x00000014 } },
+	{ AR5K_RF_GAIN(41),	{ 0x0000003b, 0x0000003a } },
+	{ AR5K_RF_GAIN(42),	{ 0x0000007b, 0x0000007a } },
+	{ AR5K_RF_GAIN(43),	{ 0x000000bb, 0x000000ba } },
+	{ AR5K_RF_GAIN(44),	{ 0x000001bc, 0x000001bb } },
+	{ AR5K_RF_GAIN(45),	{ 0x000001fc, 0x000001fb } },
+	{ AR5K_RF_GAIN(46),	{ 0x0000003c, 0x0000003b } },
+	{ AR5K_RF_GAIN(47),	{ 0x0000007c, 0x0000007b } },
+	{ AR5K_RF_GAIN(48),	{ 0x000000bc, 0x000000bb } },
+	{ AR5K_RF_GAIN(49),	{ 0x000000fc, 0x000001bc } },
+	{ AR5K_RF_GAIN(50),	{ 0x000000fc, 0x000001fc } },
+	{ AR5K_RF_GAIN(51),	{ 0x000000fc, 0x0000003c } },
+	{ AR5K_RF_GAIN(52),	{ 0x000000fc, 0x0000007c } },
+	{ AR5K_RF_GAIN(53),	{ 0x000000fc, 0x000000bc } },
+	{ AR5K_RF_GAIN(54),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(55),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(56),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(57),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(58),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(59),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(60),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(61),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(62),	{ 0x000000fc, 0x000000fc } },
+	{ AR5K_RF_GAIN(63),	{ 0x000000fc, 0x000000fc } },
+};
+
+/* Initial RF Gain settings for RF2413 */
+static const struct ath5k_ini_rfgain rfgain_2413[] = {
+	{ AR5K_RF_GAIN(0),	{ 0x00000000, 0x00000000 } },
+	{ AR5K_RF_GAIN(1),	{ 0x00000000, 0x00000040 } },
+	{ AR5K_RF_GAIN(2),	{ 0x00000000, 0x00000080 } },
+	{ AR5K_RF_GAIN(3),	{ 0x00000000, 0x00000181 } },
+	{ AR5K_RF_GAIN(4),	{ 0x00000000, 0x000001c1 } },
+	{ AR5K_RF_GAIN(5),	{ 0x00000000, 0x00000001 } },
+	{ AR5K_RF_GAIN(6),	{ 0x00000000, 0x00000041 } },
+	{ AR5K_RF_GAIN(7),	{ 0x00000000, 0x00000081 } },
+	{ AR5K_RF_GAIN(8),	{ 0x00000000, 0x00000168 } },
+	{ AR5K_RF_GAIN(9),	{ 0x00000000, 0x000001a8 } },
+	{ AR5K_RF_GAIN(10),	{ 0x00000000, 0x000001e8 } },
+	{ AR5K_RF_GAIN(11),	{ 0x00000000, 0x00000028 } },
+	{ AR5K_RF_GAIN(12),	{ 0x00000000, 0x00000068 } },
+	{ AR5K_RF_GAIN(13),	{ 0x00000000, 0x00000189 } },
+	{ AR5K_RF_GAIN(14),	{ 0x00000000, 0x000001c9 } },
+	{ AR5K_RF_GAIN(15),	{ 0x00000000, 0x00000009 } },
+	{ AR5K_RF_GAIN(16),	{ 0x00000000, 0x00000049 } },
+	{ AR5K_RF_GAIN(17),	{ 0x00000000, 0x00000089 } },
+	{ AR5K_RF_GAIN(18),	{ 0x00000000, 0x00000190 } },
+	{ AR5K_RF_GAIN(19),	{ 0x00000000, 0x000001d0 } },
+	{ AR5K_RF_GAIN(20),	{ 0x00000000, 0x00000010 } },
+	{ AR5K_RF_GAIN(21),	{ 0x00000000, 0x00000050 } },
+	{ AR5K_RF_GAIN(22),	{ 0x00000000, 0x00000090 } },
+	{ AR5K_RF_GAIN(23),	{ 0x00000000, 0x00000191 } },
+	{ AR5K_RF_GAIN(24),	{ 0x00000000, 0x000001d1 } },
+	{ AR5K_RF_GAIN(25),	{ 0x00000000, 0x00000011 } },
+	{ AR5K_RF_GAIN(26),	{ 0x00000000, 0x00000051 } },
+	{ AR5K_RF_GAIN(27),	{ 0x00000000, 0x00000091 } },
+	{ AR5K_RF_GAIN(28),	{ 0x00000000, 0x00000178 } },
+	{ AR5K_RF_GAIN(29),	{ 0x00000000, 0x000001b8 } },
+	{ AR5K_RF_GAIN(30),	{ 0x00000000, 0x000001f8 } },
+	{ AR5K_RF_GAIN(31),	{ 0x00000000, 0x00000038 } },
+	{ AR5K_RF_GAIN(32),	{ 0x00000000, 0x00000078 } },
+	{ AR5K_RF_GAIN(33),	{ 0x00000000, 0x00000199 } },
+	{ AR5K_RF_GAIN(34),	{ 0x00000000, 0x000001d9 } },
+	{ AR5K_RF_GAIN(35),	{ 0x00000000, 0x00000019 } },
+	{ AR5K_RF_GAIN(36),	{ 0x00000000, 0x00000059 } },
+	{ AR5K_RF_GAIN(37),	{ 0x00000000, 0x00000099 } },
+	{ AR5K_RF_GAIN(38),	{ 0x00000000, 0x000000d9 } },
+	{ AR5K_RF_GAIN(39),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(40),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(41),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(42),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(43),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(44),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(45),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(46),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(47),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(48),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(49),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(50),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(51),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(52),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(53),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(54),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(55),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(56),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(57),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(58),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(59),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(60),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(61),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(62),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(63),	{ 0x00000000, 0x000000f9 } },
+};
+
+/* Initial RF Gain settings for AR2316 */
+static const struct ath5k_ini_rfgain rfgain_2316[] = {
+	{ AR5K_RF_GAIN(0),	{ 0x00000000, 0x00000000 } },
+	{ AR5K_RF_GAIN(1),	{ 0x00000000, 0x00000040 } },
+	{ AR5K_RF_GAIN(2),	{ 0x00000000, 0x00000080 } },
+	{ AR5K_RF_GAIN(3),	{ 0x00000000, 0x000000c0 } },
+	{ AR5K_RF_GAIN(4),	{ 0x00000000, 0x000000e0 } },
+	{ AR5K_RF_GAIN(5),	{ 0x00000000, 0x000000e0 } },
+	{ AR5K_RF_GAIN(6),	{ 0x00000000, 0x00000128 } },
+	{ AR5K_RF_GAIN(7),	{ 0x00000000, 0x00000128 } },
+	{ AR5K_RF_GAIN(8),	{ 0x00000000, 0x00000128 } },
+	{ AR5K_RF_GAIN(9),	{ 0x00000000, 0x00000168 } },
+	{ AR5K_RF_GAIN(10),	{ 0x00000000, 0x000001a8 } },
+	{ AR5K_RF_GAIN(11),	{ 0x00000000, 0x000001e8 } },
+	{ AR5K_RF_GAIN(12),	{ 0x00000000, 0x00000028 } },
+	{ AR5K_RF_GAIN(13),	{ 0x00000000, 0x00000068 } },
+	{ AR5K_RF_GAIN(14),	{ 0x00000000, 0x000000a8 } },
+	{ AR5K_RF_GAIN(15),	{ 0x00000000, 0x000000e8 } },
+	{ AR5K_RF_GAIN(16),	{ 0x00000000, 0x000000e8 } },
+	{ AR5K_RF_GAIN(17),	{ 0x00000000, 0x00000130 } },
+	{ AR5K_RF_GAIN(18),	{ 0x00000000, 0x00000130 } },
+	{ AR5K_RF_GAIN(19),	{ 0x00000000, 0x00000170 } },
+	{ AR5K_RF_GAIN(20),	{ 0x00000000, 0x000001b0 } },
+	{ AR5K_RF_GAIN(21),	{ 0x00000000, 0x000001f0 } },
+	{ AR5K_RF_GAIN(22),	{ 0x00000000, 0x00000030 } },
+	{ AR5K_RF_GAIN(23),	{ 0x00000000, 0x00000070 } },
+	{ AR5K_RF_GAIN(24),	{ 0x00000000, 0x000000b0 } },
+	{ AR5K_RF_GAIN(25),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(26),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(27),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(28),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(29),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(30),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(31),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(32),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(33),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(34),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(35),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(36),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(37),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(38),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(39),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(40),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(41),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(42),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(43),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(44),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(45),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(46),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(47),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(48),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(49),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(50),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(51),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(52),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(53),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(54),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(55),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(56),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(57),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(58),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(59),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(60),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(61),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(62),	{ 0x00000000, 0x000000f0 } },
+	{ AR5K_RF_GAIN(63),	{ 0x00000000, 0x000000f0 } },
+};
+
+
+/* Initial RF Gain settings for RF5413 */
+static const struct ath5k_ini_rfgain rfgain_5413[] = {
+	/*			      5Ghz	2Ghz	*/
+	{ AR5K_RF_GAIN(0),	{ 0x00000000, 0x00000000 } },
+	{ AR5K_RF_GAIN(1),	{ 0x00000040, 0x00000040 } },
+	{ AR5K_RF_GAIN(2),	{ 0x00000080, 0x00000080 } },
+	{ AR5K_RF_GAIN(3),	{ 0x000001a1, 0x00000161 } },
+	{ AR5K_RF_GAIN(4),	{ 0x000001e1, 0x000001a1 } },
+	{ AR5K_RF_GAIN(5),	{ 0x00000021, 0x000001e1 } },
+	{ AR5K_RF_GAIN(6),	{ 0x00000061, 0x00000021 } },
+	{ AR5K_RF_GAIN(7),	{ 0x00000188, 0x00000061 } },
+	{ AR5K_RF_GAIN(8),	{ 0x000001c8, 0x00000188 } },
+	{ AR5K_RF_GAIN(9),	{ 0x00000008, 0x000001c8 } },
+	{ AR5K_RF_GAIN(10),	{ 0x00000048, 0x00000008 } },
+	{ AR5K_RF_GAIN(11),	{ 0x00000088, 0x00000048 } },
+	{ AR5K_RF_GAIN(12),	{ 0x000001a9, 0x00000088 } },
+	{ AR5K_RF_GAIN(13),	{ 0x000001e9, 0x00000169 } },
+	{ AR5K_RF_GAIN(14),	{ 0x00000029, 0x000001a9 } },
+	{ AR5K_RF_GAIN(15),	{ 0x00000069, 0x000001e9 } },
+	{ AR5K_RF_GAIN(16),	{ 0x000001d0, 0x00000029 } },
+	{ AR5K_RF_GAIN(17),	{ 0x00000010, 0x00000069 } },
+	{ AR5K_RF_GAIN(18),	{ 0x00000050, 0x00000190 } },
+	{ AR5K_RF_GAIN(19),	{ 0x00000090, 0x000001d0 } },
+	{ AR5K_RF_GAIN(20),	{ 0x000001b1, 0x00000010 } },
+	{ AR5K_RF_GAIN(21),	{ 0x000001f1, 0x00000050 } },
+	{ AR5K_RF_GAIN(22),	{ 0x00000031, 0x00000090 } },
+	{ AR5K_RF_GAIN(23),	{ 0x00000071, 0x00000171 } },
+	{ AR5K_RF_GAIN(24),	{ 0x000001b8, 0x000001b1 } },
+	{ AR5K_RF_GAIN(25),	{ 0x000001f8, 0x000001f1 } },
+	{ AR5K_RF_GAIN(26),	{ 0x00000038, 0x00000031 } },
+	{ AR5K_RF_GAIN(27),	{ 0x00000078, 0x00000071 } },
+	{ AR5K_RF_GAIN(28),	{ 0x00000199, 0x00000198 } },
+	{ AR5K_RF_GAIN(29),	{ 0x000001d9, 0x000001d8 } },
+	{ AR5K_RF_GAIN(30),	{ 0x00000019, 0x00000018 } },
+	{ AR5K_RF_GAIN(31),	{ 0x00000059, 0x00000058 } },
+	{ AR5K_RF_GAIN(32),	{ 0x00000099, 0x00000098 } },
+	{ AR5K_RF_GAIN(33),	{ 0x000000d9, 0x00000179 } },
+	{ AR5K_RF_GAIN(34),	{ 0x000000f9, 0x000001b9 } },
+	{ AR5K_RF_GAIN(35),	{ 0x000000f9, 0x000001f9 } },
+	{ AR5K_RF_GAIN(36),	{ 0x000000f9, 0x00000039 } },
+	{ AR5K_RF_GAIN(37),	{ 0x000000f9, 0x00000079 } },
+	{ AR5K_RF_GAIN(38),	{ 0x000000f9, 0x000000b9 } },
+	{ AR5K_RF_GAIN(39),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(40),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(41),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(42),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(43),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(44),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(45),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(46),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(47),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(48),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(49),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(50),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(51),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(52),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(53),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(54),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(55),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(56),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(57),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(58),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(59),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(60),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(61),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(62),	{ 0x000000f9, 0x000000f9 } },
+	{ AR5K_RF_GAIN(63),	{ 0x000000f9, 0x000000f9 } },
+};
+
+
+/* Initial RF Gain settings for RF2425 */
+static const struct ath5k_ini_rfgain rfgain_2425[] = {
+	{ AR5K_RF_GAIN(0),	{ 0x00000000, 0x00000000 } },
+	{ AR5K_RF_GAIN(1),	{ 0x00000000, 0x00000040 } },
+	{ AR5K_RF_GAIN(2),	{ 0x00000000, 0x00000080 } },
+	{ AR5K_RF_GAIN(3),	{ 0x00000000, 0x00000181 } },
+	{ AR5K_RF_GAIN(4),	{ 0x00000000, 0x000001c1 } },
+	{ AR5K_RF_GAIN(5),	{ 0x00000000, 0x00000001 } },
+	{ AR5K_RF_GAIN(6),	{ 0x00000000, 0x00000041 } },
+	{ AR5K_RF_GAIN(7),	{ 0x00000000, 0x00000081 } },
+	{ AR5K_RF_GAIN(8),	{ 0x00000000, 0x00000188 } },
+	{ AR5K_RF_GAIN(9),	{ 0x00000000, 0x000001c8 } },
+	{ AR5K_RF_GAIN(10),	{ 0x00000000, 0x00000008 } },
+	{ AR5K_RF_GAIN(11),	{ 0x00000000, 0x00000048 } },
+	{ AR5K_RF_GAIN(12),	{ 0x00000000, 0x00000088 } },
+	{ AR5K_RF_GAIN(13),	{ 0x00000000, 0x00000189 } },
+	{ AR5K_RF_GAIN(14),	{ 0x00000000, 0x000001c9 } },
+	{ AR5K_RF_GAIN(15),	{ 0x00000000, 0x00000009 } },
+	{ AR5K_RF_GAIN(16),	{ 0x00000000, 0x00000049 } },
+	{ AR5K_RF_GAIN(17),	{ 0x00000000, 0x00000089 } },
+	{ AR5K_RF_GAIN(18),	{ 0x00000000, 0x000001b0 } },
+	{ AR5K_RF_GAIN(19),	{ 0x00000000, 0x000001f0 } },
+	{ AR5K_RF_GAIN(20),	{ 0x00000000, 0x00000030 } },
+	{ AR5K_RF_GAIN(21),	{ 0x00000000, 0x00000070 } },
+	{ AR5K_RF_GAIN(22),	{ 0x00000000, 0x00000171 } },
+	{ AR5K_RF_GAIN(23),	{ 0x00000000, 0x000001b1 } },
+	{ AR5K_RF_GAIN(24),	{ 0x00000000, 0x000001f1 } },
+	{ AR5K_RF_GAIN(25),	{ 0x00000000, 0x00000031 } },
+	{ AR5K_RF_GAIN(26),	{ 0x00000000, 0x00000071 } },
+	{ AR5K_RF_GAIN(27),	{ 0x00000000, 0x000001b8 } },
+	{ AR5K_RF_GAIN(28),	{ 0x00000000, 0x000001f8 } },
+	{ AR5K_RF_GAIN(29),	{ 0x00000000, 0x00000038 } },
+	{ AR5K_RF_GAIN(30),	{ 0x00000000, 0x00000078 } },
+	{ AR5K_RF_GAIN(31),	{ 0x00000000, 0x000000b8 } },
+	{ AR5K_RF_GAIN(32),	{ 0x00000000, 0x000001b9 } },
+	{ AR5K_RF_GAIN(33),	{ 0x00000000, 0x000001f9 } },
+	{ AR5K_RF_GAIN(34),	{ 0x00000000, 0x00000039 } },
+	{ AR5K_RF_GAIN(35),	{ 0x00000000, 0x00000079 } },
+	{ AR5K_RF_GAIN(36),	{ 0x00000000, 0x000000b9 } },
+	{ AR5K_RF_GAIN(37),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(38),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(39),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(40),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(41),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(42),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(43),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(44),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(45),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(46),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(47),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(48),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(49),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(50),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(51),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(52),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(53),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(54),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(55),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(56),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(57),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(58),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(59),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(60),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(61),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(62),	{ 0x00000000, 0x000000f9 } },
+	{ AR5K_RF_GAIN(63),	{ 0x00000000, 0x000000f9 } },
+};
+
+#define AR5K_GAIN_CRN_FIX_BITS_5111		4
+#define AR5K_GAIN_CRN_FIX_BITS_5112		7
+#define AR5K_GAIN_CRN_MAX_FIX_BITS		AR5K_GAIN_CRN_FIX_BITS_5112
+#define AR5K_GAIN_DYN_ADJUST_HI_MARGIN		15
+#define AR5K_GAIN_DYN_ADJUST_LO_MARGIN		20
+#define AR5K_GAIN_CCK_PROBE_CORR		5
+#define AR5K_GAIN_CCK_OFDM_GAIN_DELTA		15
+#define AR5K_GAIN_STEP_COUNT			10
+
+/* Check if our current measurement is inside our
+ * current variable attenuation window */
+#define AR5K_GAIN_CHECK_ADJUST(_g) 		\
+	((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high)
+
+struct ath5k_gain_opt_step {
+	s8				gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS];
+	s8				gos_gain;
+};
+
+struct ath5k_gain_opt {
+	u8				go_default;
+	u8				go_steps_count;
+	const struct ath5k_gain_opt_step	go_step[AR5K_GAIN_STEP_COUNT];
+};
+
+/*
+ * Parameters on gos_param:
+ * 1) Tx clip PHY register
+ * 2) PWD 90 RF register
+ * 3) PWD 84 RF register
+ * 4) RFGainSel RF register
+ */
+static const struct ath5k_gain_opt rfgain_opt_5111 = {
+	4,
+	9,
+	{
+		{ { 4, 1, 1, 1 }, 6 },
+		{ { 4, 0, 1, 1 }, 4 },
+		{ { 3, 1, 1, 1 }, 3 },
+		{ { 4, 0, 0, 1 }, 1 },
+		{ { 4, 1, 1, 0 }, 0 },
+		{ { 4, 0, 1, 0 }, -2 },
+		{ { 3, 1, 1, 0 }, -3 },
+		{ { 4, 0, 0, 0 }, -4 },
+		{ { 2, 1, 1, 0 }, -6 }
+	}
+};
+
+/*
+ * Parameters on gos_param:
+ * 1) Mixgain ovr RF register
+ * 2) PWD 138 RF register
+ * 3) PWD 137 RF register
+ * 4) PWD 136 RF register
+ * 5) PWD 132 RF register
+ * 6) PWD 131 RF register
+ * 7) PWD 130 RF register
+ */
+static const struct ath5k_gain_opt rfgain_opt_5112 = {
+	1,
+	8,
+	{
+		{ { 3, 0, 0, 0, 0, 0, 0 }, 6 },
+		{ { 2, 0, 0, 0, 0, 0, 0 }, 0 },
+		{ { 1, 0, 0, 0, 0, 0, 0 }, -3 },
+		{ { 0, 0, 0, 0, 0, 0, 0 }, -6 },
+		{ { 0, 1, 1, 0, 0, 0, 0 }, -8 },
+		{ { 0, 1, 1, 0, 1, 1, 0 }, -10 },
+		{ { 0, 1, 0, 1, 1, 1, 0 }, -13 },
+		{ { 0, 1, 0, 1, 1, 0, 1 }, -16 },
+	}
+};
+
diff --git a/gpxe/src/drivers/net/atl1e.c b/gpxe/src/drivers/net/atl1e.c
new file mode 100644
index 0000000..6c0b050
--- /dev/null
+++ b/gpxe/src/drivers/net/atl1e.c
@@ -0,0 +1,1758 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ *
+ * Modified for gPXE, October 2009 by Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include "atl1e.h"
+
+/* User-tweakable parameters: */
+#define TX_DESC_COUNT	32	/* TX descriptors, minimum 32 */
+#define RX_MEM_SIZE	8192	/* RX area size, minimum 8kb */
+#define MAX_FRAME_SIZE	1500	/* Maximum MTU supported, minimum 1500 */
+
+/* Arcane parameters: */
+#define PREAMBLE_LEN	7
+#define RX_JUMBO_THRESH	((MAX_FRAME_SIZE + ETH_HLEN + \
+			  VLAN_HLEN + ETH_FCS_LEN + 7) >> 3)
+#define IMT_VAL		100	/* interrupt moderator timer, us */
+#define ICT_VAL		50000	/* interrupt clear timer, us */
+#define SMB_TIMER	200000
+#define RRD_THRESH	1	/* packets to queue before interrupt */
+#define TPD_BURST	5
+#define TPD_THRESH	(TX_DESC_COUNT / 2)
+#define RX_COUNT_DOWN	4
+#define TX_COUNT_DOWN	(IMT_VAL * 4 / 3)
+#define DMAR_DLY_CNT	15
+#define DMAW_DLY_CNT	4
+
+#define PCI_DEVICE_ID_ATTANSIC_L1E      0x1026
+
+/*
+ * atl1e_pci_tbl - PCI Device ID Table
+ *
+ * Wildcard entries (PCI_ANY_ID) should come last
+ * Last entry must be all 0s
+ *
+ * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
+ *   Class, Class Mask, private data (not used) }
+ */
+static struct pci_device_id atl1e_pci_tbl[] = {
+	PCI_ROM(0x1969, 0x1026, "atl1e_26", "Attansic L1E 0x1026", 0),
+	PCI_ROM(0x1969, 0x1066, "atl1e_66", "Attansic L1E 0x1066", 0),
+};
+
+static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter);
+
+static const u16
+atl1e_rx_page_vld_regs[AT_PAGE_NUM_PER_QUEUE] =
+{
+	REG_HOST_RXF0_PAGE0_VLD, REG_HOST_RXF0_PAGE1_VLD
+};
+
+static const u16
+atl1e_rx_page_lo_addr_regs[AT_PAGE_NUM_PER_QUEUE] =
+{
+	REG_HOST_RXF0_PAGE0_LO, REG_HOST_RXF0_PAGE1_LO
+};
+
+static const u16
+atl1e_rx_page_write_offset_regs[AT_PAGE_NUM_PER_QUEUE] =
+{
+	REG_HOST_RXF0_MB0_LO,  REG_HOST_RXF0_MB1_LO
+};
+
+static const u16 atl1e_pay_load_size[] = {
+	128, 256, 512, 1024, 2048, 4096,
+};
+
+/*
+ * atl1e_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
+ */
+static inline void atl1e_irq_enable(struct atl1e_adapter *adapter)
+{
+	AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
+	AT_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK);
+	AT_WRITE_FLUSH(&adapter->hw);
+}
+
+/*
+ * atl1e_irq_disable - Mask off interrupt generation on the NIC
+ * @adapter: board private structure
+ */
+static inline void atl1e_irq_disable(struct atl1e_adapter *adapter)
+{
+	AT_WRITE_REG(&adapter->hw, REG_IMR, 0);
+	AT_WRITE_FLUSH(&adapter->hw);
+}
+
+/*
+ * atl1e_irq_reset - reset interrupt confiure on the NIC
+ * @adapter: board private structure
+ */
+static inline void atl1e_irq_reset(struct atl1e_adapter *adapter)
+{
+	AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
+	AT_WRITE_REG(&adapter->hw, REG_IMR, 0);
+	AT_WRITE_FLUSH(&adapter->hw);
+}
+
+static void atl1e_reset(struct atl1e_adapter *adapter)
+{
+	atl1e_down(adapter);
+	atl1e_up(adapter);
+}
+
+static int atl1e_check_link(struct atl1e_adapter *adapter)
+{
+	struct atl1e_hw *hw = &adapter->hw;
+	struct net_device *netdev = adapter->netdev;
+	int err = 0;
+	u16 speed, duplex, phy_data;
+
+	/* MII_BMSR must read twise */
+	atl1e_read_phy_reg(hw, MII_BMSR, &phy_data);
+	atl1e_read_phy_reg(hw, MII_BMSR, &phy_data);
+
+	if ((phy_data & BMSR_LSTATUS) == 0) {
+		/* link down */
+		if (netdev_link_ok(netdev)) { /* old link state: Up */
+			u32 value;
+			/* disable rx */
+			value = AT_READ_REG(hw, REG_MAC_CTRL);
+			value &= ~MAC_CTRL_RX_EN;
+			AT_WRITE_REG(hw, REG_MAC_CTRL, value);
+			adapter->link_speed = SPEED_0;
+
+			DBG("atl1e: %s link is down\n", netdev->name);
+			netdev_link_down(netdev);
+		}
+	} else {
+		/* Link Up */
+		err = atl1e_get_speed_and_duplex(hw, &speed, &duplex);
+		if (err)
+			return err;
+
+		/* link result is our setting */
+		if (adapter->link_speed != speed ||
+		    adapter->link_duplex != duplex) {
+			adapter->link_speed  = speed;
+			adapter->link_duplex = duplex;
+			atl1e_setup_mac_ctrl(adapter);
+
+			DBG("atl1e: %s link is up, %d Mbps, %s duplex\n",
+			    netdev->name, adapter->link_speed,
+			    adapter->link_duplex == FULL_DUPLEX ?
+			    "full" : "half");
+			netdev_link_up(netdev);
+		}
+	}
+	return 0;
+}
+
+static int atl1e_mdio_read(struct net_device *netdev, int phy_id __unused,
+			   int reg_num)
+{
+	struct atl1e_adapter *adapter = netdev_priv(netdev);
+	u16 result;
+
+	atl1e_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result);
+	return result;
+}
+
+static void atl1e_mdio_write(struct net_device *netdev, int phy_id __unused,
+			     int reg_num, int val)
+{
+	struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+	atl1e_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val);
+}
+
+static void atl1e_setup_pcicmd(struct pci_device *pdev)
+{
+	u16 cmd;
+
+	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+	cmd |=  (PCI_COMMAND_MEM | PCI_COMMAND_MASTER);
+	pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+	/*
+	 * some motherboards BIOS(PXE/EFI) driver may set PME
+	 * while they transfer control to OS (Windows/Linux)
+	 * so we should clear this bit before NIC work normally
+	 */
+	pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0);
+	mdelay(1);
+}
+
+/*
+ * atl1e_sw_init - Initialize general software structures (struct atl1e_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * atl1e_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ */
+static int atl1e_sw_init(struct atl1e_adapter *adapter)
+{
+	struct atl1e_hw *hw = &adapter->hw;
+	struct pci_device *pdev = adapter->pdev;
+	u32 phy_status_data = 0;
+	u8 rev_id = 0;
+
+	adapter->link_speed = SPEED_0;   /* hardware init */
+	adapter->link_duplex = FULL_DUPLEX;
+
+	/* PCI config space info */
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id);
+
+	phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS);
+	/* nic type */
+	if (rev_id >= 0xF0) {
+		hw->nic_type = athr_l2e_revB;
+	} else {
+		if (phy_status_data & PHY_STATUS_100M)
+			hw->nic_type = athr_l1e;
+		else
+			hw->nic_type = athr_l2e_revA;
+	}
+
+	phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS);
+
+	hw->emi_ca = !!(phy_status_data & PHY_STATUS_EMI_CA);
+
+	hw->phy_configured = 0;
+
+	/* need confirm */
+
+	hw->dmar_block = atl1e_dma_req_1024;
+	hw->dmaw_block = atl1e_dma_req_1024;
+
+	netdev_link_down(adapter->netdev);
+
+	return 0;
+}
+
+/*
+ * atl1e_clean_tx_ring - free all Tx buffers for device close
+ * @adapter: board private structure
+ */
+static void atl1e_clean_tx_ring(struct atl1e_adapter *adapter)
+{
+	struct atl1e_tx_ring *tx_ring = (struct atl1e_tx_ring *)
+				&adapter->tx_ring;
+	struct atl1e_tx_buffer *tx_buffer = NULL;
+	u16 index, ring_count = tx_ring->count;
+
+	if (tx_ring->desc == NULL || tx_ring->tx_buffer == NULL)
+		return;
+
+	for (index = 0; index < ring_count; index++) {
+		tx_buffer = &tx_ring->tx_buffer[index];
+		if (tx_buffer->iob) {
+			netdev_tx_complete(adapter->netdev, tx_buffer->iob);
+			tx_buffer->dma = 0;
+			tx_buffer->iob = NULL;
+		}
+	}
+
+	/* Zero out Tx-buffers */
+	memset(tx_ring->desc, 0, sizeof(struct atl1e_tpd_desc) *
+	       ring_count);
+	memset(tx_ring->tx_buffer, 0, sizeof(struct atl1e_tx_buffer) *
+	       ring_count);
+}
+
+/*
+ * atl1e_clean_rx_ring - Free rx-reservation iobs
+ * @adapter: board private structure
+ */
+static void atl1e_clean_rx_ring(struct atl1e_adapter *adapter)
+{
+	struct atl1e_rx_ring *rx_ring =
+		(struct atl1e_rx_ring *)&adapter->rx_ring;
+	struct atl1e_rx_page_desc *rx_page_desc = &rx_ring->rx_page_desc;
+	u16 j;
+
+	if (adapter->ring_vir_addr == NULL)
+		return;
+
+	/* Zero out the descriptor ring */
+	for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+		if (rx_page_desc->rx_page[j].addr != NULL) {
+			memset(rx_page_desc->rx_page[j].addr, 0,
+			       rx_ring->real_page_size);
+		}
+	}
+}
+
+static void atl1e_cal_ring_size(struct atl1e_adapter *adapter, u32 *ring_size)
+{
+	*ring_size = ((u32)(adapter->tx_ring.count *
+		     sizeof(struct atl1e_tpd_desc) + 7
+			/* tx ring, qword align */
+		     + adapter->rx_ring.real_page_size * AT_PAGE_NUM_PER_QUEUE
+		     + 31
+			/* rx ring,  32 bytes align */
+		     + (1 + AT_PAGE_NUM_PER_QUEUE) *
+			sizeof(u32) + 3));
+			/* tx, rx cmd, dword align   */
+}
+
+static void atl1e_init_ring_resources(struct atl1e_adapter *adapter)
+{
+	struct atl1e_tx_ring *tx_ring = NULL;
+	struct atl1e_rx_ring *rx_ring = NULL;
+
+	tx_ring = &adapter->tx_ring;
+	rx_ring = &adapter->rx_ring;
+
+	rx_ring->real_page_size = adapter->rx_ring.page_size
+				 + MAX_FRAME_SIZE
+				 + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN;
+	rx_ring->real_page_size = (rx_ring->real_page_size + 31) & ~31;
+	atl1e_cal_ring_size(adapter, &adapter->ring_size);
+
+	adapter->ring_vir_addr = NULL;
+	adapter->rx_ring.desc = NULL;
+
+	return;
+}
+
+/*
+ * Read / Write Ptr Initialize:
+ */
+static void atl1e_init_ring_ptrs(struct atl1e_adapter *adapter)
+{
+	struct atl1e_tx_ring *tx_ring = NULL;
+	struct atl1e_rx_ring *rx_ring = NULL;
+	struct atl1e_rx_page_desc *rx_page_desc = NULL;
+	int j;
+
+	tx_ring = &adapter->tx_ring;
+	rx_ring = &adapter->rx_ring;
+	rx_page_desc = &rx_ring->rx_page_desc;
+
+	tx_ring->next_to_use = 0;
+	tx_ring->next_to_clean = 0;
+
+	rx_page_desc->rx_using  = 0;
+	rx_page_desc->rx_nxseq = 0;
+	for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+		*rx_page_desc->rx_page[j].write_offset_addr = 0;
+		rx_page_desc->rx_page[j].read_offset = 0;
+	}
+}
+
+/*
+ * atl1e_free_ring_resources - Free Tx / RX descriptor Resources
+ * @adapter: board private structure
+ *
+ * Free all transmit software resources
+ */
+static void atl1e_free_ring_resources(struct atl1e_adapter *adapter)
+{
+	atl1e_clean_tx_ring(adapter);
+	atl1e_clean_rx_ring(adapter);
+
+	if (adapter->ring_vir_addr) {
+		free_dma(adapter->ring_vir_addr, adapter->ring_size);
+		adapter->ring_vir_addr = NULL;
+		adapter->ring_dma = 0;
+	}
+
+	if (adapter->tx_ring.tx_buffer) {
+		free(adapter->tx_ring.tx_buffer);
+		adapter->tx_ring.tx_buffer = NULL;
+	}
+}
+
+/*
+ * atl1e_setup_mem_resources - allocate Tx / RX descriptor resources
+ * @adapter: board private structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int atl1e_setup_ring_resources(struct atl1e_adapter *adapter)
+{
+	struct atl1e_tx_ring *tx_ring;
+	struct atl1e_rx_ring *rx_ring;
+	struct atl1e_rx_page_desc  *rx_page_desc;
+	int size, j;
+	u32 offset = 0;
+	int err = 0;
+
+	if (adapter->ring_vir_addr != NULL)
+		return 0; /* alloced already */
+
+	tx_ring = &adapter->tx_ring;
+	rx_ring = &adapter->rx_ring;
+
+	/* real ring DMA buffer */
+
+	size = adapter->ring_size;
+	adapter->ring_vir_addr = malloc_dma(adapter->ring_size, 32);
+
+	if (adapter->ring_vir_addr == NULL) {
+		DBG("atl1e: out of memory allocating %d bytes for %s ring\n",
+		    adapter->ring_size, adapter->netdev->name);
+		return -ENOMEM;
+	}
+
+	adapter->ring_dma = virt_to_bus(adapter->ring_vir_addr);
+	memset(adapter->ring_vir_addr, 0, adapter->ring_size);
+
+	rx_page_desc = &rx_ring->rx_page_desc;
+
+	/* Init TPD Ring */
+	tx_ring->dma = (adapter->ring_dma + 7) & ~7;
+	offset = tx_ring->dma - adapter->ring_dma;
+	tx_ring->desc = (struct atl1e_tpd_desc *)
+			(adapter->ring_vir_addr + offset);
+	size = sizeof(struct atl1e_tx_buffer) * (tx_ring->count);
+	tx_ring->tx_buffer = zalloc(size);
+	if (tx_ring->tx_buffer == NULL) {
+		DBG("atl1e: out of memory allocating %d bytes for %s txbuf\n",
+		    size, adapter->netdev->name);
+		err = -ENOMEM;
+		goto failed;
+	}
+
+	/* Init RXF-Pages */
+	offset += (sizeof(struct atl1e_tpd_desc) * tx_ring->count);
+	offset = (offset + 31) & ~31;
+
+	for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+		rx_page_desc->rx_page[j].dma =
+			adapter->ring_dma + offset;
+		rx_page_desc->rx_page[j].addr =
+			adapter->ring_vir_addr + offset;
+		offset += rx_ring->real_page_size;
+	}
+
+	/* Init CMB dma address */
+	tx_ring->cmb_dma = adapter->ring_dma + offset;
+	tx_ring->cmb     = (u32 *)(adapter->ring_vir_addr + offset);
+	offset += sizeof(u32);
+
+	for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+		rx_page_desc->rx_page[j].write_offset_dma =
+			adapter->ring_dma + offset;
+		rx_page_desc->rx_page[j].write_offset_addr =
+			adapter->ring_vir_addr + offset;
+		offset += sizeof(u32);
+	}
+
+	if (offset > adapter->ring_size) {
+		DBG("atl1e: ring miscalculation! need %d > %d bytes\n",
+		    offset, adapter->ring_size);
+		err = -EINVAL;
+		goto failed;
+	}
+
+	return 0;
+failed:
+	atl1e_free_ring_resources(adapter);
+	return err;
+}
+
+static inline void atl1e_configure_des_ring(const struct atl1e_adapter *adapter)
+{
+
+	struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw;
+	struct atl1e_rx_ring *rx_ring =
+			(struct atl1e_rx_ring *)&adapter->rx_ring;
+	struct atl1e_tx_ring *tx_ring =
+			(struct atl1e_tx_ring *)&adapter->tx_ring;
+	struct atl1e_rx_page_desc *rx_page_desc = NULL;
+	int j;
+
+	AT_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI, 0);
+	AT_WRITE_REG(hw, REG_TPD_BASE_ADDR_LO, tx_ring->dma);
+	AT_WRITE_REG(hw, REG_TPD_RING_SIZE, (u16)(tx_ring->count));
+	AT_WRITE_REG(hw, REG_HOST_TX_CMB_LO, tx_ring->cmb_dma);
+
+	rx_page_desc = &rx_ring->rx_page_desc;
+
+	/* RXF Page Physical address / Page Length */
+	AT_WRITE_REG(hw, REG_RXF0_BASE_ADDR_HI, 0);
+
+	for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) {
+		u32 page_phy_addr;
+		u32 offset_phy_addr;
+
+		page_phy_addr = rx_page_desc->rx_page[j].dma;
+		offset_phy_addr = rx_page_desc->rx_page[j].write_offset_dma;
+
+		AT_WRITE_REG(hw, atl1e_rx_page_lo_addr_regs[j], page_phy_addr);
+		AT_WRITE_REG(hw, atl1e_rx_page_write_offset_regs[j],
+			     offset_phy_addr);
+		AT_WRITE_REGB(hw, atl1e_rx_page_vld_regs[j], 1);
+	}
+
+	/* Page Length */
+	AT_WRITE_REG(hw, REG_HOST_RXFPAGE_SIZE, rx_ring->page_size);
+	/* Load all of base address above */
+	AT_WRITE_REG(hw, REG_LOAD_PTR, 1);
+
+	return;
+}
+
+static inline void atl1e_configure_tx(struct atl1e_adapter *adapter)
+{
+	struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw;
+	u32 dev_ctrl_data = 0;
+	u32 max_pay_load = 0;
+	u32 jumbo_thresh = 0;
+	u32 extra_size = 0;     /* Jumbo frame threshold in QWORD unit */
+
+	/* configure TXQ param */
+	if (hw->nic_type != athr_l2e_revB) {
+		extra_size = ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN;
+		jumbo_thresh = MAX_FRAME_SIZE + extra_size;
+		AT_WRITE_REG(hw, REG_TX_EARLY_TH, (jumbo_thresh + 7) >> 3);
+	}
+
+	dev_ctrl_data = AT_READ_REG(hw, REG_DEVICE_CTRL);
+
+	max_pay_load  = ((dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT)) &
+			DEVICE_CTRL_MAX_PAYLOAD_MASK;
+	if (max_pay_load < hw->dmaw_block)
+		hw->dmaw_block = max_pay_load;
+
+	max_pay_load  = ((dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT)) &
+			DEVICE_CTRL_MAX_RREQ_SZ_MASK;
+	if (max_pay_load < hw->dmar_block)
+		hw->dmar_block = max_pay_load;
+
+	if (hw->nic_type != athr_l2e_revB)
+		AT_WRITE_REGW(hw, REG_TXQ_CTRL + 2,
+			      atl1e_pay_load_size[hw->dmar_block]);
+	/* enable TXQ */
+	AT_WRITE_REGW(hw, REG_TXQ_CTRL,
+			((TPD_BURST & TXQ_CTRL_NUM_TPD_BURST_MASK)
+			 << TXQ_CTRL_NUM_TPD_BURST_SHIFT)
+			| TXQ_CTRL_ENH_MODE | TXQ_CTRL_EN);
+	return;
+}
+
+static inline void atl1e_configure_rx(struct atl1e_adapter *adapter)
+{
+	struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw;
+	u32 rxf_len  = 0;
+	u32 rxf_low  = 0;
+	u32 rxf_high = 0;
+	u32 rxf_thresh_data = 0;
+	u32 rxq_ctrl_data = 0;
+
+	if (hw->nic_type != athr_l2e_revB) {
+		AT_WRITE_REGW(hw, REG_RXQ_JMBOSZ_RRDTIM,
+			      (u16)((RX_JUMBO_THRESH & RXQ_JMBOSZ_TH_MASK) <<
+			      RXQ_JMBOSZ_TH_SHIFT |
+			      (1 & RXQ_JMBO_LKAH_MASK) <<
+			      RXQ_JMBO_LKAH_SHIFT));
+
+		rxf_len  = AT_READ_REG(hw, REG_SRAM_RXF_LEN);
+		rxf_high = rxf_len * 4 / 5;
+		rxf_low  = rxf_len / 5;
+		rxf_thresh_data = ((rxf_high  & RXQ_RXF_PAUSE_TH_HI_MASK)
+				  << RXQ_RXF_PAUSE_TH_HI_SHIFT) |
+				  ((rxf_low & RXQ_RXF_PAUSE_TH_LO_MASK)
+				  << RXQ_RXF_PAUSE_TH_LO_SHIFT);
+
+		AT_WRITE_REG(hw, REG_RXQ_RXF_PAUSE_THRESH, rxf_thresh_data);
+	}
+
+	/* RRS */
+	AT_WRITE_REG(hw, REG_IDT_TABLE, 0);
+	AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, 0);
+
+	rxq_ctrl_data |= RXQ_CTRL_PBA_ALIGN_32 |
+			 RXQ_CTRL_CUT_THRU_EN | RXQ_CTRL_EN;
+
+	AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data);
+	return;
+}
+
+static inline void atl1e_configure_dma(struct atl1e_adapter *adapter)
+{
+	struct atl1e_hw *hw = &adapter->hw;
+	u32 dma_ctrl_data = 0;
+
+	dma_ctrl_data = DMA_CTRL_RXCMB_EN;
+	dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK)
+		<< DMA_CTRL_DMAR_BURST_LEN_SHIFT;
+	dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK)
+		<< DMA_CTRL_DMAW_BURST_LEN_SHIFT;
+	dma_ctrl_data |= DMA_CTRL_DMAR_REQ_PRI | DMA_CTRL_DMAR_OUT_ORDER;
+	dma_ctrl_data |= (DMAR_DLY_CNT & DMA_CTRL_DMAR_DLY_CNT_MASK)
+		<< DMA_CTRL_DMAR_DLY_CNT_SHIFT;
+	dma_ctrl_data |= (DMAW_DLY_CNT & DMA_CTRL_DMAW_DLY_CNT_MASK)
+		<< DMA_CTRL_DMAW_DLY_CNT_SHIFT;
+
+	AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data);
+	return;
+}
+
+static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter)
+{
+	u32 value;
+	struct atl1e_hw *hw = &adapter->hw;
+
+	/* Config MAC CTRL Register */
+	value = MAC_CTRL_TX_EN |
+		MAC_CTRL_RX_EN ;
+
+	if (FULL_DUPLEX == adapter->link_duplex)
+		value |= MAC_CTRL_DUPLX;
+
+	value |= ((u32)((SPEED_1000 == adapter->link_speed) ?
+			  MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) <<
+			  MAC_CTRL_SPEED_SHIFT);
+	value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);
+
+	value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
+	value |= ((PREAMBLE_LEN & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
+
+	value |= MAC_CTRL_BC_EN;
+	value |= MAC_CTRL_MC_ALL_EN;
+
+	AT_WRITE_REG(hw, REG_MAC_CTRL, value);
+}
+
+/*
+ * atl1e_configure - Configure Transmit&Receive Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Tx /Rx unit of the MAC after a reset.
+ */
+static int atl1e_configure(struct atl1e_adapter *adapter)
+{
+	struct atl1e_hw *hw = &adapter->hw;
+	u32 intr_status_data = 0;
+
+	/* clear interrupt status */
+	AT_WRITE_REG(hw, REG_ISR, ~0);
+
+	/* 1. set MAC Address */
+	atl1e_hw_set_mac_addr(hw);
+
+	/* 2. Init the Multicast HASH table (clear) */
+	AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
+	AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
+
+	/* 3. Clear any WOL status */
+	AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
+
+	/* 4. Descripter Ring BaseMem/Length/Read ptr/Write ptr
+	 *    TPD Ring/SMB/RXF0 Page CMBs, they use the same
+	 *    High 32bits memory */
+	atl1e_configure_des_ring(adapter);
+
+	/* 5. set Interrupt Moderator Timer */
+	AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, IMT_VAL);
+	AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER2_INIT, IMT_VAL);
+	AT_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_LED_MODE |
+			MASTER_CTRL_ITIMER_EN | MASTER_CTRL_ITIMER2_EN);
+
+	/* 6. rx/tx threshold to trig interrupt */
+	AT_WRITE_REGW(hw, REG_TRIG_RRD_THRESH, RRD_THRESH);
+	AT_WRITE_REGW(hw, REG_TRIG_TPD_THRESH, TPD_THRESH);
+	AT_WRITE_REGW(hw, REG_TRIG_RXTIMER, RX_COUNT_DOWN);
+	AT_WRITE_REGW(hw, REG_TRIG_TXTIMER, TX_COUNT_DOWN);
+
+	/* 7. set Interrupt Clear Timer */
+	AT_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, ICT_VAL);
+
+	/* 8. set MTU */
+	AT_WRITE_REG(hw, REG_MTU, MAX_FRAME_SIZE + ETH_HLEN +
+			VLAN_HLEN + ETH_FCS_LEN);
+
+	/* 9. config TXQ early tx threshold */
+	atl1e_configure_tx(adapter);
+
+	/* 10. config RXQ */
+	atl1e_configure_rx(adapter);
+
+	/* 11. config  DMA Engine */
+	atl1e_configure_dma(adapter);
+
+	/* 12. smb timer to trig interrupt */
+	AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, SMB_TIMER);
+
+	intr_status_data = AT_READ_REG(hw, REG_ISR);
+	if ((intr_status_data & ISR_PHY_LINKDOWN) != 0) {
+		DBG("atl1e: configure failed, PCIE phy link down\n");
+		return -1;
+	}
+
+	AT_WRITE_REG(hw, REG_ISR, 0x7fffffff);
+	return 0;
+}
+
+static inline void atl1e_clear_phy_int(struct atl1e_adapter *adapter)
+{
+	u16 phy_data;
+
+	atl1e_read_phy_reg(&adapter->hw, MII_INT_STATUS, &phy_data);
+}
+
+static int atl1e_clean_tx_irq(struct atl1e_adapter *adapter)
+{
+	struct atl1e_tx_ring *tx_ring = (struct atl1e_tx_ring *)
+					&adapter->tx_ring;
+	struct atl1e_tx_buffer *tx_buffer = NULL;
+	u16 hw_next_to_clean = AT_READ_REGW(&adapter->hw, REG_TPD_CONS_IDX);
+	u16 next_to_clean = tx_ring->next_to_clean;
+
+	while (next_to_clean != hw_next_to_clean) {
+		tx_buffer = &tx_ring->tx_buffer[next_to_clean];
+
+		tx_buffer->dma = 0;
+		if (tx_buffer->iob) {
+			netdev_tx_complete(adapter->netdev, tx_buffer->iob);
+			tx_buffer->iob = NULL;
+		}
+
+		if (++next_to_clean == tx_ring->count)
+			next_to_clean = 0;
+	}
+
+	tx_ring->next_to_clean = next_to_clean;
+
+	return 1;
+}
+
+static struct atl1e_rx_page *atl1e_get_rx_page(struct atl1e_adapter *adapter)
+{
+	struct atl1e_rx_page_desc *rx_page_desc =
+		(struct atl1e_rx_page_desc *) &adapter->rx_ring.rx_page_desc;
+	u8 rx_using = rx_page_desc->rx_using;
+
+	return (struct atl1e_rx_page *)&(rx_page_desc->rx_page[rx_using]);
+}
+
+static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter)
+{
+	struct net_device *netdev  = adapter->netdev;
+	struct atl1e_rx_ring *rx_ring = (struct atl1e_rx_ring *)
+					 &adapter->rx_ring;
+	struct atl1e_rx_page_desc *rx_page_desc =
+		(struct atl1e_rx_page_desc *) &rx_ring->rx_page_desc;
+	struct io_buffer *iob = NULL;
+	struct atl1e_rx_page *rx_page = atl1e_get_rx_page(adapter);
+	u32 packet_size, write_offset;
+	struct atl1e_recv_ret_status *prrs;
+
+	write_offset = *(rx_page->write_offset_addr);
+	if (rx_page->read_offset >= write_offset)
+		return;
+
+	do {
+		/* get new packet's  rrs */
+		prrs = (struct atl1e_recv_ret_status *) (rx_page->addr +
+							 rx_page->read_offset);
+		/* check sequence number */
+		if (prrs->seq_num != rx_page_desc->rx_nxseq) {
+			DBG("atl1e %s: RX sequence number error (%d != %d)\n",
+			    netdev->name, prrs->seq_num,
+			    rx_page_desc->rx_nxseq);
+			rx_page_desc->rx_nxseq++;
+			goto fatal_err;
+		}
+
+		rx_page_desc->rx_nxseq++;
+
+		/* error packet */
+		if (prrs->pkt_flag & RRS_IS_ERR_FRAME) {
+			if (prrs->err_flag & (RRS_ERR_BAD_CRC |
+					      RRS_ERR_DRIBBLE | RRS_ERR_CODE |
+					      RRS_ERR_TRUNC)) {
+				/* hardware error, discard this
+				   packet */
+				netdev_rx_err(netdev, NULL, EIO);
+				goto skip_pkt;
+			}
+		}
+
+		packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) &
+			       RRS_PKT_SIZE_MASK) - ETH_FCS_LEN;
+		iob = alloc_iob(packet_size + NET_IP_ALIGN);
+		if (iob == NULL) {
+			DBG("atl1e %s: dropping packet under memory pressure\n",
+			    netdev->name);
+			goto skip_pkt;
+		}
+		iob_reserve(iob, NET_IP_ALIGN);
+		memcpy(iob->data, (u8 *)(prrs + 1), packet_size);
+		iob_put(iob, packet_size);
+
+		netdev_rx(netdev, iob);
+
+skip_pkt:
+		/* skip current packet whether it's ok or not. */
+		rx_page->read_offset +=
+			(((u32)((prrs->word1 >> RRS_PKT_SIZE_SHIFT) &
+				RRS_PKT_SIZE_MASK) +
+			  sizeof(struct atl1e_recv_ret_status) + 31) &
+			 0xFFFFFFE0);
+
+		if (rx_page->read_offset >= rx_ring->page_size) {
+			/* mark this page clean */
+			u16 reg_addr;
+			u8  rx_using;
+
+			rx_page->read_offset =
+				*(rx_page->write_offset_addr) = 0;
+			rx_using = rx_page_desc->rx_using;
+			reg_addr =
+				atl1e_rx_page_vld_regs[rx_using];
+			AT_WRITE_REGB(&adapter->hw, reg_addr, 1);
+			rx_page_desc->rx_using ^= 1;
+			rx_page = atl1e_get_rx_page(adapter);
+		}
+		write_offset = *(rx_page->write_offset_addr);
+	} while (rx_page->read_offset < write_offset);
+
+	return;
+
+fatal_err:
+	if (!netdev_link_ok(adapter->netdev))
+		atl1e_reset(adapter);
+}
+
+/*
+ * atl1e_poll - poll for completed transmissions and received packets
+ * @netdev: network device
+ */
+static void atl1e_poll(struct net_device *netdev)
+{
+	struct atl1e_adapter *adapter = netdev_priv(netdev);
+	struct atl1e_hw *hw = &adapter->hw;
+	int max_ints = 64;
+	u32 status;
+
+	do {
+		status = AT_READ_REG(hw, REG_ISR);
+		if ((status & IMR_NORMAL_MASK) == 0)
+			break;
+
+		/* link event */
+		if (status & ISR_GPHY)
+			atl1e_clear_phy_int(adapter);
+		/* Ack ISR */
+		AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT);
+
+		/* check if PCIE PHY Link down */
+		if (status & ISR_PHY_LINKDOWN) {
+			DBG("atl1e: PCI-E PHY link down: %x\n", status);
+			if (netdev_link_ok(adapter->netdev)) {
+				/* reset MAC */
+				atl1e_irq_reset(adapter);
+				atl1e_reset(adapter);
+				break;
+			}
+		}
+
+		/* check if DMA read/write error */
+		if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) {
+			DBG("atl1e: PCI-E DMA RW error: %x\n", status);
+			atl1e_irq_reset(adapter);
+			atl1e_reset(adapter);
+			break;
+		}
+
+		/* link event */
+		if (status & (ISR_GPHY | ISR_MANUAL)) {
+			atl1e_check_link(adapter);
+			break;
+		}
+
+		/* transmit event */
+		if (status & ISR_TX_EVENT)
+			atl1e_clean_tx_irq(adapter);
+
+		if (status & ISR_RX_EVENT)
+			atl1e_clean_rx_irq(adapter);
+	} while (--max_ints > 0);
+
+	/* re-enable Interrupt*/
+	AT_WRITE_REG(&adapter->hw, REG_ISR, 0);
+
+	return;
+}
+
+static inline u16 atl1e_tpd_avail(struct atl1e_adapter *adapter)
+{
+	struct atl1e_tx_ring *tx_ring = &adapter->tx_ring;
+	u16 next_to_use = 0;
+	u16 next_to_clean = 0;
+
+	next_to_clean = tx_ring->next_to_clean;
+	next_to_use   = tx_ring->next_to_use;
+
+	return (u16)(next_to_clean > next_to_use) ?
+		(next_to_clean - next_to_use - 1) :
+		(tx_ring->count + next_to_clean - next_to_use - 1);
+}
+
+/*
+ * get next usable tpd
+ * Note: should call atl1e_tdp_avail to make sure
+ * there is enough tpd to use
+ */
+static struct atl1e_tpd_desc *atl1e_get_tpd(struct atl1e_adapter *adapter)
+{
+	struct atl1e_tx_ring *tx_ring = &adapter->tx_ring;
+	u16 next_to_use = 0;
+
+	next_to_use = tx_ring->next_to_use;
+	if (++tx_ring->next_to_use == tx_ring->count)
+		tx_ring->next_to_use = 0;
+
+	memset(&tx_ring->desc[next_to_use], 0, sizeof(struct atl1e_tpd_desc));
+	return (struct atl1e_tpd_desc *)&tx_ring->desc[next_to_use];
+}
+
+static struct atl1e_tx_buffer *
+atl1e_get_tx_buffer(struct atl1e_adapter *adapter, struct atl1e_tpd_desc *tpd)
+{
+	struct atl1e_tx_ring *tx_ring = &adapter->tx_ring;
+
+	return &tx_ring->tx_buffer[tpd - tx_ring->desc];
+}
+
+static void atl1e_tx_map(struct atl1e_adapter *adapter,
+		      struct io_buffer *iob, struct atl1e_tpd_desc *tpd)
+{
+	struct atl1e_tx_buffer *tx_buffer = NULL;
+	u16 buf_len = iob_len(iob);
+
+	tx_buffer = atl1e_get_tx_buffer(adapter, tpd);
+	tx_buffer->iob = iob;
+	tx_buffer->length = buf_len;
+	tx_buffer->dma = virt_to_bus(iob->data);
+	tpd->buffer_addr = cpu_to_le64(tx_buffer->dma);
+	tpd->word2 = ((tpd->word2 & ~TPD_BUFLEN_MASK) |
+		      ((cpu_to_le32(buf_len) & TPD_BUFLEN_MASK) <<
+		       TPD_BUFLEN_SHIFT));
+	tpd->word3 |= 1 << TPD_EOP_SHIFT;
+}
+
+static void atl1e_tx_queue(struct atl1e_adapter *adapter, u16 count __unused,
+			   struct atl1e_tpd_desc *tpd __unused)
+{
+	struct atl1e_tx_ring *tx_ring = &adapter->tx_ring;
+	wmb();
+	AT_WRITE_REG(&adapter->hw, REG_MB_TPD_PROD_IDX, tx_ring->next_to_use);
+}
+
+static int atl1e_xmit_frame(struct net_device *netdev, struct io_buffer *iob)
+{
+	struct atl1e_adapter *adapter = netdev_priv(netdev);
+	u16 tpd_req = 1;
+	struct atl1e_tpd_desc *tpd;
+
+	if (!netdev_link_ok(netdev)) {
+		return -EINVAL;
+	}
+
+	if (atl1e_tpd_avail(adapter) < tpd_req) {
+		return -EBUSY;
+	}
+
+	tpd = atl1e_get_tpd(adapter);
+
+	atl1e_tx_map(adapter, iob, tpd);
+	atl1e_tx_queue(adapter, tpd_req, tpd);
+
+	return 0;
+}
+
+int atl1e_up(struct atl1e_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	int err = 0;
+	u32 val;
+
+	/* hardware has been reset, we need to reload some things */
+	err = atl1e_init_hw(&adapter->hw);
+	if (err) {
+		return -EIO;
+	}
+	atl1e_init_ring_ptrs(adapter);
+
+	memcpy(adapter->hw.mac_addr, netdev->ll_addr, ETH_ALEN);
+
+	if (atl1e_configure(adapter) != 0) {
+		return -EIO;
+	}
+
+	atl1e_irq_disable(adapter);
+
+	val = AT_READ_REG(&adapter->hw, REG_MASTER_CTRL);
+	AT_WRITE_REG(&adapter->hw, REG_MASTER_CTRL,
+		      val | MASTER_CTRL_MANUAL_INT);
+
+	return err;
+}
+
+void atl1e_irq(struct net_device *netdev, int enable)
+{
+	struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+	if (enable)
+		atl1e_irq_enable(adapter);
+	else
+		atl1e_irq_disable(adapter);
+}
+
+void atl1e_down(struct atl1e_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+
+	/* reset MAC to disable all RX/TX */
+	atl1e_reset_hw(&adapter->hw);
+	mdelay(1);
+
+	netdev_link_down(netdev);
+	adapter->link_speed = SPEED_0;
+	adapter->link_duplex = -1;
+
+	atl1e_clean_tx_ring(adapter);
+	atl1e_clean_rx_ring(adapter);
+}
+
+/*
+ * atl1e_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP).  At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the watchdog timer is started,
+ * and the stack is notified that the interface is ready.
+ */
+static int atl1e_open(struct net_device *netdev)
+{
+	struct atl1e_adapter *adapter = netdev_priv(netdev);
+	int err;
+
+	/* allocate rx/tx dma buffer & descriptors */
+	atl1e_init_ring_resources(adapter);
+	err = atl1e_setup_ring_resources(adapter);
+	if (err)
+		return err;
+
+	err = atl1e_up(adapter);
+	if (err)
+		goto err_up;
+
+	return 0;
+
+err_up:
+	atl1e_free_ring_resources(adapter);
+	atl1e_reset_hw(&adapter->hw);
+
+	return err;
+}
+
+/*
+ * atl1e_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * Returns 0, this is not allowed to fail
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS.  The hardware is still under the drivers control, but
+ * needs to be disabled.  A global MAC reset is issued to stop the
+ * hardware, and all transmit and receive resources are freed.
+ */
+static void atl1e_close(struct net_device *netdev)
+{
+	struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+	atl1e_down(adapter);
+	atl1e_free_ring_resources(adapter);
+}
+
+static struct net_device_operations atl1e_netdev_ops = {
+	.open		= atl1e_open,
+	.close		= atl1e_close,
+	.transmit	= atl1e_xmit_frame,
+	.poll		= atl1e_poll,
+	.irq		= atl1e_irq,
+};
+
+static void atl1e_init_netdev(struct net_device *netdev, struct pci_device *pdev)
+{
+	netdev_init(netdev, &atl1e_netdev_ops);
+
+	netdev->dev = &pdev->dev;
+	pci_set_drvdata(pdev, netdev);
+}
+
+/*
+ * atl1e_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @ent: entry in atl1e_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * atl1e_probe initializes an adapter identified by a pci_device structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ */
+static int atl1e_probe(struct pci_device *pdev,
+		       const struct pci_device_id *ent __unused)
+{
+	struct net_device *netdev;
+	struct atl1e_adapter *adapter = NULL;
+	static int cards_found;
+
+	int err = 0;
+
+	adjust_pci_device(pdev);
+
+	netdev = alloc_etherdev(sizeof(struct atl1e_adapter));
+	if (netdev == NULL) {
+		err = -ENOMEM;
+		DBG("atl1e: out of memory allocating net_device\n");
+		goto err;
+	}
+
+	atl1e_init_netdev(netdev, pdev);
+
+	adapter = netdev_priv(netdev);
+	adapter->bd_number = cards_found;
+	adapter->netdev = netdev;
+	adapter->pdev = pdev;
+	adapter->hw.adapter = adapter;
+	if (!pdev->membase) {
+		err = -EIO;
+		DBG("atl1e: cannot map device registers\n");
+		goto err_free_netdev;
+	}
+	adapter->hw.hw_addr = bus_to_virt(pdev->membase);
+
+	/* init mii data */
+	adapter->mii.dev = netdev;
+	adapter->mii.mdio_read  = atl1e_mdio_read;
+	adapter->mii.mdio_write = atl1e_mdio_write;
+	adapter->mii.phy_id_mask = 0x1f;
+	adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK;
+
+	/* get user settings */
+	adapter->tx_ring.count = TX_DESC_COUNT;
+	adapter->rx_ring.page_size = RX_MEM_SIZE;
+
+	atl1e_setup_pcicmd(pdev);
+
+	/* setup the private structure */
+	err = atl1e_sw_init(adapter);
+	if (err) {
+		DBG("atl1e: private data init failed\n");
+		goto err_free_netdev;
+	}
+
+	/* Init GPHY as early as possible due to power saving issue  */
+	atl1e_phy_init(&adapter->hw);
+
+	/* reset the controller to
+	 * put the device in a known good starting state */
+	err = atl1e_reset_hw(&adapter->hw);
+	if (err) {
+		err = -EIO;
+		goto err_free_netdev;
+	}
+
+	/* This may have been run by a zero-wait timer around
+	   now... unclear. */
+	atl1e_restart_autoneg(&adapter->hw);
+
+	if (atl1e_read_mac_addr(&adapter->hw) != 0) {
+		DBG("atl1e: cannot read MAC address from EEPROM\n");
+		err = -EIO;
+		goto err_free_netdev;
+	}
+
+	memcpy(netdev->hw_addr, adapter->hw.perm_mac_addr, ETH_ALEN);
+	memcpy(netdev->ll_addr, adapter->hw.mac_addr, ETH_ALEN);
+	DBG("atl1e: Attansic L1E Ethernet controller on %s, "
+	    "%02x:%02x:%02x:%02x:%02x:%02x\n", adapter->netdev->name,
+	    adapter->hw.mac_addr[0], adapter->hw.mac_addr[1],
+	    adapter->hw.mac_addr[2], adapter->hw.mac_addr[3],
+	    adapter->hw.mac_addr[4], adapter->hw.mac_addr[5]);
+
+	err = register_netdev(netdev);
+	if (err) {
+		DBG("atl1e: cannot register network device\n");
+		goto err_free_netdev;
+	}
+
+	netdev_link_down(netdev);
+
+	cards_found++;
+	return 0;
+
+err_free_netdev:
+	netdev_nullify(netdev);
+	netdev_put(netdev);
+err:
+	return err;
+}
+
+/*
+ * atl1e_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * atl1e_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device.  The could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ */
+static void atl1e_remove(struct pci_device *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct atl1e_adapter *adapter = netdev_priv(netdev);
+
+	unregister_netdev(netdev);
+	atl1e_free_ring_resources(adapter);
+	atl1e_force_ps(&adapter->hw);
+	netdev_nullify(netdev);
+	netdev_put(netdev);
+}
+
+struct pci_driver atl1e_driver __pci_driver = {
+	.ids      = atl1e_pci_tbl,
+	.id_count = (sizeof(atl1e_pci_tbl) / sizeof(atl1e_pci_tbl[0])),
+	.probe    = atl1e_probe,
+	.remove   = atl1e_remove,
+};
+
+/********** Hardware-level functions: **********/
+
+/*
+ * check_eeprom_exist
+ * return 0 if eeprom exist
+ */
+int atl1e_check_eeprom_exist(struct atl1e_hw *hw)
+{
+	u32 value;
+
+	value = AT_READ_REG(hw, REG_SPI_FLASH_CTRL);
+	if (value & SPI_FLASH_CTRL_EN_VPD) {
+		value &= ~SPI_FLASH_CTRL_EN_VPD;
+		AT_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
+	}
+	value = AT_READ_REGW(hw, REG_PCIE_CAP_LIST);
+	return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
+}
+
+void atl1e_hw_set_mac_addr(struct atl1e_hw *hw)
+{
+	u32 value;
+	/*
+	 * 00-0B-6A-F6-00-DC
+	 * 0:  6AF600DC 1: 000B
+	 * low dword
+	 */
+	value = (((u32)hw->mac_addr[2]) << 24) |
+		(((u32)hw->mac_addr[3]) << 16) |
+		(((u32)hw->mac_addr[4]) << 8)  |
+		(((u32)hw->mac_addr[5])) ;
+	AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
+	/* hight dword */
+	value = (((u32)hw->mac_addr[0]) << 8) |
+		(((u32)hw->mac_addr[1])) ;
+	AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
+}
+
+/*
+ * atl1e_get_permanent_address
+ * return 0 if get valid mac address,
+ */
+static int atl1e_get_permanent_address(struct atl1e_hw *hw)
+{
+	union {
+		u32 dword[2];
+		u8 byte[8];
+	} hw_addr;
+	u32 i;
+	u32 twsi_ctrl_data;
+	u8  eth_addr[ETH_ALEN];
+
+	if (!atl1e_check_eeprom_exist(hw)) {
+		/* eeprom exist */
+		twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL);
+		twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART;
+		AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data);
+		for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) {
+			mdelay(10);
+			twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL);
+			if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0)
+				break;
+		}
+		if (i >= AT_TWSI_EEPROM_TIMEOUT)
+			return AT_ERR_TIMEOUT;
+	}
+
+	/* maybe MAC-address is from BIOS */
+	hw_addr.dword[0] = AT_READ_REG(hw, REG_MAC_STA_ADDR);
+	hw_addr.dword[1] = AT_READ_REG(hw, REG_MAC_STA_ADDR + 4);
+	for (i = 0; i < ETH_ALEN; i++) {
+		eth_addr[ETH_ALEN - i - 1] = hw_addr.byte[i];
+	}
+
+	memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+	return 0;
+}
+
+void atl1e_force_ps(struct atl1e_hw *hw)
+{
+	AT_WRITE_REGW(hw, REG_GPHY_CTRL,
+			GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET);
+}
+
+/*
+ * Reads the adapter's MAC address from the EEPROM
+ *
+ * hw - Struct containing variables accessed by shared code
+ */
+int atl1e_read_mac_addr(struct atl1e_hw *hw)
+{
+	int err = 0;
+
+	err = atl1e_get_permanent_address(hw);
+	if (err)
+		return AT_ERR_EEPROM;
+	memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr));
+	return 0;
+}
+
+/*
+ * Reads the value from a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to read
+ */
+int atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data)
+{
+	u32 val;
+	int i;
+
+	val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
+		MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW |
+		MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+
+	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
+
+	wmb();
+
+	for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+		udelay(2);
+		val = AT_READ_REG(hw, REG_MDIO_CTRL);
+		if (!(val & (MDIO_START | MDIO_BUSY)))
+			break;
+		wmb();
+	}
+	if (!(val & (MDIO_START | MDIO_BUSY))) {
+		*phy_data = (u16)val;
+		return 0;
+	}
+
+	return AT_ERR_PHY;
+}
+
+/*
+ * Writes a value to a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to write
+ * data - data to write to the PHY
+ */
+int atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data)
+{
+	int i;
+	u32 val;
+
+	val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
+	       (reg_addr&MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
+	       MDIO_SUP_PREAMBLE |
+	       MDIO_START |
+	       MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+
+	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
+	wmb();
+
+	for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+		udelay(2);
+		val = AT_READ_REG(hw, REG_MDIO_CTRL);
+		if (!(val & (MDIO_START | MDIO_BUSY)))
+			break;
+		wmb();
+	}
+
+	if (!(val & (MDIO_START | MDIO_BUSY)))
+		return 0;
+
+	return AT_ERR_PHY;
+}
+
+/*
+ * atl1e_init_pcie - init PCIE module
+ */
+static void atl1e_init_pcie(struct atl1e_hw *hw)
+{
+	u32 value;
+	/* comment 2lines below to save more power when sususpend
+	   value = LTSSM_TEST_MODE_DEF;
+	   AT_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value);
+	 */
+
+	/* pcie flow control mode change */
+	value = AT_READ_REG(hw, 0x1008);
+	value |= 0x8000;
+	AT_WRITE_REG(hw, 0x1008, value);
+}
+/*
+ * Configures PHY autoneg and flow control advertisement settings
+ *
+ * hw - Struct containing variables accessed by shared code
+ */
+static int atl1e_phy_setup_autoneg_adv(struct atl1e_hw *hw)
+{
+	s32 ret_val;
+	u16 mii_autoneg_adv_reg;
+	u16 mii_1000t_ctrl_reg;
+
+	if (0 != hw->mii_autoneg_adv_reg)
+		return 0;
+	/* Read the MII Auto-Neg Advertisement Register (Address 4/9). */
+	mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
+	mii_1000t_ctrl_reg  = MII_AT001_CR_1000T_DEFAULT_CAP_MASK;
+
+	/*
+	 * First we clear all the 10/100 mb speed bits in the Auto-Neg
+	 * Advertisement Register (Address 4) and the 1000 mb speed bits in
+	 * the  1000Base-T control Register (Address 9).
+	 */
+	mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK;
+	mii_1000t_ctrl_reg  &= ~MII_AT001_CR_1000T_SPEED_MASK;
+
+	/* Assume auto-detect media type */
+	mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS   |
+				MII_AR_10T_FD_CAPS   |
+				MII_AR_100TX_HD_CAPS |
+				MII_AR_100TX_FD_CAPS);
+	if (hw->nic_type == athr_l1e) {
+		mii_1000t_ctrl_reg |= MII_AT001_CR_1000T_FD_CAPS;
+	}
+
+	/* flow control fixed to enable all */
+	mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE);
+
+	hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
+	hw->mii_1000t_ctrl_reg  = mii_1000t_ctrl_reg;
+
+	ret_val = atl1e_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg);
+	if (ret_val)
+		return ret_val;
+
+	if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) {
+		ret_val = atl1e_write_phy_reg(hw, MII_AT001_CR,
+					   mii_1000t_ctrl_reg);
+		if (ret_val)
+			return ret_val;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Resets the PHY and make all config validate
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Sets bit 15 and 12 of the MII control regiser (for F001 bug)
+ */
+int atl1e_phy_commit(struct atl1e_hw *hw)
+{
+	int ret_val;
+	u16 phy_data;
+
+	phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG;
+
+	ret_val = atl1e_write_phy_reg(hw, MII_BMCR, phy_data);
+	if (ret_val) {
+		u32 val;
+		int i;
+		/**************************************
+		 * pcie serdes link may be down !
+		 **************************************/
+		for (i = 0; i < 25; i++) {
+			mdelay(1);
+			val = AT_READ_REG(hw, REG_MDIO_CTRL);
+			if (!(val & (MDIO_START | MDIO_BUSY)))
+				break;
+		}
+
+		if (0 != (val & (MDIO_START | MDIO_BUSY))) {
+			DBG("atl1e: PCI-E link down for at least 25ms\n");
+			return ret_val;
+		}
+
+		DBG("atl1e: PCI-E link up after %d ms\n", i);
+	}
+	return 0;
+}
+
+int atl1e_phy_init(struct atl1e_hw *hw)
+{
+	s32 ret_val;
+	u16 phy_val;
+
+	if (hw->phy_configured) {
+		if (hw->re_autoneg) {
+			hw->re_autoneg = 0;
+			return atl1e_restart_autoneg(hw);
+		}
+		return 0;
+	}
+
+	/* RESET GPHY Core */
+	AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT);
+	mdelay(2);
+	AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT |
+		      GPHY_CTRL_EXT_RESET);
+	mdelay(2);
+
+	/* patches */
+	/* p1. eable hibernation mode */
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0xB);
+	if (ret_val)
+		return ret_val;
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0xBC00);
+	if (ret_val)
+		return ret_val;
+	/* p2. set Class A/B for all modes */
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0);
+	if (ret_val)
+		return ret_val;
+	phy_val = 0x02ef;
+	/* remove Class AB */
+	/* phy_val = hw->emi_ca ? 0x02ef : 0x02df; */
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, phy_val);
+	if (ret_val)
+		return ret_val;
+	/* p3. 10B ??? */
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x12);
+	if (ret_val)
+		return ret_val;
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x4C04);
+	if (ret_val)
+		return ret_val;
+	/* p4. 1000T power */
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x4);
+	if (ret_val)
+		return ret_val;
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x8BBB);
+	if (ret_val)
+		return ret_val;
+
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x5);
+	if (ret_val)
+		return ret_val;
+	ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x2C46);
+	if (ret_val)
+		return ret_val;
+
+	mdelay(1);
+
+	/*Enable PHY LinkChange Interrupt */
+	ret_val = atl1e_write_phy_reg(hw, MII_INT_CTRL, 0xC00);
+	if (ret_val) {
+		DBG("atl1e: Error enable PHY linkChange Interrupt\n");
+		return ret_val;
+	}
+	/* setup AutoNeg parameters */
+	ret_val = atl1e_phy_setup_autoneg_adv(hw);
+	if (ret_val) {
+		DBG("atl1e: Error Setting up Auto-Negotiation\n");
+		return ret_val;
+	}
+	/* SW.Reset & En-Auto-Neg to restart Auto-Neg*/
+	DBG("atl1e: Restarting Auto-Neg");
+	ret_val = atl1e_phy_commit(hw);
+	if (ret_val) {
+		DBG("atl1e: Error Resetting the phy");
+		return ret_val;
+	}
+
+	hw->phy_configured = 1;
+
+	return 0;
+}
+
+/*
+ * Reset the transmit and receive units; mask and clear all interrupts.
+ * hw - Struct containing variables accessed by shared code
+ * return : 0  or  idle status (if error)
+ */
+int atl1e_reset_hw(struct atl1e_hw *hw)
+{
+	struct atl1e_adapter *adapter = hw->adapter;
+	struct pci_device *pdev = adapter->pdev;
+	int timeout = 0;
+	u32 idle_status_data = 0;
+	u16 pci_cfg_cmd_word = 0;
+
+	/* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */
+	pci_read_config_word(pdev, PCI_COMMAND, &pci_cfg_cmd_word);
+	if ((pci_cfg_cmd_word & (PCI_COMMAND_IO | PCI_COMMAND_MEM |
+				 PCI_COMMAND_MASTER))
+			!= (PCI_COMMAND_IO | PCI_COMMAND_MEM |
+			    PCI_COMMAND_MASTER)) {
+		pci_cfg_cmd_word |= (PCI_COMMAND_IO | PCI_COMMAND_MEM |
+				     PCI_COMMAND_MASTER);
+		pci_write_config_word(pdev, PCI_COMMAND, pci_cfg_cmd_word);
+	}
+
+	/*
+	 * Issue Soft Reset to the MAC.  This will reset the chip's
+	 * transmit, receive, DMA.  It will not effect
+	 * the current PCI configuration.  The global reset bit is self-
+	 * clearing, and should clear within a microsecond.
+	 */
+	AT_WRITE_REG(hw, REG_MASTER_CTRL,
+			MASTER_CTRL_LED_MODE | MASTER_CTRL_SOFT_RST);
+	wmb();
+	mdelay(1);
+
+	/* Wait at least 10ms for All module to be Idle */
+	for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) {
+		idle_status_data = AT_READ_REG(hw, REG_IDLE_STATUS);
+		if (idle_status_data == 0)
+			break;
+		mdelay(1);
+	}
+
+	if (timeout >= AT_HW_MAX_IDLE_DELAY) {
+		DBG("atl1e: MAC reset timeout\n");
+		return AT_ERR_TIMEOUT;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Performs basic configuration of the adapter.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * Assumes that the controller has previously been reset and is in a
+ * post-reset uninitialized state. Initializes multicast table,
+ * and  Calls routines to setup link
+ * Leaves the transmit and receive units disabled and uninitialized.
+ */
+int atl1e_init_hw(struct atl1e_hw *hw)
+{
+	s32 ret_val = 0;
+
+	atl1e_init_pcie(hw);
+
+	/* Zero out the Multicast HASH table */
+	/* clear the old settings from the multicast hash table */
+	AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
+	AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
+
+	ret_val = atl1e_phy_init(hw);
+
+	return ret_val;
+}
+
+/*
+ * Detects the current speed and duplex settings of the hardware.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * speed - Speed of the connection
+ * duplex - Duplex setting of the connection
+ */
+int atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex)
+{
+	int err;
+	u16 phy_data;
+
+	/* Read   PHY Specific Status Register (17) */
+	err = atl1e_read_phy_reg(hw, MII_AT001_PSSR, &phy_data);
+	if (err)
+		return err;
+
+	if (!(phy_data & MII_AT001_PSSR_SPD_DPLX_RESOLVED))
+		return AT_ERR_PHY_RES;
+
+	switch (phy_data & MII_AT001_PSSR_SPEED) {
+	case MII_AT001_PSSR_1000MBS:
+		*speed = SPEED_1000;
+		break;
+	case MII_AT001_PSSR_100MBS:
+		*speed = SPEED_100;
+		break;
+	case MII_AT001_PSSR_10MBS:
+		*speed = SPEED_10;
+		break;
+	default:
+		return AT_ERR_PHY_SPEED;
+		break;
+	}
+
+	if (phy_data & MII_AT001_PSSR_DPLX)
+		*duplex = FULL_DUPLEX;
+	else
+		*duplex = HALF_DUPLEX;
+
+	return 0;
+}
+
+int atl1e_restart_autoneg(struct atl1e_hw *hw)
+{
+	int err = 0;
+
+	err = atl1e_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg);
+	if (err)
+		return err;
+
+	if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) {
+		err = atl1e_write_phy_reg(hw, MII_AT001_CR,
+				       hw->mii_1000t_ctrl_reg);
+		if (err)
+			return err;
+	}
+
+	err = atl1e_write_phy_reg(hw, MII_BMCR,
+			MII_CR_RESET | MII_CR_AUTO_NEG_EN |
+			MII_CR_RESTART_AUTO_NEG);
+	return err;
+}
+
diff --git a/gpxe/src/drivers/net/atl1e.h b/gpxe/src/drivers/net/atl1e.h
new file mode 100644
index 0000000..949c323
--- /dev/null
+++ b/gpxe/src/drivers/net/atl1e.h
@@ -0,0 +1,1031 @@
+/*
+ * Copyright(c) 2007 Atheros Corporation. All rights reserved.
+ * Copyright(c) 2007 xiong huang <xiong.huang at atheros.com>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ *
+ * Modified for gPXE, October 2009 by Joshua Oreman <oremanj at rwcr.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _ATL1E_H_
+#define _ATL1E_H_
+
+#include <mii.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/malloc.h>
+#include <gpxe/pci.h>
+#include <gpxe/pci_io.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/io.h>
+
+#define ETH_FCS_LEN	4
+#define VLAN_HLEN	4
+#define NET_IP_ALIGN	2
+
+#define SPEED_0		   0xffff
+#define SPEED_10	   10
+#define SPEED_100	   100
+#define SPEED_1000	   1000
+#define HALF_DUPLEX        1
+#define FULL_DUPLEX        2
+
+/* Error Codes */
+#define AT_ERR_EEPROM      1
+#define AT_ERR_PHY         2
+#define AT_ERR_CONFIG      3
+#define AT_ERR_PARAM       4
+#define AT_ERR_MAC_TYPE    5
+#define AT_ERR_PHY_TYPE    6
+#define AT_ERR_PHY_SPEED   7
+#define AT_ERR_PHY_RES     8
+#define AT_ERR_TIMEOUT     9
+
+#define AT_MAX_RECEIVE_QUEUE    4
+#define AT_PAGE_NUM_PER_QUEUE   2
+
+#define AT_TWSI_EEPROM_TIMEOUT 	100
+#define AT_HW_MAX_IDLE_DELAY 	10
+
+#define AT_REGS_LEN	75
+#define AT_EEPROM_LEN 	512
+
+/* tpd word 2 */
+#define TPD_BUFLEN_MASK 	0x3FFF
+#define TPD_BUFLEN_SHIFT        0
+
+/* tpd word 3 bits 0:4 */
+#define TPD_EOP_MASK            0x0001
+#define TPD_EOP_SHIFT           0
+
+struct atl1e_tpd_desc {
+	u64 buffer_addr;
+	u32 word2;
+	u32 word3;
+};
+
+#define MAX_TX_BUF_LEN      0x2000
+#define MAX_TX_BUF_SHIFT    13
+
+/* rrs word 1 bit 0:31 */
+#define RRS_RX_CSUM_MASK	0xFFFF
+#define RRS_RX_CSUM_SHIFT	0
+#define RRS_PKT_SIZE_MASK	0x3FFF
+#define RRS_PKT_SIZE_SHIFT	16
+#define RRS_CPU_NUM_MASK	0x0003
+#define	RRS_CPU_NUM_SHIFT	30
+
+#define	RRS_IS_RSS_IPV4		0x0001
+#define RRS_IS_RSS_IPV4_TCP	0x0002
+#define RRS_IS_RSS_IPV6		0x0004
+#define RRS_IS_RSS_IPV6_TCP	0x0008
+#define RRS_IS_IPV6		0x0010
+#define RRS_IS_IP_FRAG		0x0020
+#define RRS_IS_IP_DF		0x0040
+#define RRS_IS_802_3		0x0080
+#define RRS_IS_VLAN_TAG		0x0100
+#define RRS_IS_ERR_FRAME	0x0200
+#define RRS_IS_IPV4		0x0400
+#define RRS_IS_UDP		0x0800
+#define RRS_IS_TCP		0x1000
+#define RRS_IS_BCAST		0x2000
+#define RRS_IS_MCAST		0x4000
+#define RRS_IS_PAUSE		0x8000
+
+#define RRS_ERR_BAD_CRC		0x0001
+#define RRS_ERR_CODE		0x0002
+#define RRS_ERR_DRIBBLE		0x0004
+#define RRS_ERR_RUNT		0x0008
+#define RRS_ERR_RX_OVERFLOW	0x0010
+#define RRS_ERR_TRUNC		0x0020
+#define RRS_ERR_IP_CSUM		0x0040
+#define RRS_ERR_L4_CSUM		0x0080
+#define RRS_ERR_LENGTH		0x0100
+#define RRS_ERR_DES_ADDR	0x0200
+
+struct atl1e_recv_ret_status {
+	u16 seq_num;
+	u16 hash_lo;
+	u32 word1;
+	u16 pkt_flag;
+	u16 err_flag;
+	u16 hash_hi;
+	u16 vtag;
+};
+
+enum atl1e_dma_req_block {
+	atl1e_dma_req_128 = 0,
+	atl1e_dma_req_256 = 1,
+	atl1e_dma_req_512 = 2,
+	atl1e_dma_req_1024 = 3,
+	atl1e_dma_req_2048 = 4,
+	atl1e_dma_req_4096 = 5
+};
+
+enum atl1e_nic_type {
+	athr_l1e = 0,
+	athr_l2e_revA = 1,
+	athr_l2e_revB = 2
+};
+
+struct atl1e_hw {
+	u8 *hw_addr;            /* inner register address */
+	struct atl1e_adapter *adapter;
+	enum atl1e_nic_type  nic_type;
+	u8 mac_addr[ETH_ALEN];
+	u8 perm_mac_addr[ETH_ALEN];
+
+	u16 mii_autoneg_adv_reg;
+	u16 mii_1000t_ctrl_reg;
+
+	enum atl1e_dma_req_block dmar_block;
+	enum atl1e_dma_req_block dmaw_block;
+
+	int phy_configured;
+	int re_autoneg;
+	int emi_ca;
+};
+
+/*
+ * wrapper around a pointer to a socket buffer,
+ * so a DMA handle can be stored along with the buffer
+ */
+struct atl1e_tx_buffer {
+	struct io_buffer *iob;
+	u16 length;
+	u32 dma;
+};
+
+struct atl1e_rx_page {
+	u32		dma;    /* receive rage DMA address */
+	u8		*addr;   /* receive rage virtual address */
+	u32		write_offset_dma;  /* the DMA address which contain the
+					      receive data offset in the page */
+	u32		*write_offset_addr; /* the virtaul address which contain
+					     the receive data offset in the page */
+	u32		read_offset;       /* the offset where we have read */
+};
+
+struct atl1e_rx_page_desc {
+	struct atl1e_rx_page   rx_page[AT_PAGE_NUM_PER_QUEUE];
+	u8  rx_using;
+	u16 rx_nxseq;
+};
+
+/* transmit packet descriptor (tpd) ring */
+struct atl1e_tx_ring {
+	struct atl1e_tpd_desc *desc;  /* descriptor ring virtual address  */
+	u32		   dma;    /* descriptor ring physical address */
+	u16       	   count;  /* the count of transmit rings  */
+	u16		   next_to_use;
+	u16		   next_to_clean;
+	struct atl1e_tx_buffer *tx_buffer;
+	u32		   cmb_dma;
+	u32		   *cmb;
+};
+
+/* receive packet descriptor ring */
+struct atl1e_rx_ring {
+	void        	*desc;
+	u32	  	dma;
+	int         	size;
+	u32	    	page_size; /* bytes length of rxf page */
+	u32		real_page_size; /* real_page_size = page_size + jumbo + aliagn */
+	struct atl1e_rx_page_desc rx_page_desc;
+};
+
+/* board specific private data structure */
+struct atl1e_adapter {
+	struct net_device   *netdev;
+	struct pci_device   *pdev;
+	struct mii_if_info  mii;    /* MII interface info */
+	struct atl1e_hw        hw;
+
+	u16 link_speed;
+	u16 link_duplex;
+
+	/* All Descriptor memory */
+	u32	  	ring_dma;
+	void     	*ring_vir_addr;
+	u32             ring_size;
+
+	struct atl1e_tx_ring tx_ring;
+	struct atl1e_rx_ring rx_ring;
+
+	int bd_number;     /* board number;*/
+};
+
+#define AT_WRITE_REG(a, reg, value) \
+		writel((value), ((a)->hw_addr + reg))
+
+#define AT_WRITE_FLUSH(a) \
+		readl((a)->hw_addr)
+
+#define AT_READ_REG(a, reg) \
+		readl((a)->hw_addr + reg)
+
+#define AT_WRITE_REGB(a, reg, value) \
+		writeb((value), ((a)->hw_addr + reg))
+
+#define AT_READ_REGB(a, reg) \
+		readb((a)->hw_addr + reg)
+
+#define AT_WRITE_REGW(a, reg, value) \
+		writew((value), ((a)->hw_addr + reg))
+
+#define AT_READ_REGW(a, reg) \
+		readw((a)->hw_addr + reg)
+
+#define AT_WRITE_REG_ARRAY(a, reg, offset, value) \
+		writel((value), (((a)->hw_addr + reg) + ((offset) << 2)))
+
+#define AT_READ_REG_ARRAY(a, reg, offset) \
+		readl(((a)->hw_addr + reg) + ((offset) << 2))
+
+extern int atl1e_up(struct atl1e_adapter *adapter);
+extern void atl1e_down(struct atl1e_adapter *adapter);
+extern s32 atl1e_reset_hw(struct atl1e_hw *hw);
+
+/********** Hardware-level functionality: **********/
+
+/* function prototype */
+s32 atl1e_reset_hw(struct atl1e_hw *hw);
+s32 atl1e_read_mac_addr(struct atl1e_hw *hw);
+s32 atl1e_init_hw(struct atl1e_hw *hw);
+s32 atl1e_phy_commit(struct atl1e_hw *hw);
+s32 atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex);
+u32 atl1e_auto_get_fc(struct atl1e_adapter *adapter, u16 duplex);
+s32 atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data);
+s32 atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data);
+s32 atl1e_validate_mdi_setting(struct atl1e_hw *hw);
+void atl1e_hw_set_mac_addr(struct atl1e_hw *hw);
+s32 atl1e_phy_enter_power_saving(struct atl1e_hw *hw);
+s32 atl1e_phy_leave_power_saving(struct atl1e_hw *hw);
+s32 atl1e_phy_init(struct atl1e_hw *hw);
+int atl1e_check_eeprom_exist(struct atl1e_hw *hw);
+void atl1e_force_ps(struct atl1e_hw *hw);
+s32 atl1e_restart_autoneg(struct atl1e_hw *hw);
+
+/* register definition */
+#define REG_PM_CTRLSTAT             0x44
+
+#define REG_PCIE_CAP_LIST           0x58
+
+#define REG_DEVICE_CAP              0x5C
+#define     DEVICE_CAP_MAX_PAYLOAD_MASK     0x7
+#define     DEVICE_CAP_MAX_PAYLOAD_SHIFT    0
+
+#define REG_DEVICE_CTRL             0x60
+#define     DEVICE_CTRL_MAX_PAYLOAD_MASK    0x7
+#define     DEVICE_CTRL_MAX_PAYLOAD_SHIFT   5
+#define     DEVICE_CTRL_MAX_RREQ_SZ_MASK    0x7
+#define     DEVICE_CTRL_MAX_RREQ_SZ_SHIFT   12
+
+#define REG_VPD_CAP                 0x6C
+#define     VPD_CAP_ID_MASK                 0xff
+#define     VPD_CAP_ID_SHIFT                0
+#define     VPD_CAP_NEXT_PTR_MASK           0xFF
+#define     VPD_CAP_NEXT_PTR_SHIFT          8
+#define     VPD_CAP_VPD_ADDR_MASK           0x7FFF
+#define     VPD_CAP_VPD_ADDR_SHIFT          16
+#define     VPD_CAP_VPD_FLAG                0x80000000
+
+#define REG_VPD_DATA                0x70
+
+#define REG_SPI_FLASH_CTRL          0x200
+#define     SPI_FLASH_CTRL_STS_NON_RDY      0x1
+#define     SPI_FLASH_CTRL_STS_WEN          0x2
+#define     SPI_FLASH_CTRL_STS_WPEN         0x80
+#define     SPI_FLASH_CTRL_DEV_STS_MASK     0xFF
+#define     SPI_FLASH_CTRL_DEV_STS_SHIFT    0
+#define     SPI_FLASH_CTRL_INS_MASK         0x7
+#define     SPI_FLASH_CTRL_INS_SHIFT        8
+#define     SPI_FLASH_CTRL_START            0x800
+#define     SPI_FLASH_CTRL_EN_VPD           0x2000
+#define     SPI_FLASH_CTRL_LDSTART          0x8000
+#define     SPI_FLASH_CTRL_CS_HI_MASK       0x3
+#define     SPI_FLASH_CTRL_CS_HI_SHIFT      16
+#define     SPI_FLASH_CTRL_CS_HOLD_MASK     0x3
+#define     SPI_FLASH_CTRL_CS_HOLD_SHIFT    18
+#define     SPI_FLASH_CTRL_CLK_LO_MASK      0x3
+#define     SPI_FLASH_CTRL_CLK_LO_SHIFT     20
+#define     SPI_FLASH_CTRL_CLK_HI_MASK      0x3
+#define     SPI_FLASH_CTRL_CLK_HI_SHIFT     22
+#define     SPI_FLASH_CTRL_CS_SETUP_MASK    0x3
+#define     SPI_FLASH_CTRL_CS_SETUP_SHIFT   24
+#define     SPI_FLASH_CTRL_EROM_PGSZ_MASK   0x3
+#define     SPI_FLASH_CTRL_EROM_PGSZ_SHIFT  26
+#define     SPI_FLASH_CTRL_WAIT_READY       0x10000000
+
+#define REG_SPI_ADDR                0x204
+
+#define REG_SPI_DATA                0x208
+
+#define REG_SPI_FLASH_CONFIG        0x20C
+#define     SPI_FLASH_CONFIG_LD_ADDR_MASK   0xFFFFFF
+#define     SPI_FLASH_CONFIG_LD_ADDR_SHIFT  0
+#define     SPI_FLASH_CONFIG_VPD_ADDR_MASK  0x3
+#define     SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24
+#define     SPI_FLASH_CONFIG_LD_EXIST       0x4000000
+
+
+#define REG_SPI_FLASH_OP_PROGRAM    0x210
+#define REG_SPI_FLASH_OP_SC_ERASE   0x211
+#define REG_SPI_FLASH_OP_CHIP_ERASE 0x212
+#define REG_SPI_FLASH_OP_RDID       0x213
+#define REG_SPI_FLASH_OP_WREN       0x214
+#define REG_SPI_FLASH_OP_RDSR       0x215
+#define REG_SPI_FLASH_OP_WRSR       0x216
+#define REG_SPI_FLASH_OP_READ       0x217
+
+#define REG_TWSI_CTRL               0x218
+#define     TWSI_CTRL_LD_OFFSET_MASK        0xFF
+#define     TWSI_CTRL_LD_OFFSET_SHIFT       0
+#define     TWSI_CTRL_LD_SLV_ADDR_MASK      0x7
+#define     TWSI_CTRL_LD_SLV_ADDR_SHIFT     8
+#define     TWSI_CTRL_SW_LDSTART            0x800
+#define     TWSI_CTRL_HW_LDSTART            0x1000
+#define     TWSI_CTRL_SMB_SLV_ADDR_MASK     0x0x7F
+#define     TWSI_CTRL_SMB_SLV_ADDR_SHIFT    15
+#define     TWSI_CTRL_LD_EXIST              0x400000
+#define     TWSI_CTRL_READ_FREQ_SEL_MASK    0x3
+#define     TWSI_CTRL_READ_FREQ_SEL_SHIFT   23
+#define     TWSI_CTRL_FREQ_SEL_100K         0
+#define     TWSI_CTRL_FREQ_SEL_200K         1
+#define     TWSI_CTRL_FREQ_SEL_300K         2
+#define     TWSI_CTRL_FREQ_SEL_400K         3
+#define     TWSI_CTRL_SMB_SLV_ADDR
+#define     TWSI_CTRL_WRITE_FREQ_SEL_MASK   0x3
+#define     TWSI_CTRL_WRITE_FREQ_SEL_SHIFT  24
+
+
+#define REG_PCIE_DEV_MISC_CTRL      0x21C
+#define     PCIE_DEV_MISC_CTRL_EXT_PIPE     0x2
+#define     PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1
+#define     PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4
+#define     PCIE_DEV_MISC_CTRL_SERDES_ENDIAN    0x8
+#define     PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN   0x10
+
+#define REG_PCIE_PHYMISC	    0x1000
+#define PCIE_PHYMISC_FORCE_RCV_DET	0x4
+
+#define REG_LTSSM_TEST_MODE         0x12FC
+#define         LTSSM_TEST_MODE_DEF     0xE000
+
+/* Selene Master Control Register */
+#define REG_MASTER_CTRL             0x1400
+#define     MASTER_CTRL_SOFT_RST            0x1
+#define     MASTER_CTRL_MTIMER_EN           0x2
+#define     MASTER_CTRL_ITIMER_EN           0x4
+#define     MASTER_CTRL_MANUAL_INT          0x8
+#define     MASTER_CTRL_ITIMER2_EN          0x20
+#define     MASTER_CTRL_INT_RDCLR           0x40
+#define     MASTER_CTRL_LED_MODE	    0x200
+#define     MASTER_CTRL_REV_NUM_SHIFT       16
+#define     MASTER_CTRL_REV_NUM_MASK        0xff
+#define     MASTER_CTRL_DEV_ID_SHIFT        24
+#define     MASTER_CTRL_DEV_ID_MASK         0xff
+
+/* Timer Initial Value Register */
+#define REG_MANUAL_TIMER_INIT       0x1404
+
+
+/* IRQ ModeratorTimer Initial Value Register */
+#define REG_IRQ_MODU_TIMER_INIT     0x1408   /* w */
+#define REG_IRQ_MODU_TIMER2_INIT    0x140A   /* w */
+
+
+#define REG_GPHY_CTRL               0x140C
+#define     GPHY_CTRL_EXT_RESET         1
+#define     GPHY_CTRL_PIPE_MOD          2
+#define     GPHY_CTRL_TEST_MODE_MASK    3
+#define     GPHY_CTRL_TEST_MODE_SHIFT   2
+#define     GPHY_CTRL_BERT_START        0x10
+#define     GPHY_CTRL_GATE_25M_EN       0x20
+#define     GPHY_CTRL_LPW_EXIT          0x40
+#define     GPHY_CTRL_PHY_IDDQ          0x80
+#define     GPHY_CTRL_PHY_IDDQ_DIS      0x100
+#define     GPHY_CTRL_PCLK_SEL_DIS      0x200
+#define     GPHY_CTRL_HIB_EN            0x400
+#define     GPHY_CTRL_HIB_PULSE         0x800
+#define     GPHY_CTRL_SEL_ANA_RST       0x1000
+#define     GPHY_CTRL_PHY_PLL_ON        0x2000
+#define     GPHY_CTRL_PWDOWN_HW		0x4000
+#define     GPHY_CTRL_DEFAULT (\
+		GPHY_CTRL_PHY_PLL_ON	|\
+		GPHY_CTRL_SEL_ANA_RST	|\
+		GPHY_CTRL_HIB_PULSE	|\
+		GPHY_CTRL_HIB_EN)
+
+#define     GPHY_CTRL_PW_WOL_DIS (\
+		GPHY_CTRL_PHY_PLL_ON	|\
+		GPHY_CTRL_SEL_ANA_RST	|\
+		GPHY_CTRL_HIB_PULSE	|\
+		GPHY_CTRL_HIB_EN	|\
+		GPHY_CTRL_PWDOWN_HW	|\
+		GPHY_CTRL_PCLK_SEL_DIS	|\
+		GPHY_CTRL_PHY_IDDQ)
+
+/* IRQ Anti-Lost Timer Initial Value Register */
+#define REG_CMBDISDMA_TIMER         0x140E
+
+
+/* Block IDLE Status Register */
+#define REG_IDLE_STATUS  	0x1410
+#define     IDLE_STATUS_RXMAC       1    /* 1: RXMAC state machine is in non-IDLE state. 0: RXMAC is idling */
+#define     IDLE_STATUS_TXMAC       2    /* 1: TXMAC state machine is in non-IDLE state. 0: TXMAC is idling */
+#define     IDLE_STATUS_RXQ         4    /* 1: RXQ state machine is in non-IDLE state.   0: RXQ is idling   */
+#define     IDLE_STATUS_TXQ         8    /* 1: TXQ state machine is in non-IDLE state.   0: TXQ is idling   */
+#define     IDLE_STATUS_DMAR        0x10 /* 1: DMAR state machine is in non-IDLE state.  0: DMAR is idling  */
+#define     IDLE_STATUS_DMAW        0x20 /* 1: DMAW state machine is in non-IDLE state.  0: DMAW is idling  */
+#define     IDLE_STATUS_SMB         0x40 /* 1: SMB state machine is in non-IDLE state.   0: SMB is idling   */
+#define     IDLE_STATUS_CMB         0x80 /* 1: CMB state machine is in non-IDLE state.   0: CMB is idling   */
+
+/* MDIO Control Register */
+#define REG_MDIO_CTRL           0x1414
+#define     MDIO_DATA_MASK          0xffff  /* On MDIO write, the 16-bit control data to write to PHY MII management register */
+#define     MDIO_DATA_SHIFT         0       /* On MDIO read, the 16-bit status data that was read from the PHY MII management register*/
+#define     MDIO_REG_ADDR_MASK      0x1f    /* MDIO register address */
+#define     MDIO_REG_ADDR_SHIFT     16
+#define     MDIO_RW                 0x200000      /* 1: read, 0: write */
+#define     MDIO_SUP_PREAMBLE       0x400000      /* Suppress preamble */
+#define     MDIO_START              0x800000      /* Write 1 to initiate the MDIO master. And this bit is self cleared after one cycle*/
+#define     MDIO_CLK_SEL_SHIFT      24
+#define     MDIO_CLK_25_4           0
+#define     MDIO_CLK_25_6           2
+#define     MDIO_CLK_25_8           3
+#define     MDIO_CLK_25_10          4
+#define     MDIO_CLK_25_14          5
+#define     MDIO_CLK_25_20          6
+#define     MDIO_CLK_25_28          7
+#define     MDIO_BUSY               0x8000000
+#define     MDIO_AP_EN              0x10000000
+#define MDIO_WAIT_TIMES         10
+
+/* MII PHY Status Register */
+#define REG_PHY_STATUS           0x1418
+#define     PHY_STATUS_100M	      0x20000
+#define     PHY_STATUS_EMI_CA	      0x40000
+
+/* BIST Control and Status Register0 (for the Packet Memory) */
+#define REG_BIST0_CTRL              0x141c
+#define     BIST0_NOW                   0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */
+/* BIST process and reset to zero when BIST is done */
+#define     BIST0_SRAM_FAIL             0x2 /* 1: The SRAM failure is un-repairable because it has address */
+/* decoder failure or more than 1 cell stuck-to-x failure */
+#define     BIST0_FUSE_FLAG             0x4 /* 1: Indicating one cell has been fixed */
+
+/* BIST Control and Status Register1(for the retry buffer of PCI Express) */
+#define REG_BIST1_CTRL              0x1420
+#define     BIST1_NOW                   0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */
+/* BIST process and reset to zero when BIST is done */
+#define     BIST1_SRAM_FAIL             0x2 /* 1: The SRAM failure is un-repairable because it has address */
+/* decoder failure or more than 1 cell stuck-to-x failure.*/
+#define     BIST1_FUSE_FLAG             0x4
+
+/* SerDes Lock Detect Control and Status Register */
+#define REG_SERDES_LOCK             0x1424
+#define     SERDES_LOCK_DETECT          1  /* 1: SerDes lock detected . This signal comes from Analog SerDes */
+#define     SERDES_LOCK_DETECT_EN       2  /* 1: Enable SerDes Lock detect function */
+
+/* MAC Control Register  */
+#define REG_MAC_CTRL                0x1480
+#define     MAC_CTRL_TX_EN              1  /* 1: Transmit Enable */
+#define     MAC_CTRL_RX_EN              2  /* 1: Receive Enable */
+#define     MAC_CTRL_TX_FLOW            4  /* 1: Transmit Flow Control Enable */
+#define     MAC_CTRL_RX_FLOW            8  /* 1: Receive Flow Control Enable */
+#define     MAC_CTRL_LOOPBACK           0x10      /* 1: Loop back at G/MII Interface */
+#define     MAC_CTRL_DUPLX              0x20      /* 1: Full-duplex mode  0: Half-duplex mode */
+#define     MAC_CTRL_ADD_CRC            0x40      /* 1: Instruct MAC to attach CRC on all egress Ethernet frames */
+#define     MAC_CTRL_PAD                0x80      /* 1: Instruct MAC to pad short frames to 60-bytes, and then attach CRC. This bit has higher priority over CRC_EN */
+#define     MAC_CTRL_LENCHK             0x100     /* 1: Instruct MAC to check if length field matches the real packet length */
+#define     MAC_CTRL_HUGE_EN            0x200     /* 1: receive Jumbo frame enable */
+#define     MAC_CTRL_PRMLEN_SHIFT       10        /* Preamble length */
+#define     MAC_CTRL_PRMLEN_MASK        0xf
+#define     MAC_CTRL_RMV_VLAN           0x4000    /* 1: to remove VLAN Tag automatically from all receive packets */
+#define     MAC_CTRL_PROMIS_EN          0x8000    /* 1: Promiscuous Mode Enable */
+#define     MAC_CTRL_TX_PAUSE           0x10000   /* 1: transmit test pause */
+#define     MAC_CTRL_SCNT               0x20000   /* 1: shortcut slot time counter */
+#define     MAC_CTRL_SRST_TX            0x40000   /* 1: synchronized reset Transmit MAC module */
+#define     MAC_CTRL_TX_SIMURST         0x80000   /* 1: transmit simulation reset */
+#define     MAC_CTRL_SPEED_SHIFT        20        /* 10: gigabit 01:10M/100M */
+#define     MAC_CTRL_SPEED_MASK         0x300000
+#define     MAC_CTRL_SPEED_1000         2
+#define     MAC_CTRL_SPEED_10_100       1
+#define     MAC_CTRL_DBG_TX_BKPRESURE   0x400000  /* 1: transmit maximum backoff (half-duplex test bit) */
+#define     MAC_CTRL_TX_HUGE            0x800000  /* 1: transmit huge enable */
+#define     MAC_CTRL_RX_CHKSUM_EN       0x1000000 /* 1: RX checksum enable */
+#define     MAC_CTRL_MC_ALL_EN          0x2000000 /* 1: upload all multicast frame without error to system */
+#define     MAC_CTRL_BC_EN              0x4000000 /* 1: upload all broadcast frame without error to system */
+#define     MAC_CTRL_DBG                0x8000000 /* 1: upload all received frame to system (Debug Mode) */
+
+/* MAC IPG/IFG Control Register  */
+#define REG_MAC_IPG_IFG             0x1484
+#define     MAC_IPG_IFG_IPGT_SHIFT      0     /* Desired back to back inter-packet gap. The default is 96-bit time */
+#define     MAC_IPG_IFG_IPGT_MASK       0x7f
+#define     MAC_IPG_IFG_MIFG_SHIFT      8     /* Minimum number of IFG to enforce in between RX frames */
+#define     MAC_IPG_IFG_MIFG_MASK       0xff  /* Frame gap below such IFP is dropped */
+#define     MAC_IPG_IFG_IPGR1_SHIFT     16    /* 64bit Carrier-Sense window */
+#define     MAC_IPG_IFG_IPGR1_MASK      0x7f
+#define     MAC_IPG_IFG_IPGR2_SHIFT     24    /* 96-bit IPG window */
+#define     MAC_IPG_IFG_IPGR2_MASK      0x7f
+
+/* MAC STATION ADDRESS  */
+#define REG_MAC_STA_ADDR            0x1488
+
+/* Hash table for multicast address */
+#define REG_RX_HASH_TABLE           0x1490
+
+
+/* MAC Half-Duplex Control Register */
+#define REG_MAC_HALF_DUPLX_CTRL     0x1498
+#define     MAC_HALF_DUPLX_CTRL_LCOL_SHIFT   0      /* Collision Window */
+#define     MAC_HALF_DUPLX_CTRL_LCOL_MASK    0x3ff
+#define     MAC_HALF_DUPLX_CTRL_RETRY_SHIFT  12     /* Retransmission maximum, afterwards the packet will be discarded */
+#define     MAC_HALF_DUPLX_CTRL_RETRY_MASK   0xf
+#define     MAC_HALF_DUPLX_CTRL_EXC_DEF_EN   0x10000 /* 1: Allow the transmission of a packet which has been excessively deferred */
+#define     MAC_HALF_DUPLX_CTRL_NO_BACK_C    0x20000 /* 1: No back-off on collision, immediately start the retransmission */
+#define     MAC_HALF_DUPLX_CTRL_NO_BACK_P    0x40000 /* 1: No back-off on backpressure, immediately start the transmission after back pressure */
+#define     MAC_HALF_DUPLX_CTRL_ABEBE        0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */
+#define     MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT  20      /* Maximum binary exponential number */
+#define     MAC_HALF_DUPLX_CTRL_ABEBT_MASK   0xf
+#define     MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24      /* IPG to start JAM for collision based flow control in half-duplex */
+#define     MAC_HALF_DUPLX_CTRL_JAMIPG_MASK  0xf     /* mode. In unit of 8-bit time */
+
+/* Maximum Frame Length Control Register   */
+#define REG_MTU                     0x149c
+
+/* Wake-On-Lan control register */
+#define REG_WOL_CTRL                0x14a0
+#define     WOL_PATTERN_EN                  0x00000001
+#define     WOL_PATTERN_PME_EN              0x00000002
+#define     WOL_MAGIC_EN                    0x00000004
+#define     WOL_MAGIC_PME_EN                0x00000008
+#define     WOL_LINK_CHG_EN                 0x00000010
+#define     WOL_LINK_CHG_PME_EN             0x00000020
+#define     WOL_PATTERN_ST                  0x00000100
+#define     WOL_MAGIC_ST                    0x00000200
+#define     WOL_LINKCHG_ST                  0x00000400
+#define     WOL_CLK_SWITCH_EN               0x00008000
+#define     WOL_PT0_EN                      0x00010000
+#define     WOL_PT1_EN                      0x00020000
+#define     WOL_PT2_EN                      0x00040000
+#define     WOL_PT3_EN                      0x00080000
+#define     WOL_PT4_EN                      0x00100000
+#define     WOL_PT5_EN                      0x00200000
+#define     WOL_PT6_EN                      0x00400000
+/* WOL Length ( 2 DWORD ) */
+#define REG_WOL_PATTERN_LEN         0x14a4
+#define     WOL_PT_LEN_MASK                 0x7f
+#define     WOL_PT0_LEN_SHIFT               0
+#define     WOL_PT1_LEN_SHIFT               8
+#define     WOL_PT2_LEN_SHIFT               16
+#define     WOL_PT3_LEN_SHIFT               24
+#define     WOL_PT4_LEN_SHIFT               0
+#define     WOL_PT5_LEN_SHIFT               8
+#define     WOL_PT6_LEN_SHIFT               16
+
+/* Internal SRAM Partition Register */
+#define REG_SRAM_TRD_ADDR           0x1518
+#define REG_SRAM_TRD_LEN            0x151C
+#define REG_SRAM_RXF_ADDR           0x1520
+#define REG_SRAM_RXF_LEN            0x1524
+#define REG_SRAM_TXF_ADDR           0x1528
+#define REG_SRAM_TXF_LEN            0x152C
+#define REG_SRAM_TCPH_ADDR          0x1530
+#define REG_SRAM_PKTH_ADDR          0x1532
+
+/* Load Ptr Register */
+#define REG_LOAD_PTR                0x1534  /* Software sets this bit after the initialization of the head and tail */
+
+/*
+ * addresses of all descriptors, as well as the following descriptor
+ * control register, which triggers each function block to load the head
+ * pointer to prepare for the operation. This bit is then self-cleared
+ * after one cycle.
+ */
+
+/* Descriptor Control register  */
+#define REG_RXF3_BASE_ADDR_HI           0x153C
+#define REG_DESC_BASE_ADDR_HI           0x1540
+#define REG_RXF0_BASE_ADDR_HI           0x1540 /* share with DESC BASE ADDR HI */
+#define REG_HOST_RXF0_PAGE0_LO          0x1544
+#define REG_HOST_RXF0_PAGE1_LO          0x1548
+#define REG_TPD_BASE_ADDR_LO            0x154C
+#define REG_RXF1_BASE_ADDR_HI           0x1550
+#define REG_RXF2_BASE_ADDR_HI           0x1554
+#define REG_HOST_RXFPAGE_SIZE           0x1558
+#define REG_TPD_RING_SIZE               0x155C
+/* RSS about */
+#define REG_RSS_KEY0                    0x14B0
+#define REG_RSS_KEY1                    0x14B4
+#define REG_RSS_KEY2                    0x14B8
+#define REG_RSS_KEY3                    0x14BC
+#define REG_RSS_KEY4                    0x14C0
+#define REG_RSS_KEY5                    0x14C4
+#define REG_RSS_KEY6                    0x14C8
+#define REG_RSS_KEY7                    0x14CC
+#define REG_RSS_KEY8                    0x14D0
+#define REG_RSS_KEY9                    0x14D4
+#define REG_IDT_TABLE4                  0x14E0
+#define REG_IDT_TABLE5                  0x14E4
+#define REG_IDT_TABLE6                  0x14E8
+#define REG_IDT_TABLE7                  0x14EC
+#define REG_IDT_TABLE0                  0x1560
+#define REG_IDT_TABLE1                  0x1564
+#define REG_IDT_TABLE2                  0x1568
+#define REG_IDT_TABLE3                  0x156C
+#define REG_IDT_TABLE                   REG_IDT_TABLE0
+#define REG_RSS_HASH_VALUE              0x1570
+#define REG_RSS_HASH_FLAG               0x1574
+#define REG_BASE_CPU_NUMBER             0x157C
+
+
+/* TXQ Control Register */
+#define REG_TXQ_CTRL                0x1580
+#define     TXQ_CTRL_NUM_TPD_BURST_MASK     0xF
+#define     TXQ_CTRL_NUM_TPD_BURST_SHIFT    0
+#define     TXQ_CTRL_EN                     0x20  /* 1: Enable TXQ */
+#define     TXQ_CTRL_ENH_MODE               0x40  /* Performance enhancement mode, in which up to two back-to-back DMA read commands might be dispatched. */
+#define     TXQ_CTRL_TXF_BURST_NUM_SHIFT    16    /* Number of data byte to read in a cache-aligned burst. Each SRAM entry is 8-byte in length. */
+#define     TXQ_CTRL_TXF_BURST_NUM_MASK     0xffff
+
+/* Jumbo packet Threshold for task offload */
+#define REG_TX_EARLY_TH                     0x1584 /* Jumbo frame threshold in QWORD unit. Packet greater than */
+/* JUMBO_TASK_OFFLOAD_THRESHOLD will not be task offloaded. */
+#define     TX_TX_EARLY_TH_MASK             0x7ff
+#define     TX_TX_EARLY_TH_SHIFT            0
+
+
+/* RXQ Control Register */
+#define REG_RXQ_CTRL                0x15A0
+#define         RXQ_CTRL_PBA_ALIGN_32                   0   /* rx-packet alignment */
+#define         RXQ_CTRL_PBA_ALIGN_64                   1
+#define         RXQ_CTRL_PBA_ALIGN_128                  2
+#define         RXQ_CTRL_PBA_ALIGN_256                  3
+#define         RXQ_CTRL_Q1_EN				0x10
+#define         RXQ_CTRL_Q2_EN				0x20
+#define         RXQ_CTRL_Q3_EN				0x40
+#define         RXQ_CTRL_IPV6_XSUM_VERIFY_EN		0x80
+#define         RXQ_CTRL_HASH_TLEN_SHIFT                8
+#define         RXQ_CTRL_HASH_TLEN_MASK                 0xFF
+#define         RXQ_CTRL_HASH_TYPE_IPV4                 0x10000
+#define         RXQ_CTRL_HASH_TYPE_IPV4_TCP             0x20000
+#define         RXQ_CTRL_HASH_TYPE_IPV6                 0x40000
+#define         RXQ_CTRL_HASH_TYPE_IPV6_TCP             0x80000
+#define         RXQ_CTRL_RSS_MODE_DISABLE               0
+#define         RXQ_CTRL_RSS_MODE_SQSINT                0x4000000
+#define         RXQ_CTRL_RSS_MODE_MQUESINT              0x8000000
+#define         RXQ_CTRL_RSS_MODE_MQUEMINT              0xC000000
+#define         RXQ_CTRL_NIP_QUEUE_SEL_TBL              0x10000000
+#define         RXQ_CTRL_HASH_ENABLE                    0x20000000
+#define         RXQ_CTRL_CUT_THRU_EN                    0x40000000
+#define         RXQ_CTRL_EN                             0x80000000
+
+/* Rx jumbo packet threshold and rrd  retirement timer  */
+#define REG_RXQ_JMBOSZ_RRDTIM       0x15A4
+/*
+ * Jumbo packet threshold for non-VLAN packet, in QWORD (64-bit) unit.
+ * When the packet length greater than or equal to this value, RXQ
+ * shall start cut-through forwarding of the received packet.
+ */
+#define         RXQ_JMBOSZ_TH_MASK      0x7ff
+#define         RXQ_JMBOSZ_TH_SHIFT         0  /* RRD retirement timer. Decrement by 1 after every 512ns passes*/
+#define         RXQ_JMBO_LKAH_MASK          0xf
+#define         RXQ_JMBO_LKAH_SHIFT         11
+
+/* RXF flow control register */
+#define REG_RXQ_RXF_PAUSE_THRESH    0x15A8
+#define     RXQ_RXF_PAUSE_TH_HI_SHIFT       0
+#define     RXQ_RXF_PAUSE_TH_HI_MASK        0xfff
+#define     RXQ_RXF_PAUSE_TH_LO_SHIFT       16
+#define     RXQ_RXF_PAUSE_TH_LO_MASK        0xfff
+
+
+/* DMA Engine Control Register */
+#define REG_DMA_CTRL                0x15C0
+#define     DMA_CTRL_DMAR_IN_ORDER          0x1
+#define     DMA_CTRL_DMAR_ENH_ORDER         0x2
+#define     DMA_CTRL_DMAR_OUT_ORDER         0x4
+#define     DMA_CTRL_RCB_VALUE              0x8
+#define     DMA_CTRL_DMAR_BURST_LEN_SHIFT   4
+#define     DMA_CTRL_DMAR_BURST_LEN_MASK    7
+#define     DMA_CTRL_DMAW_BURST_LEN_SHIFT   7
+#define     DMA_CTRL_DMAW_BURST_LEN_MASK    7
+#define     DMA_CTRL_DMAR_REQ_PRI           0x400
+#define     DMA_CTRL_DMAR_DLY_CNT_MASK      0x1F
+#define     DMA_CTRL_DMAR_DLY_CNT_SHIFT     11
+#define     DMA_CTRL_DMAW_DLY_CNT_MASK      0xF
+#define     DMA_CTRL_DMAW_DLY_CNT_SHIFT     16
+#define     DMA_CTRL_TXCMB_EN               0x100000
+#define     DMA_CTRL_RXCMB_EN				0x200000
+
+
+/* CMB/SMB Control Register */
+#define REG_SMB_STAT_TIMER                      0x15C4
+#define REG_TRIG_RRD_THRESH                     0x15CA
+#define REG_TRIG_TPD_THRESH                     0x15C8
+#define REG_TRIG_TXTIMER                        0x15CC
+#define REG_TRIG_RXTIMER                        0x15CE
+
+/* HOST RXF Page 1,2,3 address */
+#define REG_HOST_RXF1_PAGE0_LO                  0x15D0
+#define REG_HOST_RXF1_PAGE1_LO                  0x15D4
+#define REG_HOST_RXF2_PAGE0_LO                  0x15D8
+#define REG_HOST_RXF2_PAGE1_LO                  0x15DC
+#define REG_HOST_RXF3_PAGE0_LO                  0x15E0
+#define REG_HOST_RXF3_PAGE1_LO                  0x15E4
+
+/* Mail box */
+#define REG_MB_RXF1_RADDR                       0x15B4
+#define REG_MB_RXF2_RADDR                       0x15B8
+#define REG_MB_RXF3_RADDR                       0x15BC
+#define REG_MB_TPD_PROD_IDX                     0x15F0
+
+/* RXF-Page 0-3  PageNo & Valid bit */
+#define REG_HOST_RXF0_PAGE0_VLD     0x15F4
+#define     HOST_RXF_VALID              1
+#define     HOST_RXF_PAGENO_SHIFT       1
+#define     HOST_RXF_PAGENO_MASK        0x7F
+#define REG_HOST_RXF0_PAGE1_VLD     0x15F5
+#define REG_HOST_RXF1_PAGE0_VLD     0x15F6
+#define REG_HOST_RXF1_PAGE1_VLD     0x15F7
+#define REG_HOST_RXF2_PAGE0_VLD     0x15F8
+#define REG_HOST_RXF2_PAGE1_VLD     0x15F9
+#define REG_HOST_RXF3_PAGE0_VLD     0x15FA
+#define REG_HOST_RXF3_PAGE1_VLD     0x15FB
+
+/* Interrupt Status Register */
+#define REG_ISR    0x1600
+#define  ISR_SMB   		1
+#define  ISR_TIMER		2       /* Interrupt when Timer is counted down to zero */
+/*
+ * Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set
+ * in Table 51 Selene Master Control Register (Offset 0x1400).
+ */
+#define  ISR_MANUAL         	4
+#define  ISR_HW_RXF_OV          8        /* RXF overflow interrupt */
+#define  ISR_HOST_RXF0_OV       0x10
+#define  ISR_HOST_RXF1_OV       0x20
+#define  ISR_HOST_RXF2_OV       0x40
+#define  ISR_HOST_RXF3_OV       0x80
+#define  ISR_TXF_UN             0x100
+#define  ISR_RX0_PAGE_FULL      0x200
+#define  ISR_DMAR_TO_RST        0x400
+#define  ISR_DMAW_TO_RST        0x800
+#define  ISR_GPHY               0x1000
+#define  ISR_TX_CREDIT          0x2000
+#define  ISR_GPHY_LPW           0x4000    /* GPHY low power state interrupt */
+#define  ISR_RX_PKT             0x10000   /* One packet received, triggered by RFD */
+#define  ISR_TX_PKT             0x20000   /* One packet transmitted, triggered by TPD */
+#define  ISR_TX_DMA             0x40000
+#define  ISR_RX_PKT_1           0x80000
+#define  ISR_RX_PKT_2           0x100000
+#define  ISR_RX_PKT_3           0x200000
+#define  ISR_MAC_RX             0x400000
+#define  ISR_MAC_TX             0x800000
+#define  ISR_UR_DETECTED        0x1000000
+#define  ISR_FERR_DETECTED      0x2000000
+#define  ISR_NFERR_DETECTED     0x4000000
+#define  ISR_CERR_DETECTED      0x8000000
+#define  ISR_PHY_LINKDOWN       0x10000000
+#define  ISR_DIS_INT            0x80000000
+
+
+/* Interrupt Mask Register */
+#define REG_IMR 0x1604
+
+
+#define IMR_NORMAL_MASK (\
+		ISR_SMB	        |\
+		ISR_TXF_UN      |\
+		ISR_HW_RXF_OV   |\
+		ISR_HOST_RXF0_OV|\
+		ISR_MANUAL      |\
+		ISR_GPHY        |\
+		ISR_GPHY_LPW    |\
+		ISR_DMAR_TO_RST |\
+		ISR_DMAW_TO_RST |\
+		ISR_PHY_LINKDOWN|\
+		ISR_RX_PKT      |\
+		ISR_TX_PKT)
+
+#define ISR_TX_EVENT (ISR_TXF_UN | ISR_TX_PKT)
+#define ISR_RX_EVENT (ISR_HOST_RXF0_OV | ISR_HW_RXF_OV | ISR_RX_PKT)
+
+#define REG_MAC_RX_STATUS_BIN 0x1700
+#define REG_MAC_RX_STATUS_END 0x175c
+#define REG_MAC_TX_STATUS_BIN 0x1760
+#define REG_MAC_TX_STATUS_END 0x17c0
+
+/* Hardware Offset Register */
+#define REG_HOST_RXF0_PAGEOFF 0x1800
+#define REG_TPD_CONS_IDX      0x1804
+#define REG_HOST_RXF1_PAGEOFF 0x1808
+#define REG_HOST_RXF2_PAGEOFF 0x180C
+#define REG_HOST_RXF3_PAGEOFF 0x1810
+
+/* RXF-Page 0-3 Offset DMA Address */
+#define REG_HOST_RXF0_MB0_LO  0x1820
+#define REG_HOST_RXF0_MB1_LO  0x1824
+#define REG_HOST_RXF1_MB0_LO  0x1828
+#define REG_HOST_RXF1_MB1_LO  0x182C
+#define REG_HOST_RXF2_MB0_LO  0x1830
+#define REG_HOST_RXF2_MB1_LO  0x1834
+#define REG_HOST_RXF3_MB0_LO  0x1838
+#define REG_HOST_RXF3_MB1_LO  0x183C
+
+/* Tpd CMB DMA Address */
+#define REG_HOST_TX_CMB_LO    0x1840
+#define REG_HOST_SMB_ADDR_LO  0x1844
+
+/* DEBUG ADDR */
+#define REG_DEBUG_DATA0 0x1900
+#define REG_DEBUG_DATA1 0x1904
+
+/***************************** MII definition ***************************************/
+/* PHY Common Register */
+#define MII_BMCR                        0x00
+#define MII_BMSR                        0x01
+#define MII_PHYSID1                     0x02
+#define MII_PHYSID2                     0x03
+#define MII_ADVERTISE                   0x04
+#define MII_LPA                         0x05
+#define MII_EXPANSION                   0x06
+#define MII_AT001_CR                    0x09
+#define MII_AT001_SR                    0x0A
+#define MII_AT001_ESR                   0x0F
+#define MII_AT001_PSCR                  0x10
+#define MII_AT001_PSSR                  0x11
+#define MII_INT_CTRL                    0x12
+#define MII_INT_STATUS                  0x13
+#define MII_SMARTSPEED                  0x14
+#define MII_RERRCOUNTER                 0x15
+#define MII_SREVISION                   0x16
+#define MII_RESV1                       0x17
+#define MII_LBRERROR                    0x18
+#define MII_PHYADDR                     0x19
+#define MII_RESV2                       0x1a
+#define MII_TPISTATUS                   0x1b
+#define MII_NCONFIG                     0x1c
+
+#define MII_DBG_ADDR			0x1D
+#define MII_DBG_DATA			0x1E
+
+
+/* PHY Control Register */
+#define MII_CR_SPEED_SELECT_MSB                  0x0040  /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_COLL_TEST_ENABLE                  0x0080  /* Collision test enable */
+#define MII_CR_FULL_DUPLEX                       0x0100  /* FDX =1, half duplex =0 */
+#define MII_CR_RESTART_AUTO_NEG                  0x0200  /* Restart auto negotiation */
+#define MII_CR_ISOLATE                           0x0400  /* Isolate PHY from MII */
+#define MII_CR_POWER_DOWN                        0x0800  /* Power down */
+#define MII_CR_AUTO_NEG_EN                       0x1000  /* Auto Neg Enable */
+#define MII_CR_SPEED_SELECT_LSB                  0x2000  /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_LOOPBACK                          0x4000  /* 0 = normal, 1 = loopback */
+#define MII_CR_RESET                             0x8000  /* 0 = normal, 1 = PHY reset */
+#define MII_CR_SPEED_MASK                        0x2040
+#define MII_CR_SPEED_1000                        0x0040
+#define MII_CR_SPEED_100                         0x2000
+#define MII_CR_SPEED_10                          0x0000
+
+
+/* PHY Status Register */
+#define MII_SR_EXTENDED_CAPS                     0x0001  /* Extended register capabilities */
+#define MII_SR_JABBER_DETECT                     0x0002  /* Jabber Detected */
+#define MII_SR_LINK_STATUS                       0x0004  /* Link Status 1 = link */
+#define MII_SR_AUTONEG_CAPS                      0x0008  /* Auto Neg Capable */
+#define MII_SR_REMOTE_FAULT                      0x0010  /* Remote Fault Detect */
+#define MII_SR_AUTONEG_COMPLETE                  0x0020  /* Auto Neg Complete */
+#define MII_SR_PREAMBLE_SUPPRESS                 0x0040  /* Preamble may be suppressed */
+#define MII_SR_EXTENDED_STATUS                   0x0100  /* Ext. status info in Reg 0x0F */
+#define MII_SR_100T2_HD_CAPS                     0x0200  /* 100T2 Half Duplex Capable */
+#define MII_SR_100T2_FD_CAPS                     0x0400  /* 100T2 Full Duplex Capable */
+#define MII_SR_10T_HD_CAPS                       0x0800  /* 10T   Half Duplex Capable */
+#define MII_SR_10T_FD_CAPS                       0x1000  /* 10T   Full Duplex Capable */
+#define MII_SR_100X_HD_CAPS                      0x2000  /* 100X  Half Duplex Capable */
+#define MII_SR_100X_FD_CAPS                      0x4000  /* 100X  Full Duplex Capable */
+#define MII_SR_100T4_CAPS                        0x8000  /* 100T4 Capable */
+
+/* Link partner ability register. */
+#define MII_LPA_SLCT                             0x001f  /* Same as advertise selector  */
+#define MII_LPA_10HALF                           0x0020  /* Can do 10mbps half-duplex   */
+#define MII_LPA_10FULL                           0x0040  /* Can do 10mbps full-duplex   */
+#define MII_LPA_100HALF                          0x0080  /* Can do 100mbps half-duplex  */
+#define MII_LPA_100FULL                          0x0100  /* Can do 100mbps full-duplex  */
+#define MII_LPA_100BASE4                         0x0200  /* 100BASE-T4  */
+#define MII_LPA_PAUSE                            0x0400  /* PAUSE */
+#define MII_LPA_ASYPAUSE                         0x0800  /* Asymmetrical PAUSE */
+#define MII_LPA_RFAULT                           0x2000  /* Link partner faulted        */
+#define MII_LPA_LPACK                            0x4000  /* Link partner acked us       */
+#define MII_LPA_NPAGE                            0x8000  /* Next page bit               */
+
+/* Autoneg Advertisement Register */
+#define MII_AR_SELECTOR_FIELD                   0x0001  /* indicates IEEE 802.3 CSMA/CD */
+#define MII_AR_10T_HD_CAPS                      0x0020  /* 10T   Half Duplex Capable */
+#define MII_AR_10T_FD_CAPS                      0x0040  /* 10T   Full Duplex Capable */
+#define MII_AR_100TX_HD_CAPS                    0x0080  /* 100TX Half Duplex Capable */
+#define MII_AR_100TX_FD_CAPS                    0x0100  /* 100TX Full Duplex Capable */
+#define MII_AR_100T4_CAPS                       0x0200  /* 100T4 Capable */
+#define MII_AR_PAUSE                            0x0400  /* Pause operation desired */
+#define MII_AR_ASM_DIR                          0x0800  /* Asymmetric Pause Direction bit */
+#define MII_AR_REMOTE_FAULT                     0x2000  /* Remote Fault detected */
+#define MII_AR_NEXT_PAGE                        0x8000  /* Next Page ability supported */
+#define MII_AR_SPEED_MASK                       0x01E0
+#define MII_AR_DEFAULT_CAP_MASK                 0x0DE0
+
+/* 1000BASE-T Control Register */
+#define MII_AT001_CR_1000T_HD_CAPS              0x0100  /* Advertise 1000T HD capability */
+#define MII_AT001_CR_1000T_FD_CAPS              0x0200  /* Advertise 1000T FD capability  */
+#define MII_AT001_CR_1000T_REPEATER_DTE         0x0400  /* 1=Repeater/switch device port */
+/* 0=DTE device */
+#define MII_AT001_CR_1000T_MS_VALUE             0x0800  /* 1=Configure PHY as Master */
+/* 0=Configure PHY as Slave */
+#define MII_AT001_CR_1000T_MS_ENABLE            0x1000  /* 1=Master/Slave manual config value */
+/* 0=Automatic Master/Slave config */
+#define MII_AT001_CR_1000T_TEST_MODE_NORMAL     0x0000  /* Normal Operation */
+#define MII_AT001_CR_1000T_TEST_MODE_1          0x2000  /* Transmit Waveform test */
+#define MII_AT001_CR_1000T_TEST_MODE_2          0x4000  /* Master Transmit Jitter test */
+#define MII_AT001_CR_1000T_TEST_MODE_3          0x6000  /* Slave Transmit Jitter test */
+#define MII_AT001_CR_1000T_TEST_MODE_4          0x8000  /* Transmitter Distortion test */
+#define MII_AT001_CR_1000T_SPEED_MASK           0x0300
+#define MII_AT001_CR_1000T_DEFAULT_CAP_MASK     0x0300
+
+/* 1000BASE-T Status Register */
+#define MII_AT001_SR_1000T_LP_HD_CAPS           0x0400  /* LP is 1000T HD capable */
+#define MII_AT001_SR_1000T_LP_FD_CAPS           0x0800  /* LP is 1000T FD capable */
+#define MII_AT001_SR_1000T_REMOTE_RX_STATUS     0x1000  /* Remote receiver OK */
+#define MII_AT001_SR_1000T_LOCAL_RX_STATUS      0x2000  /* Local receiver OK */
+#define MII_AT001_SR_1000T_MS_CONFIG_RES        0x4000  /* 1=Local TX is Master, 0=Slave */
+#define MII_AT001_SR_1000T_MS_CONFIG_FAULT      0x8000  /* Master/Slave config fault */
+#define MII_AT001_SR_1000T_REMOTE_RX_STATUS_SHIFT   12
+#define MII_AT001_SR_1000T_LOCAL_RX_STATUS_SHIFT    13
+
+/* Extended Status Register */
+#define MII_AT001_ESR_1000T_HD_CAPS             0x1000  /* 1000T HD capable */
+#define MII_AT001_ESR_1000T_FD_CAPS             0x2000  /* 1000T FD capable */
+#define MII_AT001_ESR_1000X_HD_CAPS             0x4000  /* 1000X HD capable */
+#define MII_AT001_ESR_1000X_FD_CAPS             0x8000  /* 1000X FD capable */
+
+/* AT001 PHY Specific Control Register */
+#define MII_AT001_PSCR_JABBER_DISABLE           0x0001  /* 1=Jabber Function disabled */
+#define MII_AT001_PSCR_POLARITY_REVERSAL        0x0002  /* 1=Polarity Reversal enabled */
+#define MII_AT001_PSCR_SQE_TEST                 0x0004  /* 1=SQE Test enabled */
+#define MII_AT001_PSCR_MAC_POWERDOWN            0x0008
+#define MII_AT001_PSCR_CLK125_DISABLE           0x0010  /* 1=CLK125 low,
+							 * 0=CLK125 toggling
+							 */
+#define MII_AT001_PSCR_MDI_MANUAL_MODE          0x0000  /* MDI Crossover Mode bits 6:5 */
+/* Manual MDI configuration */
+#define MII_AT001_PSCR_MDIX_MANUAL_MODE         0x0020  /* Manual MDIX configuration */
+#define MII_AT001_PSCR_AUTO_X_1000T             0x0040  /* 1000BASE-T: Auto crossover,
+							 *  100BASE-TX/10BASE-T:
+							 *  MDI Mode
+							 */
+#define MII_AT001_PSCR_AUTO_X_MODE              0x0060  /* Auto crossover enabled
+							 * all speeds.
+							 */
+#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE     0x0080
+/* 1=Enable Extended 10BASE-T distance
+ * (Lower 10BASE-T RX Threshold)
+ * 0=Normal 10BASE-T RX Threshold */
+#define MII_AT001_PSCR_MII_5BIT_ENABLE          0x0100
+/* 1=5-Bit interface in 100BASE-TX
+ * 0=MII interface in 100BASE-TX */
+#define MII_AT001_PSCR_SCRAMBLER_DISABLE        0x0200  /* 1=Scrambler disable */
+#define MII_AT001_PSCR_FORCE_LINK_GOOD          0x0400  /* 1=Force link good */
+#define MII_AT001_PSCR_ASSERT_CRS_ON_TX         0x0800  /* 1=Assert CRS on Transmit */
+#define MII_AT001_PSCR_POLARITY_REVERSAL_SHIFT    1
+#define MII_AT001_PSCR_AUTO_X_MODE_SHIFT          5
+#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7
+/* AT001 PHY Specific Status Register */
+#define MII_AT001_PSSR_SPD_DPLX_RESOLVED        0x0800  /* 1=Speed & Duplex resolved */
+#define MII_AT001_PSSR_DPLX                     0x2000  /* 1=Duplex 0=Half Duplex */
+#define MII_AT001_PSSR_SPEED                    0xC000  /* Speed, bits 14:15 */
+#define MII_AT001_PSSR_10MBS                    0x0000  /* 00=10Mbs */
+#define MII_AT001_PSSR_100MBS                   0x4000  /* 01=100Mbs */
+#define MII_AT001_PSSR_1000MBS                  0x8000  /* 10=1000Mbs */
+
+
+#endif /* _ATL1_E_H_ */
diff --git a/gpxe/src/drivers/net/b44.c b/gpxe/src/drivers/net/b44.c
index 4c9c8e6..c48b314 100644
--- a/gpxe/src/drivers/net/b44.c
+++ b/gpxe/src/drivers/net/b44.c
@@ -28,6 +28,8 @@
  * Copyright (c) a lot of people too. Please respect their work.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <errno.h>
 #include <assert.h>
 #include <stdio.h>
@@ -592,12 +594,12 @@ static void b44_load_mac_and_phy_addr(struct b44_private *bp)
 
 	/* Load MAC address, note byteswapping */
 	b44_read_eeprom(bp, &eeprom[0]);
-	bp->netdev->ll_addr[0] = eeprom[79];
-	bp->netdev->ll_addr[1] = eeprom[78];
-	bp->netdev->ll_addr[2] = eeprom[81];
-	bp->netdev->ll_addr[3] = eeprom[80];
-	bp->netdev->ll_addr[4] = eeprom[83];
-	bp->netdev->ll_addr[5] = eeprom[82];
+	bp->netdev->hw_addr[0] = eeprom[79];
+	bp->netdev->hw_addr[1] = eeprom[78];
+	bp->netdev->hw_addr[2] = eeprom[81];
+	bp->netdev->hw_addr[3] = eeprom[80];
+	bp->netdev->hw_addr[4] = eeprom[83];
+	bp->netdev->hw_addr[5] = eeprom[82];
 
 	/* Load PHY address */
 	bp->phy_addr = eeprom[90] & 0x1f;
@@ -935,9 +937,9 @@ static struct net_device_operations b44_operations = {
 
 
 static struct pci_device_id b44_nics[] = {
-	PCI_ROM(0x14e4, 0x4401, "BCM4401", "BCM4401"),
-	PCI_ROM(0x14e4, 0x170c, "BCM4401-B0", "BCM4401-B0"),
-	PCI_ROM(0x14e4, 0x4402, "BCM4401-B1", "BCM4401-B1"),
+	PCI_ROM(0x14e4, 0x4401, "BCM4401", "BCM4401", 0),
+	PCI_ROM(0x14e4, 0x170c, "BCM4401-B0", "BCM4401-B0", 0),
+	PCI_ROM(0x14e4, 0x4402, "BCM4401-B1", "BCM4401-B1", 0),
 };
 
 
diff --git a/gpxe/src/drivers/net/b44.h b/gpxe/src/drivers/net/b44.h
index fb36757..b5afcbd 100644
--- a/gpxe/src/drivers/net/b44.h
+++ b/gpxe/src/drivers/net/b44.h
@@ -27,6 +27,9 @@
  *
  * Copyright (c) a lot of people too. Please respect their work.
  */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef _B44_H
 #define _B44_H
 
diff --git a/gpxe/src/drivers/net/bnx2.c b/gpxe/src/drivers/net/bnx2.c
index 9c989c4..c385dd8 100644
--- a/gpxe/src/drivers/net/bnx2.c
+++ b/gpxe/src/drivers/net/bnx2.c
@@ -12,6 +12,7 @@
  * version 1.4.40 from linux 2.6.17
  */
 
+FILE_LICENCE ( GPL_ANY );
 
 #include "etherboot.h"
 #include "nic.h"
@@ -19,6 +20,7 @@
 #include <gpxe/pci.h>
 #include <gpxe/ethernet.h>
 #include "string.h"
+#include <mii.h>
 #include "bnx2.h"
 #include "bnx2_fw.h"
 
@@ -2673,10 +2675,10 @@ err_out_disable:
 }
 
 static struct pci_device_id bnx2_nics[] = {
-	PCI_ROM(0x14e4, 0x164a, "bnx2-5706",        "Broadcom NetXtreme II BCM5706"),
-	PCI_ROM(0x14e4, 0x164c, "bnx2-5708",        "Broadcom NetXtreme II BCM5708"),
-	PCI_ROM(0x14e4, 0x16aa, "bnx2-5706S",       "Broadcom NetXtreme II BCM5706S"),
-	PCI_ROM(0x14e4, 0x16ac, "bnx2-5708S",       "Broadcom NetXtreme II BCM5708S"),
+	PCI_ROM(0x14e4, 0x164a, "bnx2-5706",        "Broadcom NetXtreme II BCM5706", 0),
+	PCI_ROM(0x14e4, 0x164c, "bnx2-5708",        "Broadcom NetXtreme II BCM5708", 0),
+	PCI_ROM(0x14e4, 0x16aa, "bnx2-5706S",       "Broadcom NetXtreme II BCM5706S", 0),
+	PCI_ROM(0x14e4, 0x16ac, "bnx2-5708S",       "Broadcom NetXtreme II BCM5708S", 0),
 };
 
 PCI_DRIVER ( bnx2_driver, bnx2_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/bnx2.h b/gpxe/src/drivers/net/bnx2.h
index 57d5fee..9267868 100644
--- a/gpxe/src/drivers/net/bnx2.h
+++ b/gpxe/src/drivers/net/bnx2.h
@@ -9,6 +9,7 @@
  * Written by: Michael Chan  (mchan at broadcom.com)
  */
 
+FILE_LICENCE ( GPL_ANY );
 
 #ifndef BNX2_H
 #define BNX2_H
@@ -91,113 +92,6 @@ typedef int pci_power_t;
 #define WAKE_MAGIC		(1 << 5)
 #define WAKE_MAGICSECURE	(1 << 6) /* only meaningful if WAKE_MAGIC */
 
-/* Generic MII registers. */
-
-#define MII_BMCR            0x00        /* Basic mode control register */
-#define MII_BMSR            0x01        /* Basic mode status register  */
-#define MII_PHYSID1         0x02        /* PHYS ID 1                   */
-#define MII_PHYSID2         0x03        /* PHYS ID 2                   */
-#define MII_ADVERTISE       0x04        /* Advertisement control reg   */
-#define MII_LPA             0x05        /* Link partner ability reg    */
-#define MII_EXPANSION       0x06        /* Expansion register          */
-#define MII_CTRL1000        0x09        /* 1000BASE-T control          */
-#define MII_STAT1000        0x0a        /* 1000BASE-T status           */
-#define MII_DCOUNTER        0x12        /* Disconnect counter          */
-#define MII_FCSCOUNTER      0x13        /* False carrier counter       */
-#define MII_NWAYTEST        0x14        /* N-way auto-neg test reg     */
-#define MII_RERRCOUNTER     0x15        /* Receive error counter       */
-#define MII_SREVISION       0x16        /* Silicon revision            */
-#define MII_RESV1           0x17        /* Reserved...                 */
-#define MII_LBRERROR        0x18        /* Lpback, rx, bypass error    */
-#define MII_PHYADDR         0x19        /* PHY address                 */
-#define MII_RESV2           0x1a        /* Reserved...                 */
-#define MII_TPISTATUS       0x1b        /* TPI status for 10mbps       */
-#define MII_NCONFIG         0x1c        /* Network interface config    */
-
-/* Basic mode control register. */
-#define BMCR_RESV               0x007f  /* Unused...                   */
-#define BMCR_SPEED1000		0x0040  /* MSB of Speed (1000)         */
-#define BMCR_CTST               0x0080  /* Collision test              */
-#define BMCR_FULLDPLX           0x0100  /* Full duplex                 */
-#define BMCR_ANRESTART          0x0200  /* Auto negotiation restart    */
-#define BMCR_ISOLATE            0x0400  /* Disconnect DP83840 from MII */
-#define BMCR_PDOWN              0x0800  /* Powerdown the DP83840       */
-#define BMCR_ANENABLE           0x1000  /* Enable auto negotiation     */
-#define BMCR_SPEED100           0x2000  /* Select 100Mbps              */
-#define BMCR_LOOPBACK           0x4000  /* TXD loopback bits           */
-#define BMCR_RESET              0x8000  /* Reset the DP83840           */
-
-/* Basic mode status register. */
-#define BMSR_ERCAP              0x0001  /* Ext-reg capability          */
-#define BMSR_JCD                0x0002  /* Jabber detected             */
-#define BMSR_LSTATUS            0x0004  /* Link status                 */
-#define BMSR_ANEGCAPABLE        0x0008  /* Able to do auto-negotiation */
-#define BMSR_RFAULT             0x0010  /* Remote fault detected       */
-#define BMSR_ANEGCOMPLETE       0x0020  /* Auto-negotiation complete   */
-#define BMSR_RESV               0x07c0  /* Unused...                   */
-#define BMSR_10HALF             0x0800  /* Can do 10mbps, half-duplex  */
-#define BMSR_10FULL             0x1000  /* Can do 10mbps, full-duplex  */
-#define BMSR_100HALF            0x2000  /* Can do 100mbps, half-duplex */
-#define BMSR_100FULL            0x4000  /* Can do 100mbps, full-duplex */
-#define BMSR_100BASE4           0x8000  /* Can do 100mbps, 4k packets  */
-
-/* Advertisement control register. */
-#define ADVERTISE_SLCT          0x001f  /* Selector bits               */
-#define ADVERTISE_CSMA          0x0001  /* Only selector supported     */
-#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
-#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
-#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
-#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
-#define ADVERTISE_100BASE4      0x0200  /* Try for 100mbps 4k packets  */
-#define ADVERTISE_RESV          0x1c00  /* Unused...                   */
-#define ADVERTISE_RFAULT        0x2000  /* Say we can detect faults    */
-#define ADVERTISE_LPACK         0x4000  /* Ack link partners response  */
-#define ADVERTISE_NPAGE         0x8000  /* Next page bit               */
-#define ADVERTISE_1000XFULL     0x0020  /* Try for 1000BASE-X full-duplex */
-#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
-#define ADVERTISE_1000XHALF     0x0040  /* Try for 1000BASE-X half-duplex */
-#define ADVERTISE_1000XPAUSE    0x0080  /* Try for 1000BASE-X pause    */
-#define ADVERTISE_PAUSE_CAP     0x0400  /* Try for pause               */
-#define ADVERTISE_1000XPSE_ASYM 0x0100  /* Try for 1000BASE-X asym pause */
-#define ADVERTISE_PAUSE_ASYM    0x0800  /* Try for asymetric pause     */
-
-#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
-			ADVERTISE_CSMA)
-#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
-                       ADVERTISE_100HALF | ADVERTISE_100FULL)
-
-/* Link partner ability register. */
-#define LPA_SLCT                0x001f  /* Same as advertise selector  */
-#define LPA_10HALF              0x0020  /* Can do 10mbps half-duplex   */
-#define LPA_10FULL              0x0040  /* Can do 10mbps full-duplex   */
-#define LPA_100HALF             0x0080  /* Can do 100mbps half-duplex  */
-#define LPA_100FULL             0x0100  /* Can do 100mbps full-duplex  */
-#define LPA_100BASE4            0x0200  /* Can do 100mbps 4k packets   */
-#define LPA_RESV                0x1c00  /* Unused...                   */
-#define LPA_RFAULT              0x2000  /* Link partner faulted        */
-#define LPA_LPACK               0x4000  /* Link partner acked us       */
-#define LPA_NPAGE               0x8000  /* Next page bit               */
-
-#define LPA_DUPLEX		(LPA_10FULL | LPA_100FULL)
-#define LPA_100			(LPA_100FULL | LPA_100HALF | LPA_100BASE4)
-
-/* Expansion register for auto-negotiation. */
-#define EXPANSION_NWAY          0x0001  /* Can do N-way auto-nego      */
-#define EXPANSION_LCWP          0x0002  /* Got new RX page code word   */
-#define EXPANSION_ENABLENPAGE   0x0004  /* This enables npage words    */
-#define EXPANSION_NPCAPABLE     0x0008  /* Link partner supports npage */
-#define EXPANSION_MFAULTS       0x0010  /* Multiple faults detected    */
-#define EXPANSION_RESV          0xffe0  /* Unused...                   */
-
-/* 1000BASE-T Control register */
-#define ADVERTISE_1000FULL      0x0200  /* Advertise 1000BASE-T full duplex */
-#define ADVERTISE_1000HALF      0x0100  /* Advertise 1000BASE-T half duplex */
-
-/* N-way test register. */
-#define NWAYTEST_RESV1          0x00ff  /* Unused...                   */
-#define NWAYTEST_LOOPBACK       0x0100  /* Enable loopback for N-way   */
-#define NWAYTEST_RESV2          0xfe00  /* Unused...                   */
-
 /* The following are all involved in forcing a particular link
  *  * mode for the device for setting things.  When getting the
  *   * devices settings, these indicate the current mode and whether
diff --git a/gpxe/src/drivers/net/cs89x0.c b/gpxe/src/drivers/net/cs89x0.c
index 1f647a8..df2667d 100644
--- a/gpxe/src/drivers/net/cs89x0.c
+++ b/gpxe/src/drivers/net/cs89x0.c
@@ -20,6 +20,8 @@
    -- quote from email
 **/
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */
 /*
   Permission is granted to distribute the enclosed cs89x0.[ch] driver
diff --git a/gpxe/src/drivers/net/cs89x0.h b/gpxe/src/drivers/net/cs89x0.h
index 696191c..a36b907 100644
--- a/gpxe/src/drivers/net/cs89x0.h
+++ b/gpxe/src/drivers/net/cs89x0.h
@@ -16,6 +16,8 @@
    -- quote from email
 **/
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /*  Copyright, 1988-1992, Russell Nelson, Crynwr Software
 
    This program is free software; you can redistribute it and/or modify
diff --git a/gpxe/src/drivers/net/davicom.c b/gpxe/src/drivers/net/davicom.c
index bb6d7e9..07c5e1b 100644
--- a/gpxe/src/drivers/net/davicom.c
+++ b/gpxe/src/drivers/net/davicom.c
@@ -15,6 +15,8 @@
 
 */
 
+FILE_LICENCE ( GPL_ANY );
+
 /*********************************************************************/
 /* Revision History                                                  */
 /*********************************************************************/
@@ -705,10 +707,10 @@ static struct nic_operations davicom_operations = {
 };
 
 static struct pci_device_id davicom_nics[] = {
-PCI_ROM(0x1282, 0x9100, "davicom9100", "Davicom 9100"),
-PCI_ROM(0x1282, 0x9102, "davicom9102", "Davicom 9102"),
-PCI_ROM(0x1282, 0x9009, "davicom9009", "Davicom 9009"),
-PCI_ROM(0x1282, 0x9132, "davicom9132", "Davicom 9132"),	/* Needs probably some fixing */
+PCI_ROM(0x1282, 0x9100, "davicom9100", "Davicom 9100", 0),
+PCI_ROM(0x1282, 0x9102, "davicom9102", "Davicom 9102", 0),
+PCI_ROM(0x1282, 0x9009, "davicom9009", "Davicom 9009", 0),
+PCI_ROM(0x1282, 0x9132, "davicom9132", "Davicom 9132", 0),	/* Needs probably some fixing */
 };
 
 PCI_DRIVER ( davicom_driver, davicom_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/depca.c b/gpxe/src/drivers/net/depca.c
index 7372e60..ea85cbe 100644
--- a/gpxe/src/drivers/net/depca.c
+++ b/gpxe/src/drivers/net/depca.c
@@ -1,5 +1,7 @@
 /* #warning "depca.c: FIXME: fix relocation" */
 
+FILE_LICENCE ( GPL_ANY );
+
 #if 0
 /* Not fixed for relocation yet. Probably won't work relocated above 16MB */
 #ifdef ALLMULTI
diff --git a/gpxe/src/drivers/net/dmfe.c b/gpxe/src/drivers/net/dmfe.c
index 26021e6..fad1737 100644
--- a/gpxe/src/drivers/net/dmfe.c
+++ b/gpxe/src/drivers/net/dmfe.c
@@ -36,6 +36,8 @@
 *
 ***************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* to get some global routines like printf */
 #include "etherboot.h"
 /* to get the interface to the body of the program */
@@ -1204,10 +1206,10 @@ static struct nic_operations dmfe_operations = {
 };
 
 static struct pci_device_id dmfe_nics[] = {
-	PCI_ROM(0x1282, 0x9100, "dmfe9100", "Davicom 9100"),
-	PCI_ROM(0x1282, 0x9102, "dmfe9102", "Davicom 9102"),
-	PCI_ROM(0x1282, 0x9009, "dmfe9009", "Davicom 9009"),
-	PCI_ROM(0x1282, 0x9132, "dmfe9132", "Davicom 9132"),	/* Needs probably some fixing */
+	PCI_ROM(0x1282, 0x9100, "dmfe9100", "Davicom 9100", 0),
+	PCI_ROM(0x1282, 0x9102, "dmfe9102", "Davicom 9102", 0),
+	PCI_ROM(0x1282, 0x9009, "dmfe9009", "Davicom 9009", 0),
+	PCI_ROM(0x1282, 0x9132, "dmfe9132", "Davicom 9132", 0),	/* Needs probably some fixing */
 };
 
 PCI_DRIVER ( dmfe_driver, dmfe_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/e1000/e1000.c b/gpxe/src/drivers/net/e1000/e1000.c
index c1a4a52..8e8c697 100644
--- a/gpxe/src/drivers/net/e1000/e1000.c
+++ b/gpxe/src/drivers/net/e1000/e1000.c
@@ -41,6 +41,8 @@
 
 *******************************************************************************/
 
+FILE_LICENCE ( GPL2_ONLY );
+
 #include "e1000.h"
 
 /**
@@ -71,6 +73,7 @@ e1000_get_hw_control ( struct e1000_adapter *adapter )
 		break;
 	case e1000_82571:
 	case e1000_82572:
+	case e1000_82576:
 	case e1000_80003es2lan:
 	case e1000_ich8lan:
 		ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
@@ -90,8 +93,7 @@ e1000_get_hw_control ( struct e1000_adapter *adapter )
 static void
 e1000_irq_enable ( struct e1000_adapter *adapter )
 {
-	E1000_WRITE_REG ( &adapter->hw, IMS, E1000_IMS_RXDMT0 |
-			                     E1000_IMS_RXSEQ );
+	E1000_WRITE_REG ( &adapter->hw, IMS, IMS_ENABLE_MASK );
 	E1000_WRITE_FLUSH ( &adapter->hw );
 }
 
@@ -108,18 +110,6 @@ e1000_irq_disable ( struct e1000_adapter *adapter )
 }
 
 /**
- * e1000_irq_force - trigger interrupt
- *
- * @v adapter	e1000 private structure
- **/
-static void
-e1000_irq_force ( struct e1000_adapter *adapter )
-{
-	E1000_WRITE_REG ( &adapter->hw, ICS, E1000_ICS_RXDMT0 );
-	E1000_WRITE_FLUSH ( &adapter->hw );
-}
-
-/**
  * e1000_sw_init - Initialize general software structures (struct e1000_adapter)
  *
  * @v adapter	e1000 private structure
@@ -251,6 +241,7 @@ e1000_configure_tx ( struct e1000_adapter *adapter )
 {
 	struct e1000_hw *hw = &adapter->hw;
 	uint32_t tctl;
+	uint32_t txdctl;
 
 	DBG ( "e1000_configure_tx\n" );
 
@@ -269,6 +260,12 @@ e1000_configure_tx ( struct e1000_adapter *adapter )
 	adapter->tx_tail = 0;
 	adapter->tx_fill_ctr = 0;
 
+	if (hw->mac_type == e1000_82576) {
+		txdctl = E1000_READ_REG ( hw, TXDCTL );
+		txdctl |= E1000_TXDCTL_QUEUE_ENABLE;
+		E1000_WRITE_REG ( hw, TXDCTL, txdctl );
+	}
+
 	/* Setup Transmit Descriptor Settings for eop descriptor */
 	tctl = E1000_TCTL_PSP | E1000_TCTL_EN |
 		(E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT) | 
@@ -280,6 +277,65 @@ e1000_configure_tx ( struct e1000_adapter *adapter )
         E1000_WRITE_FLUSH ( hw );
 }
 
+static void
+e1000_free_rx_resources ( struct e1000_adapter *adapter )
+{
+	int i;
+
+	DBG ( "e1000_free_rx_resources\n" );
+
+	free_dma ( adapter->rx_base, adapter->rx_ring_size );
+
+	for ( i = 0; i < NUM_RX_DESC; i++ ) {
+		free_iob ( adapter->rx_iobuf[i] );
+	}
+}
+
+/**
+ * e1000_refill_rx_ring - allocate Rx io_buffers
+ *
+ * @v adapter	e1000 private structure
+ *
+ * @ret rc       Returns 0 on success, negative on failure
+ **/
+int e1000_refill_rx_ring ( struct e1000_adapter *adapter )
+{
+	int i, rx_curr;
+	int rc = 0;
+	struct e1000_rx_desc *rx_curr_desc;
+	struct e1000_hw *hw = &adapter->hw;
+	struct io_buffer *iob;
+
+	DBG ("e1000_refill_rx_ring\n");
+
+	for ( i = 0; i < NUM_RX_DESC; i++ ) {
+		rx_curr = ( ( adapter->rx_curr + i ) % NUM_RX_DESC );
+		rx_curr_desc = adapter->rx_base + rx_curr;
+
+		if ( rx_curr_desc->status & E1000_RXD_STAT_DD )
+			continue;
+
+		if ( adapter->rx_iobuf[rx_curr] != NULL )
+			continue;
+
+		DBG2 ( "Refilling rx desc %d\n", rx_curr );
+
+		iob = alloc_iob ( MAXIMUM_ETHERNET_VLAN_SIZE );
+		adapter->rx_iobuf[rx_curr] = iob;
+
+		if ( ! iob ) {
+			DBG ( "alloc_iob failed\n" );
+			rc = -ENOMEM;
+			break;
+		} else {
+			rx_curr_desc->buffer_addr = virt_to_bus ( iob->data );
+
+			E1000_WRITE_REG ( hw, RDT, rx_curr );
+		}
+	}
+	return rc;
+}
+
 /**
  * e1000_setup_rx_resources - allocate Rx resources (Descriptors)
  *
@@ -290,8 +346,7 @@ e1000_configure_tx ( struct e1000_adapter *adapter )
 static int
 e1000_setup_rx_resources ( struct e1000_adapter *adapter )
 {
-	int i, j;
-	struct e1000_rx_desc *rx_curr_desc;
+	int i, rc = 0;
 	
 	DBG ( "e1000_setup_rx_resources\n" );
 	
@@ -301,50 +356,23 @@ e1000_setup_rx_resources ( struct e1000_adapter *adapter )
 
         adapter->rx_base = 
         	malloc_dma ( adapter->rx_ring_size, adapter->rx_ring_size );
-        		     
+
        	if ( ! adapter->rx_base ) {
        		return -ENOMEM;
 	}
 	memset ( adapter->rx_base, 0, adapter->rx_ring_size );
 
 	for ( i = 0; i < NUM_RX_DESC; i++ ) {
-	
-		adapter->rx_iobuf[i] = alloc_iob ( MAXIMUM_ETHERNET_VLAN_SIZE );
-		
-		/* If unable to allocate all iobufs, free any that
-		 * were successfully allocated, and return an error 
-		 */
-		if ( ! adapter->rx_iobuf[i] ) {
-			for ( j = 0; j < i; j++ ) {
-				free_iob ( adapter->rx_iobuf[j] );
-			}
-			return -ENOMEM;
-		}
-
-		rx_curr_desc = ( void * ) ( adapter->rx_base ) + 
-					  ( i * sizeof ( *adapter->rx_base ) ); 
-			
-		rx_curr_desc->buffer_addr = virt_to_bus ( adapter->rx_iobuf[i]->data );	
-
-		DBG ( "i = %d  rx_curr_desc->buffer_addr = %#16llx\n", 
-		      i, rx_curr_desc->buffer_addr );
-		
-	}	
-	return 0;
-}
-
-static void
-e1000_free_rx_resources ( struct e1000_adapter *adapter )
-{
-	int i;
-	
-	DBG ( "e1000_free_rx_resources\n" );
+		/* let e1000_refill_rx_ring() io_buffer allocations */
+		adapter->rx_iobuf[i] = NULL;
+	}
 
-        free_dma ( adapter->rx_base, adapter->rx_ring_size );
+	/* allocate io_buffers */
+	rc = e1000_refill_rx_ring ( adapter );
+	if ( rc < 0 )
+		e1000_free_rx_resources ( adapter );
 
-	for ( i = 0; i < NUM_RX_DESC; i++ ) {
-		free_iob ( adapter->rx_iobuf[i] );
-	}
+	return rc;
 }
 
 /**
@@ -357,13 +385,15 @@ static void
 e1000_configure_rx ( struct e1000_adapter *adapter )
 {
 	struct e1000_hw *hw = &adapter->hw;
-	uint32_t rctl;
+	uint32_t rctl, rxdctl, mrqc, rxcsum;
 
 	DBG ( "e1000_configure_rx\n" );
 
 	/* disable receives while setting up the descriptors */
 	rctl = E1000_READ_REG ( hw, RCTL );
 	E1000_WRITE_REG ( hw, RCTL, rctl & ~E1000_RCTL_EN );
+	E1000_WRITE_FLUSH ( hw );
+	mdelay(10);
 
 	adapter->rx_curr = 0;
 
@@ -375,16 +405,57 @@ e1000_configure_rx ( struct e1000_adapter *adapter )
 	E1000_WRITE_REG ( hw, RDLEN, adapter->rx_ring_size );
 
 	E1000_WRITE_REG ( hw, RDH, 0 );
-	E1000_WRITE_REG ( hw, RDT, NUM_RX_DESC - 1 );
-	
-	/* Enable Receives */
-	rctl = ( E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 |
-		 E1000_RCTL_MPE 
-		);
+	if (hw->mac_type == e1000_82576)
+		E1000_WRITE_REG ( hw, RDT, 0 );
+	else
+		E1000_WRITE_REG ( hw, RDT, NUM_RX_DESC - 1 );
+
+	/* This doesn't seem to  be necessary for correct operation,
+	 * but it seems as well to be implicit
+	 */
+	if (hw->mac_type == e1000_82576) {
+		rxdctl = E1000_READ_REG ( hw, RXDCTL );
+		rxdctl |= E1000_RXDCTL_QUEUE_ENABLE;
+		rxdctl &= 0xFFF00000;
+		rxdctl |= IGB_RX_PTHRESH;
+		rxdctl |= IGB_RX_HTHRESH << 8;
+		rxdctl |= IGB_RX_WTHRESH << 16;
+		E1000_WRITE_REG ( hw, RXDCTL, rxdctl );
+		E1000_WRITE_FLUSH ( hw );
+
+		rxcsum = E1000_READ_REG(hw, RXCSUM);
+		rxcsum &= ~( E1000_RXCSUM_TUOFL | E1000_RXCSUM_IPPCSE );
+		E1000_WRITE_REG ( hw, RXCSUM, 0 );
+
+		/* The initial value for MRQC disables multiple receive
+		 * queues, however this setting is not recommended.
+		 * - Intel® 82576 Gigabit Ethernet Controller Datasheet r2.41
+	         *   Section 8.10.9 Multiple Queues Command Register - MRQC
+		 */
+		mrqc = E1000_MRQC_ENABLE_VMDQ;
+		E1000_WRITE_REG ( hw, MRQC, mrqc );
+	}
 
+	/* Enable Receives */
+	rctl |=  E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 |
+		 E1000_RCTL_MPE;
 	E1000_WRITE_REG ( hw, RCTL, rctl );
 	E1000_WRITE_FLUSH ( hw );
 
+	/* On the 82576, RDT([0]) must not be "bumped" before
+	 * the enable bit of RXDCTL([0]) is set.
+	 * - Intel® 82576 Gigabit Ethernet Controller Datasheet r2.41
+	 *   Section 4.5.9 receive Initialization
+	 *
+	 * By observation I have found to occur when the enable bit of
+	 * RCTL is set. The datasheet recommends polling for this bit,
+	 * however as I see no evidence of this in the Linux igb driver
+	 * I have omitted that step.
+	 * - Simon Horman, May 2009
+	 */
+	if (hw->mac_type == e1000_82576)
+		E1000_WRITE_REG ( hw, RDT, NUM_RX_DESC - 1 );
+
         DBG ( "RDBAL: %#08x\n",  E1000_READ_REG ( hw, RDBAL ) );
         DBG ( "RDLEN: %d\n",     E1000_READ_REG ( hw, RDLEN ) );
         DBG ( "RCTL:  %#08x\n",  E1000_READ_REG ( hw, RCTL ) );
@@ -431,6 +502,9 @@ e1000_reset ( struct e1000_adapter *adapter )
 	case e1000_82573:
 		pba = E1000_PBA_20K;
 		break;
+	case e1000_82576:
+		pba = E1000_PBA_64K;
+		break;
 	case e1000_ich8lan:
 		pba = E1000_PBA_8K;
 	case e1000_undefined:
@@ -444,6 +518,7 @@ e1000_reset ( struct e1000_adapter *adapter )
 	/* Set the FC high water mark to 90% of the FIFO size.
 	 * Required to clear last 3 LSB */
 	fc_high_water_mark = ((pba * 9216)/10) & 0xFFF8;
+
 	/* We can't use 90% on small FIFOs because the remainder
 	 * would be less than 1 full frame.  In this case, we size
 	 * it to allow at least a full frame above the high water
@@ -451,9 +526,20 @@ e1000_reset ( struct e1000_adapter *adapter )
 	if (pba < E1000_PBA_16K)
 		fc_high_water_mark = (pba * 1024) - 1600;
 
-	adapter->hw.fc_high_water = fc_high_water_mark;
-	adapter->hw.fc_low_water = fc_high_water_mark - 8;
-	if (adapter->hw.mac_type == e1000_80003es2lan)
+	/* This actually applies to < e1000_82575, one revision less than
+	 * e1000_82576, but e1000_82575 isn't currently defined in the code */
+	if (adapter->hw.mac_type < e1000_82576) {
+		/* 8-byte granularity */
+		adapter->hw.fc_high_water = fc_high_water_mark & 0xFFF8;
+		adapter->hw.fc_low_water = adapter->hw.fc_high_water - 8;
+	} else {
+		/* 16-byte granularity */
+		adapter->hw.fc_high_water = fc_high_water_mark & 0xFFF0;
+		adapter->hw.fc_low_water = adapter->hw.fc_high_water - 16;
+	}
+
+	if (adapter->hw.mac_type == e1000_80003es2lan ||
+	    adapter->hw.mac_type == e1000_82576)
 		adapter->hw.fc_pause_time = 0xFFFF;
 	else
 		adapter->hw.fc_pause_time = E1000_FC_PAUSE_TIME;
@@ -586,6 +672,7 @@ e1000_transmit ( struct net_device *netdev, struct io_buffer *iobuf )
 
 	/* Write new tail to NIC, making packet available for transmit
 	 */
+	wmb();
 	E1000_WRITE_REG ( hw, TDT, adapter->tx_tail );
 
 	return 0;
@@ -607,12 +694,10 @@ e1000_poll ( struct net_device *netdev )
 	uint32_t rx_status;
 	uint32_t rx_len;
 	uint32_t rx_err;
-	struct io_buffer *rx_iob;
 	struct e1000_tx_desc *tx_curr_desc;
 	struct e1000_rx_desc *rx_curr_desc;
 	uint32_t i;
-	uint64_t tmp_buffer_addr;
-	
+
 	DBGP ( "e1000_poll\n" );
 
 	/* Acknowledge interrupts */
@@ -672,46 +757,37 @@ e1000_poll ( struct net_device *netdev )
 		if ( ! ( rx_status & E1000_RXD_STAT_DD ) )
 			break;
 
+		if ( adapter->rx_iobuf[i] == NULL )
+			break;
+
 		DBG ( "RCTL = %#08x\n", E1000_READ_REG ( &adapter->hw, RCTL ) );
 	
 		rx_len = rx_curr_desc->length;
 
                 DBG ( "Received packet, rx_curr: %d  rx_status: %#08x  rx_len: %d\n",
                       i, rx_status, rx_len );
-                
+
                 rx_err = rx_curr_desc->errors;
-                
+
+		iob_put ( adapter->rx_iobuf[i], rx_len );
+
 		if ( rx_err & E1000_RXD_ERR_FRAME_ERR_MASK ) {
 		
-			netdev_rx_err ( netdev, NULL, -EINVAL );
+			netdev_rx_err ( netdev, adapter->rx_iobuf[i], -EINVAL );
 			DBG ( "e1000_poll: Corrupted packet received!"
 			      " rx_err: %#08x\n", rx_err );
 		} else 	{
-		
-			/* If unable allocate space for this packet,
-			 *  try again next poll
-			 */
-			rx_iob = alloc_iob ( rx_len );
-			if ( ! rx_iob ) 
-				break;
-				
-			memcpy ( iob_put ( rx_iob, rx_len ), 
-				adapter->rx_iobuf[i]->data, rx_len );
-				
-			/* Add this packet to the receive queue. 
-			 */
-			netdev_rx ( netdev, rx_iob );
+			/* Add this packet to the receive queue. */
+			netdev_rx ( netdev, adapter->rx_iobuf[i] );
 		}
+		adapter->rx_iobuf[i] = NULL;
 
-		tmp_buffer_addr = rx_curr_desc->buffer_addr;
 		memset ( rx_curr_desc, 0, sizeof ( *rx_curr_desc ) );
-		rx_curr_desc->buffer_addr = tmp_buffer_addr;
-
-		E1000_WRITE_REG ( hw, RDT, adapter->rx_curr );
 
 		adapter->rx_curr = ( adapter->rx_curr + 1 ) % NUM_RX_DESC;
 	}
-}				
+	e1000_refill_rx_ring(adapter);
+}
 
 /**
  * e1000_irq - enable or Disable interrupts
@@ -725,18 +801,11 @@ e1000_irq ( struct net_device *netdev, int enable )
 	struct e1000_adapter *adapter = netdev_priv(netdev);
 
 	DBG ( "e1000_irq\n" );
-	
-	switch ( enable ) {
-	case 0 :
-		e1000_irq_disable ( adapter );
-		break;
-	case 1 :
+
+	if ( enable )
 		e1000_irq_enable ( adapter );
-		break;
-	case 2 :
-		e1000_irq_force ( adapter );
-		break;
-	}
+	else
+		e1000_irq_disable ( adapter );
 }
 
 static struct net_device_operations e1000_operations;
@@ -850,7 +919,7 @@ e1000_probe ( struct pci_device *pdev,
 	if ( e1000_read_mac_addr ( &adapter->hw ) )
 		DBG ( "EEPROM Read Error\n" );
 
-        memcpy ( netdev->ll_addr, adapter->hw.mac_addr, ETH_ALEN );
+        memcpy ( netdev->hw_addr, adapter->hw.mac_addr, ETH_ALEN );
 
 	/* print bus type/speed/width info */
 	{
@@ -1039,68 +1108,69 @@ e1000_io_write ( struct e1000_hw *hw  __unused, unsigned long port, uint32_t val
 }
 
 static struct pci_device_id e1000_nics[] = {
-	PCI_ROM(0x8086, 0x1000, "e1000-0x1000", "e1000-0x1000"),
-	PCI_ROM(0x8086, 0x1001, "e1000-0x1001", "e1000-0x1001"),
-	PCI_ROM(0x8086, 0x1004, "e1000-0x1004", "e1000-0x1004"),
-	PCI_ROM(0x8086, 0x1008, "e1000-0x1008", "e1000-0x1008"),
-	PCI_ROM(0x8086, 0x1009, "e1000-0x1009", "e1000-0x1009"),
-	PCI_ROM(0x8086, 0x100c, "e1000-0x100c", "e1000-0x100c"),
-	PCI_ROM(0x8086, 0x100d, "e1000-0x100d", "e1000-0x100d"),
-	PCI_ROM(0x8086, 0x100e, "e1000-0x100e", "e1000-0x100e"),
-	PCI_ROM(0x8086, 0x100f, "e1000-0x100f", "e1000-0x100f"),
-	PCI_ROM(0x8086, 0x1010, "e1000-0x1010", "e1000-0x1010"),
-	PCI_ROM(0x8086, 0x1011, "e1000-0x1011", "e1000-0x1011"),
-	PCI_ROM(0x8086, 0x1012, "e1000-0x1012", "e1000-0x1012"),
-	PCI_ROM(0x8086, 0x1013, "e1000-0x1013", "e1000-0x1013"),
-	PCI_ROM(0x8086, 0x1014, "e1000-0x1014", "e1000-0x1014"),
-	PCI_ROM(0x8086, 0x1015, "e1000-0x1015", "e1000-0x1015"),
-	PCI_ROM(0x8086, 0x1016, "e1000-0x1016", "e1000-0x1016"),
-	PCI_ROM(0x8086, 0x1017, "e1000-0x1017", "e1000-0x1017"),
-	PCI_ROM(0x8086, 0x1018, "e1000-0x1018", "e1000-0x1018"),
-	PCI_ROM(0x8086, 0x1019, "e1000-0x1019", "e1000-0x1019"),
-	PCI_ROM(0x8086, 0x101a, "e1000-0x101a", "e1000-0x101a"),
-	PCI_ROM(0x8086, 0x101d, "e1000-0x101d", "e1000-0x101d"),
-	PCI_ROM(0x8086, 0x101e, "e1000-0x101e", "e1000-0x101e"),
-	PCI_ROM(0x8086, 0x1026, "e1000-0x1026", "e1000-0x1026"),
-	PCI_ROM(0x8086, 0x1027, "e1000-0x1027", "e1000-0x1027"),
-	PCI_ROM(0x8086, 0x1028, "e1000-0x1028", "e1000-0x1028"),
-	PCI_ROM(0x8086, 0x1049, "e1000-0x1049", "e1000-0x1049"),
-	PCI_ROM(0x8086, 0x104a, "e1000-0x104a", "e1000-0x104a"),
-	PCI_ROM(0x8086, 0x104b, "e1000-0x104b", "e1000-0x104b"),
-	PCI_ROM(0x8086, 0x104c, "e1000-0x104c", "e1000-0x104c"),
-	PCI_ROM(0x8086, 0x104d, "e1000-0x104d", "e1000-0x104d"),
-	PCI_ROM(0x8086, 0x105e, "e1000-0x105e", "e1000-0x105e"),
-	PCI_ROM(0x8086, 0x105f, "e1000-0x105f", "e1000-0x105f"),
-	PCI_ROM(0x8086, 0x1060, "e1000-0x1060", "e1000-0x1060"),
-	PCI_ROM(0x8086, 0x1075, "e1000-0x1075", "e1000-0x1075"),
-	PCI_ROM(0x8086, 0x1076, "e1000-0x1076", "e1000-0x1076"),
-	PCI_ROM(0x8086, 0x1077, "e1000-0x1077", "e1000-0x1077"),
-	PCI_ROM(0x8086, 0x1078, "e1000-0x1078", "e1000-0x1078"),
-	PCI_ROM(0x8086, 0x1079, "e1000-0x1079", "e1000-0x1079"),
-	PCI_ROM(0x8086, 0x107a, "e1000-0x107a", "e1000-0x107a"),
-	PCI_ROM(0x8086, 0x107b, "e1000-0x107b", "e1000-0x107b"),
-	PCI_ROM(0x8086, 0x107c, "e1000-0x107c", "e1000-0x107c"),
-	PCI_ROM(0x8086, 0x107d, "e1000-0x107d", "e1000-0x107d"),
-	PCI_ROM(0x8086, 0x107e, "e1000-0x107e", "e1000-0x107e"),
-	PCI_ROM(0x8086, 0x107f, "e1000-0x107f", "e1000-0x107f"),
-	PCI_ROM(0x8086, 0x108a, "e1000-0x108a", "e1000-0x108a"),
-	PCI_ROM(0x8086, 0x108b, "e1000-0x108b", "e1000-0x108b"),
-	PCI_ROM(0x8086, 0x108c, "e1000-0x108c", "e1000-0x108c"),
-	PCI_ROM(0x8086, 0x1096, "e1000-0x1096", "e1000-0x1096"),
-	PCI_ROM(0x8086, 0x1098, "e1000-0x1098", "e1000-0x1098"),
-	PCI_ROM(0x8086, 0x1099, "e1000-0x1099", "e1000-0x1099"),
-	PCI_ROM(0x8086, 0x109a, "e1000-0x109a", "e1000-0x109a"),
-	PCI_ROM(0x8086, 0x10a4, "e1000-0x10a4", "e1000-0x10a4"),
-	PCI_ROM(0x8086, 0x10a5, "e1000-0x10a5", "e1000-0x10a5"),
-	PCI_ROM(0x8086, 0x10b5, "e1000-0x10b5", "e1000-0x10b5"),
-	PCI_ROM(0x8086, 0x10b9, "e1000-0x10b9", "e1000-0x10b9"),
-	PCI_ROM(0x8086, 0x10ba, "e1000-0x10ba", "e1000-0x10ba"),
-	PCI_ROM(0x8086, 0x10bb, "e1000-0x10bb", "e1000-0x10bb"),
-	PCI_ROM(0x8086, 0x10bc, "e1000-0x10bc", "e1000-0x10bc"),
-	PCI_ROM(0x8086, 0x10c4, "e1000-0x10c4", "e1000-0x10c4"),
-	PCI_ROM(0x8086, 0x10c5, "e1000-0x10c5", "e1000-0x10c5"),
-	PCI_ROM(0x8086, 0x10d9, "e1000-0x10d9", "e1000-0x10d9"),
-	PCI_ROM(0x8086, 0x10da, "e1000-0x10da", "e1000-0x10da"),
+	PCI_ROM(0x8086, 0x1000, "e1000-0x1000", "e1000-0x1000", 0),
+	PCI_ROM(0x8086, 0x1001, "e1000-0x1001", "e1000-0x1001", 0),
+	PCI_ROM(0x8086, 0x1004, "e1000-0x1004", "e1000-0x1004", 0),
+	PCI_ROM(0x8086, 0x1008, "e1000-0x1008", "e1000-0x1008", 0),
+	PCI_ROM(0x8086, 0x1009, "e1000-0x1009", "e1000-0x1009", 0),
+	PCI_ROM(0x8086, 0x100c, "e1000-0x100c", "e1000-0x100c", 0),
+	PCI_ROM(0x8086, 0x100d, "e1000-0x100d", "e1000-0x100d", 0),
+	PCI_ROM(0x8086, 0x100e, "e1000-0x100e", "e1000-0x100e", 0),
+	PCI_ROM(0x8086, 0x100f, "e1000-0x100f", "e1000-0x100f", 0),
+	PCI_ROM(0x8086, 0x1010, "e1000-0x1010", "e1000-0x1010", 0),
+	PCI_ROM(0x8086, 0x1011, "e1000-0x1011", "e1000-0x1011", 0),
+	PCI_ROM(0x8086, 0x1012, "e1000-0x1012", "e1000-0x1012", 0),
+	PCI_ROM(0x8086, 0x1013, "e1000-0x1013", "e1000-0x1013", 0),
+	PCI_ROM(0x8086, 0x1014, "e1000-0x1014", "e1000-0x1014", 0),
+	PCI_ROM(0x8086, 0x1015, "e1000-0x1015", "e1000-0x1015", 0),
+	PCI_ROM(0x8086, 0x1016, "e1000-0x1016", "e1000-0x1016", 0),
+	PCI_ROM(0x8086, 0x1017, "e1000-0x1017", "e1000-0x1017", 0),
+	PCI_ROM(0x8086, 0x1018, "e1000-0x1018", "e1000-0x1018", 0),
+	PCI_ROM(0x8086, 0x1019, "e1000-0x1019", "e1000-0x1019", 0),
+	PCI_ROM(0x8086, 0x101a, "e1000-0x101a", "e1000-0x101a", 0),
+	PCI_ROM(0x8086, 0x101d, "e1000-0x101d", "e1000-0x101d", 0),
+	PCI_ROM(0x8086, 0x101e, "e1000-0x101e", "e1000-0x101e", 0),
+	PCI_ROM(0x8086, 0x1026, "e1000-0x1026", "e1000-0x1026", 0),
+	PCI_ROM(0x8086, 0x1027, "e1000-0x1027", "e1000-0x1027", 0),
+	PCI_ROM(0x8086, 0x1028, "e1000-0x1028", "e1000-0x1028", 0),
+	PCI_ROM(0x8086, 0x1049, "e1000-0x1049", "e1000-0x1049", 0),
+	PCI_ROM(0x8086, 0x104a, "e1000-0x104a", "e1000-0x104a", 0),
+	PCI_ROM(0x8086, 0x104b, "e1000-0x104b", "e1000-0x104b", 0),
+	PCI_ROM(0x8086, 0x104c, "e1000-0x104c", "e1000-0x104c", 0),
+	PCI_ROM(0x8086, 0x104d, "e1000-0x104d", "e1000-0x104d", 0),
+	PCI_ROM(0x8086, 0x105e, "e1000-0x105e", "e1000-0x105e", 0),
+	PCI_ROM(0x8086, 0x105f, "e1000-0x105f", "e1000-0x105f", 0),
+	PCI_ROM(0x8086, 0x1060, "e1000-0x1060", "e1000-0x1060", 0),
+	PCI_ROM(0x8086, 0x1075, "e1000-0x1075", "e1000-0x1075", 0),
+	PCI_ROM(0x8086, 0x1076, "e1000-0x1076", "e1000-0x1076", 0),
+	PCI_ROM(0x8086, 0x1077, "e1000-0x1077", "e1000-0x1077", 0),
+	PCI_ROM(0x8086, 0x1078, "e1000-0x1078", "e1000-0x1078", 0),
+	PCI_ROM(0x8086, 0x1079, "e1000-0x1079", "e1000-0x1079", 0),
+	PCI_ROM(0x8086, 0x107a, "e1000-0x107a", "e1000-0x107a", 0),
+	PCI_ROM(0x8086, 0x107b, "e1000-0x107b", "e1000-0x107b", 0),
+	PCI_ROM(0x8086, 0x107c, "e1000-0x107c", "e1000-0x107c", 0),
+	PCI_ROM(0x8086, 0x107d, "e1000-0x107d", "e1000-0x107d", 0),
+	PCI_ROM(0x8086, 0x107e, "e1000-0x107e", "e1000-0x107e", 0),
+	PCI_ROM(0x8086, 0x107f, "e1000-0x107f", "e1000-0x107f", 0),
+	PCI_ROM(0x8086, 0x108a, "e1000-0x108a", "e1000-0x108a", 0),
+	PCI_ROM(0x8086, 0x108b, "e1000-0x108b", "e1000-0x108b", 0),
+	PCI_ROM(0x8086, 0x108c, "e1000-0x108c", "e1000-0x108c", 0),
+	PCI_ROM(0x8086, 0x1096, "e1000-0x1096", "e1000-0x1096", 0),
+	PCI_ROM(0x8086, 0x1098, "e1000-0x1098", "e1000-0x1098", 0),
+	PCI_ROM(0x8086, 0x1099, "e1000-0x1099", "e1000-0x1099", 0),
+	PCI_ROM(0x8086, 0x109a, "e1000-0x109a", "e1000-0x109a", 0),
+	PCI_ROM(0x8086, 0x10a4, "e1000-0x10a4", "e1000-0x10a4", 0),
+	PCI_ROM(0x8086, 0x10a5, "e1000-0x10a5", "e1000-0x10a5", 0),
+	PCI_ROM(0x8086, 0x10b5, "e1000-0x10b5", "e1000-0x10b5", 0),
+	PCI_ROM(0x8086, 0x10b9, "e1000-0x10b9", "e1000-0x10b9", 0),
+	PCI_ROM(0x8086, 0x10ba, "e1000-0x10ba", "e1000-0x10ba", 0),
+	PCI_ROM(0x8086, 0x10bb, "e1000-0x10bb", "e1000-0x10bb", 0),
+	PCI_ROM(0x8086, 0x10bc, "e1000-0x10bc", "e1000-0x10bc", 0),
+	PCI_ROM(0x8086, 0x10c4, "e1000-0x10c4", "e1000-0x10c4", 0),
+	PCI_ROM(0x8086, 0x10c5, "e1000-0x10c5", "e1000-0x10c5", 0),
+	PCI_ROM(0x8086, 0x10c9, "e1000-0x10c9", "e1000-0x10c9", 0),
+	PCI_ROM(0x8086, 0x10d9, "e1000-0x10d9", "e1000-0x10d9", 0),
+	PCI_ROM(0x8086, 0x10da, "e1000-0x10da", "e1000-0x10da", 0),
 };
 
 struct pci_driver e1000_driver __pci_driver = {
diff --git a/gpxe/src/drivers/net/e1000/e1000.h b/gpxe/src/drivers/net/e1000/e1000.h
index 77a09ef..ea51db6 100644
--- a/gpxe/src/drivers/net/e1000/e1000.h
+++ b/gpxe/src/drivers/net/e1000/e1000.h
@@ -26,6 +26,8 @@
 
 *******************************************************************************/
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /* Linux PRO/1000 Ethernet Driver main header file */
 
 #ifndef _E1000_H_
diff --git a/gpxe/src/drivers/net/e1000/e1000_hw.c b/gpxe/src/drivers/net/e1000/e1000_hw.c
index 1054b90..1871dfc 100644
--- a/gpxe/src/drivers/net/e1000/e1000_hw.c
+++ b/gpxe/src/drivers/net/e1000/e1000_hw.c
@@ -26,6 +26,8 @@
 
 *******************************************************************************/
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /* e1000_hw.c
  * Shared functions for accessing and configuring the MAC
  */
@@ -417,6 +419,9 @@ e1000_set_mac_type(struct e1000_hw *hw)
 	case E1000_DEV_ID_ICH8_IGP_M:
 		hw->mac_type = e1000_ich8lan;
 		break;
+	case E1000_DEV_ID_82576:
+		hw->mac_type = e1000_82576;
+		break;
 	default:
 		/* Should never have loaded on this device */
 		return -E1000_ERR_MAC_TYPE;
@@ -424,6 +429,7 @@ e1000_set_mac_type(struct e1000_hw *hw)
 
 	switch (hw->mac_type) {
 	case e1000_ich8lan:
+	case e1000_82576:
 		hw->swfwhw_semaphore_present = TRUE;
 		hw->asf_firmware_present = TRUE;
 		break;
@@ -502,6 +508,7 @@ e1000_set_media_type(struct e1000_hw *hw)
             break;
         case e1000_ich8lan:
         case e1000_82573:
+        case e1000_82576:
             /* The STATUS_TBIMODE bit is reserved or reused for the this
              * device.
              */
@@ -721,6 +728,17 @@ e1000_reset_hw(struct e1000_hw *hw)
     /* Clear any pending interrupt events. */
     icr = E1000_READ_REG(hw, ICR);
 
+    if (hw->mac_type == e1000_82571 && hw->laa_is_present == TRUE) {
+        /*
+         * Hold a copy of the LAA in RAR[14] This is done so that
+         * between the time RAR[0] gets clobbered and the time it
+         * gets fixed, the actual LAA is in one of the RARs and no
+         * incoming packets directed to this port are dropped.
+         * Eventually the LAA will be in RAR[0] and RAR[14].
+         */
+        e1000_rar_set(hw, hw->mac_addr, E1000_RAR_ENTRIES - 1);
+    }
+
     /* If MWI was previously enabled, reenable it. */
     if (hw->mac_type == e1000_82542_rev2_0) {
         if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE)
@@ -748,7 +766,8 @@ e1000_reset_hw(struct e1000_hw *hw)
 static void
 e1000_initialize_hardware_bits(struct e1000_hw *hw)
 {
-    if ((hw->mac_type >= e1000_82571) && (!hw->initialize_hw_bits_disable)) {
+    if ((hw->mac_type >= e1000_82571 && hw->mac_type < e1000_82576) &&
+        (!hw->initialize_hw_bits_disable)) {
         /* Settings common to all PCI-express silicon */
         uint32_t reg_ctrl, reg_ctrl_ext;
         uint32_t reg_tarc0, reg_tarc1;
@@ -905,11 +924,27 @@ e1000_init_hw(struct e1000_hw *hw)
 
     /* Disabling VLAN filtering. */
     DEBUGOUT("Initializing the IEEE VLAN\n");
-    /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */
-    if (hw->mac_type != e1000_ich8lan) {
+    switch (hw->mac_type) {
+    case e1000_ich8lan:
+        /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */
+        break;
+    case e1000_82576:
+        /* There is no need to clear vfta on 82576 if VLANs are not used.
+         * - Intel® 82576 Gigabit Ethernet Controller Datasheet r2.41
+         *   Section 8.10.19 Table Array - VFTA
+         *
+         * Setting VET may also be unnecessary, however the documentation
+         * isn't specific on this point. The value used here is as advised in
+	 * - Intel® 82576 Gigabit Ethernet Controller Datasheet r2.41
+         *   Section 8.2.7 VLAN Ether Type - VET
+         */
+        E1000_WRITE_REG(hw, VET, ETHERNET_IEEE_VLAN_TYPE);
+        break;
+    default:
         if (hw->mac_type < e1000_82545_rev_3)
             E1000_WRITE_REG(hw, VET, 0);
         e1000_clear_vfta(hw);
+        break;
     }
 
     /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */
@@ -1475,9 +1510,13 @@ e1000_copper_link_igp_setup(struct e1000_hw *hw)
         return ret_val;
     }
 
-    /* Wait 15ms for MAC to configure PHY from eeprom settings */
-    msleep(15);
-    if (hw->mac_type != e1000_ich8lan) {
+    /*
+     * Wait 100ms for MAC to configure PHY from NVM settings, to avoid
+     * timeout issues when LFS is enabled.
+     */
+    msleep(100);
+
+    if (hw->mac_type != e1000_ich8lan && hw->mac_type != e1000_82576) {
     /* Configure activity LED after PHY reset */
     led_ctrl = E1000_READ_REG(hw, LEDCTL);
     led_ctrl &= IGP_ACTIVITY_LED_MASK;
@@ -3491,7 +3530,7 @@ e1000_read_phy_reg(struct e1000_hw *hw,
 
     DEBUGFUNC("e1000_read_phy_reg");
 
-    if ((hw->mac_type == e1000_80003es2lan) &&
+    if ((hw->mac_type == e1000_80003es2lan || hw->mac_type == e1000_82576) &&
         (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
         swfw = E1000_SWFW_PHY1_SM;
     } else {
@@ -3629,7 +3668,7 @@ e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr,
 
     DEBUGFUNC("e1000_write_phy_reg");
 
-    if ((hw->mac_type == e1000_80003es2lan) &&
+    if ((hw->mac_type == e1000_80003es2lan || hw->mac_type == e1000_82576) &&
         (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
         swfw = E1000_SWFW_PHY1_SM;
     } else {
@@ -3749,7 +3788,7 @@ e1000_read_kmrn_reg(struct e1000_hw *hw,
     uint16_t swfw;
     DEBUGFUNC("e1000_read_kmrn_reg");
 
-    if ((hw->mac_type == e1000_80003es2lan) &&
+    if ((hw->mac_type == e1000_80003es2lan || hw->mac_type == e1000_82576) &&
         (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
         swfw = E1000_SWFW_PHY1_SM;
     } else {
@@ -3782,7 +3821,7 @@ e1000_write_kmrn_reg(struct e1000_hw *hw,
     uint16_t swfw;
     DEBUGFUNC("e1000_write_kmrn_reg");
 
-    if ((hw->mac_type == e1000_80003es2lan) &&
+    if ((hw->mac_type == e1000_80003es2lan || hw->mac_type == e1000_82576) &&
         (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
         swfw = E1000_SWFW_PHY1_SM;
     } else {
@@ -3824,7 +3863,8 @@ e1000_phy_hw_reset(struct e1000_hw *hw)
     DEBUGOUT("Resetting Phy...\n");
 
     if (hw->mac_type > e1000_82543) {
-        if ((hw->mac_type == e1000_80003es2lan) &&
+        if ((hw->mac_type == e1000_80003es2lan ||
+             hw->mac_type == e1000_82576) &&
             (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
             swfw = E1000_SWFW_PHY1_SM;
         } else {
@@ -4134,6 +4174,9 @@ e1000_detect_gig_phy(struct e1000_hw *hw)
         if (hw->phy_id == IFE_PLUS_E_PHY_ID) match = TRUE;
         if (hw->phy_id == IFE_C_E_PHY_ID) match = TRUE;
         break;
+    case e1000_82576:
+        match = TRUE;
+        break;
     default:
         DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type);
         return -E1000_ERR_CONFIG;
@@ -4607,6 +4650,38 @@ e1000_init_eeprom_params(struct e1000_hw *hw)
 
         break;
         }
+    case e1000_82576:
+        {
+        uint16_t size;
+
+        eeprom->type = e1000_eeprom_spi;
+        eeprom->opcode_bits = 8;
+        eeprom->delay_usec = 1;
+        if (eecd & E1000_EECD_ADDR_BITS) {
+            eeprom->page_size = 32;
+            eeprom->address_bits = 16;
+        } else {
+            eeprom->page_size = 8;
+            eeprom->address_bits = 8;
+        }
+        eeprom->use_eerd = TRUE;
+        eeprom->use_eewr = FALSE;
+
+        size = (uint16_t)((eecd & E1000_EECD_SIZE_EX_MASK) >>
+                          E1000_EECD_SIZE_EX_SHIFT);
+	/*
+	 * Added to a constant, "size" becomes the left-shift value
+	 * for setting word_size.
+	 */
+	size += EEPROM_WORD_SIZE_SHIFT;
+
+	/* EEPROM access above 16k is unsupported */
+	if (size > 14)
+		size = 14;
+	eeprom->word_size = 1 << size;
+
+        break;
+        }
     default:
         break;
     }
@@ -5012,8 +5087,7 @@ e1000_read_eeprom(struct e1000_hw *hw,
      * directly. In this case, we need to acquire the EEPROM so that
      * FW or other port software does not interrupt.
      */
-    if (e1000_is_onboard_nvm_eeprom(hw) == TRUE &&
-        hw->eeprom.use_eerd == FALSE) {
+    if (hw->eeprom.use_eerd == FALSE && e1000_is_onboard_nvm_eeprom(hw)) {
         /* Prepare the EEPROM for bit-bang reading */
         if (e1000_acquire_eeprom(hw) != E1000_SUCCESS)
             return -E1000_ERR_EEPROM;
@@ -5196,6 +5270,8 @@ e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw)
 
     DEBUGFUNC("e1000_is_onboard_nvm_eeprom");
 
+    assert(hw->mac_type != e1000_82576);
+
     if (hw->mac_type == e1000_ich8lan)
         return FALSE;
 
@@ -5709,13 +5785,48 @@ e1000_commit_shadow_ram(struct e1000_hw *hw)
 int32_t
 e1000_read_mac_addr(struct e1000_hw * hw)
 {
-    uint16_t offset;
+    uint16_t offset, mac_addr_offset = 0;
     uint16_t eeprom_data, i;
+    int32_t ret_val;
 
     DEBUGFUNC("e1000_read_mac_addr");
 
+    if (hw->mac_type == e1000_82571) {
+        /* Check for an alternate MAC address.  An alternate MAC
+         * address can be setup by pre-boot software and must be
+         * treated like a permanent address and must override the
+         * actual permanent MAC address.*/
+        ret_val = e1000_read_eeprom(hw, EEPROM_ALT_MAC_ADDR_PTR, 1,
+                                    &mac_addr_offset);
+        if (ret_val) {
+            DEBUGOUT("EEPROM Read Error\n");
+            return -E1000_ERR_EEPROM;
+        }
+        if (mac_addr_offset == 0xFFFF)
+            mac_addr_offset = 0;
+
+        if (mac_addr_offset) {
+            if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
+                mac_addr_offset += NODE_ADDRESS_SIZE/sizeof(u16);
+
+                /* make sure we have a valid mac address here
+                 * before using it */
+               ret_val = e1000_read_eeprom(hw, mac_addr_offset, 1,
+                                           &eeprom_data);
+               if (ret_val) {
+                   DEBUGOUT("EEPROM Read Error\n");
+                   return -E1000_ERR_EEPROM;
+               }
+               if (eeprom_data & 0x0001)
+                   mac_addr_offset = 0;
+        }
+
+        if (mac_addr_offset)
+            hw->laa_is_present = TRUE;
+    }
+
     for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) {
-        offset = i >> 1;
+        offset = mac_addr_offset + (i >> 1);
         if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) {
             DEBUGOUT("EEPROM Read Error\n");
             return -E1000_ERR_EEPROM;
@@ -5730,8 +5841,10 @@ e1000_read_mac_addr(struct e1000_hw * hw)
     case e1000_82546:
     case e1000_82546_rev_3:
     case e1000_82571:
+    case e1000_82576:
     case e1000_80003es2lan:
-        if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
+        if (!mac_addr_offset &&
+            E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
             hw->perm_mac_addr[5] ^= 0x01;
         break;
     }
@@ -5942,6 +6055,13 @@ e1000_rar_set(struct e1000_hw *hw,
     case e1000_80003es2lan:
         if (hw->leave_av_bit_off == TRUE)
             break;
+    case e1000_82576:
+        /* If MAC address zero, no need to set the AV bit */
+        if (rar_low || rar_high)
+            rar_high |= E1000_RAH_AV;
+            // Only neded when Multiple Receive Queues are enabmed in MRQC
+        rar_high |= E1000_RAH_POOL_1;
+        break;
     default:
         /* Indicate to hardware the Address is Valid. */
         rar_high |= E1000_RAH_AV;
@@ -6607,6 +6727,7 @@ e1000_get_bus_info(struct e1000_hw *hw)
     case e1000_82572:
     case e1000_82573:
     case e1000_80003es2lan:
+    case e1000_82576:
         hw->bus_type = e1000_bus_type_pci_express;
         hw->bus_speed = e1000_bus_speed_2500;
         ret_val = e1000_read_pcie_cap_reg(hw,
@@ -8025,6 +8146,7 @@ e1000_get_auto_rd_done(struct e1000_hw *hw)
     case e1000_82573:
     case e1000_80003es2lan:
     case e1000_ich8lan:
+    case e1000_82576:
         while (timeout) {
             if (E1000_READ_REG(hw, EECD) & E1000_EECD_AUTO_RD)
                 break;
@@ -8070,6 +8192,7 @@ e1000_get_phy_cfg_done(struct e1000_hw *hw)
         mdelay(10);
         break;
     case e1000_80003es2lan:
+    case e1000_82576:
         /* Separate *_CFG_DONE_* bit for each port */
         if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
             cfg_mask = E1000_EEPROM_CFG_DONE_PORT_1;
@@ -8280,6 +8403,7 @@ e1000_arc_subsystem_valid(struct e1000_hw *hw)
     case e1000_82572:
     case e1000_82573:
     case e1000_80003es2lan:
+    case e1000_82576:
         fwsm = E1000_READ_REG(hw, FWSM);
         if ((fwsm & E1000_FWSM_MODE_MASK) != 0)
             return TRUE;
diff --git a/gpxe/src/drivers/net/e1000/e1000_hw.h b/gpxe/src/drivers/net/e1000/e1000_hw.h
index 9e31939..628b2e3 100644
--- a/gpxe/src/drivers/net/e1000/e1000_hw.h
+++ b/gpxe/src/drivers/net/e1000/e1000_hw.h
@@ -26,6 +26,8 @@
 
 *******************************************************************************/
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /* e1000_hw.h
  * Structures, enums, and macros for the MAC
  */
@@ -62,6 +64,7 @@ typedef enum {
     e1000_82573,
     e1000_80003es2lan,
     e1000_ich8lan,
+    e1000_82576,
     e1000_num_macs
 } e1000_mac_type;
 
@@ -500,6 +503,7 @@ int32_t e1000_check_phy_reset_block(struct e1000_hw *hw);
 #define E1000_DEV_ID_ICH8_IFE_G          0x10C5
 #define E1000_DEV_ID_ICH8_IGP_M          0x104D
 
+#define E1000_DEV_ID_82576                    0x10C9
 
 #define NODE_ADDRESS_SIZE 6
 #define ETH_LENGTH_OF_ADDRESS 6
@@ -567,7 +571,8 @@ int32_t e1000_check_phy_reset_block(struct e1000_hw *hw);
     E1000_IMS_TXDW   |    \
     E1000_IMS_RXDMT0 |    \
     E1000_IMS_RXSEQ  |    \
-    E1000_IMS_LSC)
+    E1000_IMS_LSC    |    \
+    E1000_IMS_DOUTSYNC)
 
 /* Additional interrupts need to be handled for e1000_ich8lan:
     DSW = The FW changed the status of the DISSW bit in FWSM
@@ -1746,12 +1751,16 @@ struct e1000_hw {
 /* Receive Address */
 #define E1000_RAH_AV  0x80000000        /* Receive descriptor valid */
 
+#define E1000_RAH_POOL_1 0x00040000
+
 /* Interrupt Cause Read */
 #define E1000_ICR_TXDW          0x00000001 /* Transmit desc written back */
 #define E1000_ICR_TXQE          0x00000002 /* Transmit Queue empty */
 #define E1000_ICR_LSC           0x00000004 /* Link Status Change */
 #define E1000_ICR_RXSEQ         0x00000008 /* rx sequence error */
 #define E1000_ICR_RXDMT0        0x00000010 /* rx desc min. threshold (0) */
+/* LAN connected device generates an interrupt */
+#define E1000_ICR_DOUTSYNC      0x10000000 /* NIC DMA out of sync */
 #define E1000_ICR_RXO           0x00000040 /* rx overrun */
 #define E1000_ICR_RXT0          0x00000080 /* rx timer intr (ring 0) */
 #define E1000_ICR_MDAC          0x00000200 /* MDIO access complete */
@@ -1813,6 +1822,7 @@ struct e1000_hw {
 #define E1000_IMS_RXSEQ     E1000_ICR_RXSEQ     /* rx sequence error */
 #define E1000_IMS_RXDMT0    E1000_ICR_RXDMT0    /* rx desc min. threshold */
 #define E1000_IMS_RXO       E1000_ICR_RXO       /* rx overrun */
+#define E1000_IMS_DOUTSYNC  E1000_ICR_DOUTSYNC  /* NIC DMA out of sync */
 #define E1000_IMS_RXT0      E1000_ICR_RXT0      /* rx timer intr */
 #define E1000_IMS_MDAC      E1000_ICR_MDAC      /* MDIO access complete */
 #define E1000_IMS_RXCFG     E1000_ICR_RXCFG     /* RX /c/ ordered set */
@@ -1973,6 +1983,10 @@ struct e1000_hw {
 #define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */
 #define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */
 #define E1000_RXDCTL_GRAN    0x01000000 /* RXDCTL Granularity */
+#define E1000_RXDCTL_QUEUE_ENABLE  0x02000000 /* Enable specific Rx Queue */
+#define IGB_RX_PTHRESH                    16
+#define IGB_RX_HTHRESH                     8
+#define IGB_RX_WTHRESH                     1
 
 /* Transmit Descriptor Control */
 #define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */
@@ -1983,6 +1997,7 @@ struct e1000_hw {
 #define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */
 #define E1000_TXDCTL_COUNT_DESC 0x00400000 /* Enable the counting of desc.
                                               still to be processed. */
+#define E1000_TXDCTL_QUEUE_ENABLE  0x02000000 /* Enable specific Tx Queue */
 /* Transmit Configuration Word */
 #define E1000_TXCW_FD         0x00000020        /* TXCW full duplex */
 #define E1000_TXCW_HD         0x00000040        /* TXCW half duplex */
@@ -2032,6 +2047,7 @@ struct e1000_hw {
 
 /* Multiple Receive Queue Control */
 #define E1000_MRQC_ENABLE_MASK              0x00000003
+#define E1000_MRQC_ENABLE_VMDQ              0x00000003
 #define E1000_MRQC_ENABLE_RSS_2Q            0x00000001
 #define E1000_MRQC_ENABLE_RSS_INT           0x00000004
 #define E1000_MRQC_RSS_FIELD_MASK           0xFFFF0000
@@ -2283,6 +2299,7 @@ struct e1000_host_command_info {
 #define EEPROM_INIT_CONTROL3_PORT_A   0x0024
 #define EEPROM_CFG                    0x0012
 #define EEPROM_FLASH_VERSION          0x0032
+#define EEPROM_ALT_MAC_ADDR_PTR       0x0037
 #define EEPROM_CHECKSUM_REG           0x003F
 
 #define E1000_EEPROM_CFG_DONE         0x00040000   /* MNG config cycle done */
@@ -2435,6 +2452,7 @@ struct e1000_host_command_info {
 #define E1000_PBA_38K 0x0026
 #define E1000_PBA_40K 0x0028
 #define E1000_PBA_48K 0x0030    /* 48KB, default RX allocation */
+#define E1000_PBA_64K 0x0040    /* 64KB */
 
 #define E1000_PBS_16K E1000_PBA_16K
 
diff --git a/gpxe/src/drivers/net/e1000/e1000_osdep.h b/gpxe/src/drivers/net/e1000/e1000_osdep.h
index c2d9eb9..cdbf8d1 100644
--- a/gpxe/src/drivers/net/e1000/e1000_osdep.h
+++ b/gpxe/src/drivers/net/e1000/e1000_osdep.h
@@ -26,6 +26,7 @@
 
 *******************************************************************************/
 
+FILE_LICENCE ( GPL2_ONLY );
 
 /* glue for the OS independent part of e1000
  * includes register access macros
diff --git a/gpxe/src/drivers/net/eepro.c b/gpxe/src/drivers/net/eepro.c
index 2a163d1..a248692 100644
--- a/gpxe/src/drivers/net/eepro.c
+++ b/gpxe/src/drivers/net/eepro.c
@@ -31,6 +31,8 @@ has 34 pins, the top row of 2 are not used.
  * your option) any later version.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "etherboot.h"
 #include <errno.h>
 #include "nic.h"
diff --git a/gpxe/src/drivers/net/eepro100.c b/gpxe/src/drivers/net/eepro100.c
index e6e7db4..8a75608 100644
--- a/gpxe/src/drivers/net/eepro100.c
+++ b/gpxe/src/drivers/net/eepro100.c
@@ -1,17 +1,17 @@
 /*
- * eepro100.c -- This file implements the eepro100 driver for etherboot.
+ * eepro100.c -- This is a driver for Intel Fast Ethernet Controllers
+ * (ifec).
  *
+ * Originally written for Etherboot by:
  *
- * Copyright (C) AW Computer Systems.
- * written by R.E.Wolff -- R.E.Wolff at BitWizard.nl
+ *   Copyright (C) AW Computer Systems.
+ *   written by R.E.Wolff -- R.E.Wolff at BitWizard.nl
  *
+ *   AW Computer Systems is contributing to the free software community
+ *   by paying for this driver and then putting the result under GPL.
  *
- * AW Computer Systems is contributing to the free software community
- * by paying for this driver and then putting the result under GPL.
- *
- * If you need a Linux device driver, please contact BitWizard for a
- * quote.
- *
+ *   If you need a Linux device driver, please contact BitWizard for a
+ *   quote.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -28,14 +28,17 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  *
- *              date       version  by   what
- *  Written:    May 29 1997  V0.10  REW  Initial revision.
- * changes:     May 31 1997  V0.90  REW  Works!
- *              Jun 1  1997  V0.91  REW  Cleanup
- *              Jun 2  1997  V0.92  REW  Add some code documentation
- *              Jul 25 1997  V1.00  REW  Tested by AW to work in a PROM
- *                                       Cleanup for publication
+ *              date       version  by      what
+ *  Written:    May 29 1997  V0.10  REW     Initial revision.
+ * changes:     May 31 1997  V0.90  REW     Works!
+ *              Jun 1  1997  V0.91  REW     Cleanup
+ *              Jun 2  1997  V0.92  REW     Add some code documentation
+ *              Jul 25 1997  V1.00  REW     Tested by AW to work in a PROM
+ *                                          Cleanup for publication
  *              Dez 11 2004  V1.10  Kiszka  Add RX ring buffer support
+ *              Jun    2008  v2.0   mdeck   Updated to gPXE. Changed much.
+ *
+ * Cleanups and fixes by Thomas Miletich<thomas.miletich at gmail.com>
  *
  * This is the etherboot intel etherexpress Pro/100B driver.
  *
@@ -44,803 +47,1156 @@
  * lower level routines have been cut-and-pasted into this source.
  *
  * The driver was finished before Intel got the NDA out of the closet.
- * I still don't have the docs.
- *
  *
  * Datasheet is now published and available from 
  * ftp://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf
  *    - Michael Brown
  * */
 
-/* Philosophy of this driver.
- *
- * Probing:
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/*
+ * General Theory of Operation
  *
- * Using the pci.c functions of the Etherboot code, the 82557 chip is detected.
- * It is verified that the BIOS initialized everything properly and if
- * something is missing it is done now.
+ * Initialization
  *
+ * ifec_pci_probe() is called by gPXE during initialization. Typical NIC
+ * initialization is performed.  EEPROM data is read.
  *
- * Initialization:
+ * Network Boot
  *
+ * ifec_net_open() is called by gPXE before attempting to network boot from the
+ * card.  Here, the Command Unit & Receive Unit are initialized.  The tx & rx
+ * rings are setup.  The MAC address is programmed and the card is configured.
  *
- * The chip is then initialized to "know" its ethernet address, and to
- * start recieving packets. The Linux driver has a whole transmit and
- * recieve ring of buffers. This is neat if you need high performance:
- * you can write the buffers asynchronously to the chip reading the
- * buffers and transmitting them over the network.  Performance is NOT
- * an issue here. We can boot a 400k kernel in about two
- * seconds. (Theory: 0.4 seconds). Booting a system is going to take
- * about half a minute anyway, so getting 10 times closer to the
- * theoretical limit is going to make a difference of a few percent. */
-/* Not totally true: busy networks can cause packet drops due to RX
- * buffer overflows. Fixed in V1.10 of this driver. [Kiszka] */
-/*
+ * Transmit
  *
- * Transmitting and recieving.
+ * ifec_net_transmit() enqueues a packet in the tx ring - active::tcbs[]  The tx
+ * ring is composed of TCBs linked to each other into a ring.  A tx request
+ * fills out the next available TCB with a pointer to the packet data.
+ * The last enqueued tx is always at active::tcb_head.  Thus, a tx request fills
+ * out the TCB following tcb_head.
+ * active::tcb_tail points to the TCB we're awaiting completion of.
+ * ifec_tx_process() checks tcb_tail, and once complete,
+ * blindly increments tcb_tail to the next ring TCB.
  *
- * We have only one transmit descriptor. It has two buffer descriptors:
- * one for the header, and the other for the data.
- * We have multiple receive buffers (currently: 4). The chip is told to
- * receive packets and suspend itself once it ran on the last free buffer.
- * The recieve (poll) routine simply looks at the current recieve buffer,
- * picks the packet if any, and releases this buffer again (classic ring
- * buffer concept). This helps to avoid packet drops on busy networks.
+ * Receive
  *
- * Caveats:
- *
- * The Etherboot framework moves the code to the 48k segment from
- * 0x94000 to 0xa0000. There is just a little room between the end of
- * this driver and the 0xa0000 address. If you compile in too many
- * features, this will overflow.
- * The number under "hex" in the output of size that scrolls by while
- * compiling should be less than 8000. Maybe even the stack is up there,
- * so that you need even more headroom.
+ * priv::rfds[] is an array of Receive Frame Descriptors. The RFDs are linked
+ * together to form a ring.
+ * ifec_net_poll() calls ifec_rx_process(), which checks the next RFD for
+ * data.  If we received a packet, we allocate a new io_buffer and copy the
+ * packet data into it. If alloc_iob() fails, we don't touch the RFD and try
+ * again on the next poll.
  */
 
-/* The etherboot authors seem to dislike the argument ordering in
- * outb macros that Linux uses. I disklike the confusion that this
- * has caused even more.... This file uses the Linux argument ordering.  */
-/* Sorry not us. It's inherited code from FreeBSD. [The authors] */
+/*
+ * Debugging levels:
+ *	- DBG() is for any errors, i.e. failed alloc_iob(), malloc_dma(),
+ *	  TX overflow, corrupted packets, ...
+ *	- DBG2() is for successful events, like packet received,
+ *	  packet transmitted, and other general notifications.
+ *	- DBGP() prints the name of each called function on entry
+ */
 
-#include "etherboot.h"
-#include "nic.h"
+#include <stdint.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
 #include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
 #include <gpxe/pci.h>
+#include <gpxe/spi_bit.h>
+#include <gpxe/timer.h>
+#include <gpxe/nvs.h>
+#include <gpxe/threewire.h>
+#include <gpxe/netdevice.h>
+#include "eepro100.h"
 
-static int ioaddr;
+/****************************** Global data **********************************/
 
-enum speedo_offsets {
-  SCBStatus = 0, SCBCmd = 2,      /* Rx/Command Unit command and status. */
-  SCBPointer = 4,                 /* General purpose pointer. */
-  SCBPort = 8,                    /* Misc. commands and operands.  */
-  SCBflash = 12, SCBeeprom = 14,  /* EEPROM and flash memory control. */
-  SCBCtrlMDI = 16,                /* MDI interface control. */
-  SCBEarlyRx = 20,                /* Early receive byte count. */
+/*
+ * This is the default configuration command data. The values were copied from
+ * the Linux kernel initialization for the eepro100.
+ */
+static struct ifec_cfg ifec_cfg = {
+	.status  = 0,
+	.command = CmdConfigure | CmdSuspend,
+	.link    = 0,        /* Filled in later */
+	.byte = { 22,        /* How many bytes in this array */
+	          ( TX_FIFO << 4 ) | RX_FIFO,  /* Rx & Tx FIFO limits */
+	          0, 0,                        /* Adaptive Interframe Spacing */
+	          RX_DMA_COUNT,                /* Rx DMA max byte count */
+	          TX_DMA_COUNT + 0x80,         /* Tx DMA max byte count */
+	          0x32,      /* Many bits. */
+	          0x03,      /* Discard short receive & Underrun retries */
+	          1,         /* 1=Use MII  0=Use AUI */
+	          0,
+	          0x2E,      /* NSAI, Preamble length, & Loopback*/
+	          0,         /* Linear priority */
+	          0x60,      /* L PRI MODE & Interframe spacing */
+	          0, 0xf2,
+	          0x48,      /* Promiscuous, Broadcast disable, CRS & CDT */
+	          0, 0x40,
+	          0xf2,      /* Stripping, Padding, Receive CRC Transfer */
+	          0x80,      /* 0x40=Force full-duplex, 0x80=Allowfull-duplex*/
+	          0x3f,      /* Multiple IA */
+	          0x0D }     /* Multicast all */
 };
 
-enum SCBCmdBits {
-	SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
-	SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
-	SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
-	/* The rest are Rx and Tx commands. */
-	CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
-	CUCmdBase=0x0060,	/* CU Base address (set to zero) . */
-	CUDumpStats=0x0070, /* Dump then reset stats counters. */
-	RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
-	RxResumeNoResources=0x0007,
+static struct net_device_operations ifec_operations = {
+	.open     = ifec_net_open,
+	.close    = ifec_net_close,
+	.transmit = ifec_net_transmit,
+	.poll     = ifec_net_poll,
+	.irq      = ifec_net_irq
 };
 
-static int do_eeprom_cmd(int cmd, int cmd_len);
-void hd(void *where, int n);
-
-/***********************************************************************/
-/*                       I82557 related defines                        */
-/***********************************************************************/
-
-/* Serial EEPROM section.
-   A "bit" grungy, but we work our way through bit-by-bit :->. */
-/*  EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK    0x01    /* EEPROM shift clock. */
-#define EE_CS           0x02    /* EEPROM chip select. */
-#define EE_DATA_WRITE   0x04    /* EEPROM chip data in. */
-#define EE_DATA_READ    0x08    /* EEPROM chip data out. */
-#define EE_WRITE_0      0x4802
-#define EE_WRITE_1      0x4806
-#define EE_ENB          (0x4800 | EE_CS)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_READ_CMD     6
-
-/* The SCB accepts the following controls for the Tx and Rx units: */
-#define  CU_START       0x0010
-#define  CU_RESUME      0x0020
-#define  CU_STATSADDR   0x0040
-#define  CU_SHOWSTATS   0x0050  /* Dump statistics counters. */
-#define  CU_CMD_BASE    0x0060  /* Base address to add to add CU commands. */
-#define  CU_DUMPSTATS   0x0070  /* Dump then reset stats counters. */
-
-#define  RX_START       0x0001
-#define  RX_RESUME      0x0002
-#define  RX_ABORT       0x0004
-#define  RX_ADDR_LOAD   0x0006
-#define  RX_RESUMENR    0x0007
-#define INT_MASK        0x0100
-#define DRVR_INT        0x0200          /* Driver generated interrupt. */
-
-enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
-                                         S80C24, PhyUndefined, DP83840A=10, };
-
-/* Commands that can be put in a command list entry. */
-enum commands {
-  CmdNOp = 0,
-  CmdIASetup = 1,
-  CmdConfigure = 2,
-  CmdMulticastList = 3,
-  CmdTx = 4,
-  CmdTDR = 5,
-  CmdDump = 6,
-  CmdDiagnose = 7,
-
-  /* And some extra flags: */
-  CmdSuspend = 0x4000,      /* Suspend after completion. */
-  CmdIntr = 0x2000,         /* Interrupt after completion. */
-  CmdTxFlex = 0x0008,       /* Use "Flexible mode" for CmdTx command. */
-};
+/******************* gPXE PCI Device Driver API functions ********************/
 
-/* How to wait for the command unit to accept a command.
-   Typically this takes 0 ticks. */
-static inline void wait_for_cmd_done(int cmd_ioaddr)
+/*
+ * Initialize the PCI device.
+ *
+ * @v pci 		The device's associated pci_device structure.
+ * @v id  		The PCI device + vendor id.
+ * @ret rc		Returns zero if successfully initialized.
+ *
+ * This function is called very early on, while gPXE is initializing.
+ * This is a gPXE PCI Device Driver API function.
+ */
+static int ifec_pci_probe ( struct pci_device *pci,
+                            const struct pci_device_id *id __unused )
 {
-  int wait = 0;
-  int delayed_cmd;
-
-  do
-    if (inb(cmd_ioaddr) == 0) return;
-  while(++wait <= 100);
-  delayed_cmd = inb(cmd_ioaddr);
-  do
-    if (inb(cmd_ioaddr) == 0) break;
-  while(++wait <= 10000);
-  printf("Command %2.2x was not immediately accepted, %d ticks!\n",
-      delayed_cmd, wait);
+	struct net_device *netdev;
+	struct ifec_private *priv;
+	int rc;
+
+	DBGP ( "ifec_pci_probe: " );
+
+	if ( pci->ioaddr == 0 )
+		return -EINVAL;
+
+	netdev = alloc_etherdev ( sizeof(*priv) );
+	if ( !netdev )
+		return -ENOMEM;
+
+	netdev_init ( netdev, &ifec_operations );
+	priv = netdev->priv;
+
+	pci_set_drvdata ( pci, netdev );
+	netdev->dev = &pci->dev;
+
+	/* enable bus master, etc */
+	adjust_pci_device( pci );
+
+	DBGP ( "pci " );
+
+	memset ( priv, 0, sizeof(*priv) );
+	priv->ioaddr = pci->ioaddr;
+
+	ifec_reset ( netdev );
+	DBGP ( "reset " );
+
+	ifec_init_eeprom ( netdev );
+
+	/* read MAC address */
+	nvs_read ( &priv->eeprom.nvs, EEPROM_ADDR_MAC_0, netdev->hw_addr,
+		   ETH_ALEN );
+	/* read mdio_register */
+	nvs_read ( &priv->eeprom.nvs, EEPROM_ADDR_MDIO_REGISTER,
+		   &priv->mdio_register, 2 );
+
+	ifec_link_update ( netdev );	/* Update link state */
+
+	if ( ( rc = register_netdev ( netdev ) ) != 0 )
+		goto error;
+
+	DBGP ( "ints\n" );
+
+	return 0;
+
+error:
+	ifec_reset     ( netdev );
+	netdev_nullify ( netdev );
+	netdev_put     ( netdev );
+
+	return rc;
 }
 
-/* Elements of the dump_statistics block. This block must be lword aligned. */
-static struct speedo_stats {
-        u32 tx_good_frames;
-        u32 tx_coll16_errs;
-        u32 tx_late_colls;
-        u32 tx_underruns;
-        u32 tx_lost_carrier;
-        u32 tx_deferred;
-        u32 tx_one_colls;
-        u32 tx_multi_colls;
-        u32 tx_total_colls;
-        u32 rx_good_frames;
-        u32 rx_crc_errs;
-        u32 rx_align_errs;
-        u32 rx_resource_errs;
-        u32 rx_overrun_errs;
-        u32 rx_colls_errs;
-        u32 rx_runt_errs;
-        u32 done_marker;
-} lstats;
-
-/* A speedo3 TX buffer descriptor with two buffers... */
-static struct TxFD {
-	volatile s16 status;
-	s16 command;
-	u32 link;          /* void * */
-	u32 tx_desc_addr;  /* (almost) Always points to the tx_buf_addr element. */
-	s32 count;         /* # of TBD (=2), Tx start thresh., etc. */
-	/* This constitutes two "TBD" entries: hdr and data */
-	u32 tx_buf_addr0;  /* void *, header of frame to be transmitted.  */
-	s32 tx_buf_size0;  /* Length of Tx hdr. */
-	u32 tx_buf_addr1;  /* void *, data to be transmitted.  */
-	s32 tx_buf_size1;  /* Length of Tx data. */
-} txfd;
-
-struct RxFD {               /* Receive frame descriptor. */
-	volatile s16 status;
-	s16 command;
-	u32 link;                 /* struct RxFD * */
-	u32 rx_buf_addr;          /* void * */
-	u16 count;
-	u16 size;
-	char packet[1518];
-};
+/*
+ * Remove a device from the PCI device list.
+ *
+ * @v pci		PCI device to remove.
+ *
+ * This is a PCI Device Driver API function.
+ */
+static void ifec_pci_remove ( struct pci_device *pci )
+{
+	struct net_device *netdev = pci_get_drvdata ( pci );
+
+	DBGP ( "ifec_pci_remove\n" );
+
+	unregister_netdev ( netdev );
+	ifec_reset        ( netdev );
+	netdev_nullify    ( netdev );
+	netdev_put        ( netdev );
+}
+
+/****************** gPXE Network Device Driver API functions *****************/
+
+/*
+ * Close a network device.
+ *
+ * @v netdev		Device to close.
+ *
+ * This is a gPXE Network Device Driver API function.
+ */
+static void ifec_net_close ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	unsigned long ioaddr = priv->ioaddr;
+	unsigned short intr_status;
+
+	DBGP ( "ifec_net_close\n" );
+
+	/* disable interrupts */
+	ifec_net_irq ( netdev, 0 );
+
+	/* Ack & clear ints */
+	intr_status = inw ( ioaddr + SCBStatus );
+	outw ( intr_status, ioaddr + SCBStatus );
+	inw ( ioaddr + SCBStatus );
+
+	ifec_reset ( netdev );
+
+	/* Free any resources */
+	ifec_free ( netdev );
+}
+
+/* Interrupts to be masked */
+#define INTERRUPT_MASK	( SCBMaskEarlyRx | SCBMaskFlowCtl )
+
+/*
+ * Enable or disable IRQ masking.
+ *
+ * @v netdev		Device to control.
+ * @v enable		Zero to mask off IRQ, non-zero to enable IRQ.
+ *
+ * This is a gPXE Network Driver API function.
+ */
+static void ifec_net_irq ( struct net_device *netdev, int enable )
+{
+	struct ifec_private *priv = netdev->priv;
+	unsigned long ioaddr = priv->ioaddr;
+
+	DBGP ( "ifec_net_irq\n" );
+
+	outw ( enable ? INTERRUPT_MASK : SCBMaskAll, ioaddr + SCBCmd );
+}
+
+/*
+ * Opens a network device.
+ *
+ * @v netdev		Device to be opened.
+ * @ret rc  		Non-zero if failed to open.
+ *
+ * This enables tx and rx on the device.
+ * This is a gPXE Network Device Driver API function.
+ */
+static int ifec_net_open ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	struct ifec_ias *ias = NULL;
+	struct ifec_cfg *cfg = NULL;
+	int i, options;
+	int rc = -ENOMEM;
+
+	DBGP ( "ifec_net_open: " );
+
+	/* Ensure interrupts are disabled. */
+	ifec_net_irq ( netdev, 0 );
+
+	/* Initialize Command Unit and Receive Unit base addresses. */
+	ifec_scb_cmd ( netdev, 0, RUAddrLoad );
+	ifec_scb_cmd ( netdev, virt_to_bus ( &priv->stats ), CUStatsAddr );
+	ifec_scb_cmd ( netdev, 0, CUCmdBase );
+
+	/* Initialize both rings */
+	if ( ( rc = ifec_rx_setup ( netdev ) ) != 0 )
+		goto error;
+	if ( ( rc = ifec_tx_setup ( netdev ) ) != 0 )
+		goto error;
+
+	/* Initialize MDIO */
+	options = 0x00; /* 0x40 = 10mbps half duplex, 0x00 = Autosense */
+	ifec_mdio_setup ( netdev, options );
+
+	/* Prepare MAC address w/ Individual Address Setup (ias) command.*/
+	ias = malloc_dma ( sizeof ( *ias ), CB_ALIGN );
+	if ( !ias ) {
+		rc = -ENOMEM;
+		goto error;
+	}
+	ias->command      = CmdIASetup;
+	ias->status       = 0;
+	memcpy ( ias->ia, netdev->ll_addr, ETH_ALEN );
+
+	/* Prepare operating parameters w/ a configure command. */
+	cfg = malloc_dma ( sizeof ( *cfg ), CB_ALIGN );
+	if ( !cfg ) {
+		rc = -ENOMEM;
+		goto error;
+	}
+	memcpy ( cfg, &ifec_cfg, sizeof ( *cfg ) );
+	cfg->link     = virt_to_bus ( priv->tcbs );
+	cfg->byte[19] = ( options & 0x10 ) ? 0xC0 : 0x80;
+	ias->link     = virt_to_bus ( cfg );
+
+	/* Issue the ias and configure commands. */
+	ifec_scb_cmd ( netdev, virt_to_bus ( ias ), CUStart );
+	ifec_scb_cmd_wait ( netdev );
+	priv->configured = 1;
+
+	/* Wait up to 10 ms for configuration to initiate */
+	for ( i = 10; i && !cfg->status; i-- )
+		mdelay ( 1 );
+	if ( ! cfg->status ) {
+		DBG ( "Failed to initiate!\n" );
+		goto error;
+	}
+	free_dma ( ias, sizeof ( *ias ) );
+	free_dma ( cfg, sizeof ( *cfg ) );
+	DBG2 ( "cfg " );
+
+	/* Enable rx by sending ring address to card */
+	if ( priv->rfds[0] != NULL ) {
+		ifec_scb_cmd ( netdev, virt_to_bus( priv->rfds[0] ), RUStart );
+		ifec_scb_cmd_wait ( netdev );
+	}
+	DBG2 ( "rx_start\n" );
+
+	return 0;
+
+error:
+	free_dma ( cfg, sizeof ( *cfg ) );
+	free_dma ( ias, sizeof ( *ias ) );
+	ifec_free ( netdev );
+	ifec_reset ( netdev );
+	return rc;
+}
+
+/*
+ * This function allows a driver to process events during operation.
+ *
+ * @v netdev		Device being polled.
+ *
+ * This is called periodically by gPXE to let the driver check the status of
+ * transmitted packets and to allow the driver to check for received packets.
+ * This is a gPXE Network Device Driver API function.
+ */
+static void ifec_net_poll ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	static int linkpoll = 0;
+	unsigned short intr_status;
+
+	DBGP ( "ifec_net_poll\n" );
+
+	/* acknowledge interrupts ASAP */
+	intr_status = inw ( priv->ioaddr + SCBStatus );
+	outw ( intr_status, priv->ioaddr + SCBStatus );
+	inw ( priv->ioaddr + SCBStatus );
+
+	DBG2 ( "poll - status: 0x%04X\n", intr_status );
+
+	if ( ++linkpoll > LINK_CHECK_PERIOD ) {
+		linkpoll = 0;
+		ifec_link_update ( netdev );	/* Update link state */
+	}
+
+	/* anything to do here? */
+	if ( ( intr_status & ( ~INTERRUPT_MASK ) ) == 0 )
+		return;
+
+	/* process received and transmitted packets */
+	ifec_tx_process ( netdev );
+	ifec_rx_process ( netdev );
+
+	ifec_check_ru_status ( netdev, intr_status );
+
+	return;
+}
+
+/*
+ * This transmits a packet.
+ *
+ * @v netdev		Device to transmit from.
+ * @v iobuf 		Data to transmit.
+ * @ret rc  		Non-zero if failed to transmit.
+ *
+ * This is a gPXE Network Driver API function.
+ */
+static int ifec_net_transmit ( struct net_device *netdev,
+                               struct io_buffer *iobuf )
+{
+	struct ifec_private *priv = netdev->priv;
+	struct ifec_tcb *tcb = priv->tcb_head->next;
+	unsigned long ioaddr = priv->ioaddr;
+
+	DBGP ( "ifec_net_transmit\n" );
+
+	/* Wait for TCB to become available. */
+	if ( tcb->status || tcb->iob ) {
+		DBG ( "TX overflow\n" );
+		return -ENOBUFS;
+	}
+
+	DBG2 ( "transmitting packet (%d bytes). status = %hX, cmd=%hX\n",
+		iob_len ( iobuf ), tcb->status, inw ( ioaddr + SCBCmd ) );
+
+	tcb->command   = CmdSuspend | CmdTx | CmdTxFlex;
+	tcb->count     = 0x01208000;
+	tcb->tbd_addr0 = virt_to_bus ( iobuf->data );
+	tcb->tbd_size0 = 0x3FFF & iob_len ( iobuf );
+	tcb->iob = iobuf;
+
+	ifec_tx_wake ( netdev );
 
-static struct nic_operations eepro100_operations;
-
-#define RXFD_COUNT 4
-struct {
-	struct RxFD rxfds[RXFD_COUNT];
-} eepro100_bufs __shared;
-#define rxfds eepro100_bufs.rxfds
-static unsigned int rxfd = 0;
-
-static int congenb = 0;         /* Enable congestion control in the DP83840. */
-static int txfifo = 8;          /* Tx FIFO threshold in 4 byte units, 0-15 */
-static int rxfifo = 8;          /* Rx FIFO threshold, default 32 bytes. */
-static int txdmacount = 0;      /* Tx DMA burst length, 0-127, default 0. */
-static int rxdmacount = 0;      /* Rx DMA length, 0 means no preemption. */
-
-/* I don't understand a byte in this structure. It was copied from the
- * Linux kernel initialization for the eepro100. -- REW */
-static struct ConfCmd {
-  s16 status;
-  s16 command;
-  u32 link;
-  unsigned char data[22];
-} confcmd = {
-  0, 0, 0, /* filled in later */
-  {22, 0x08, 0, 0,  0, 0x80, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
-   0, 0x2E, 0,  0x60, 0,
-   0xf2, 0x48,   0, 0x40, 0xf2, 0x80,        /* 0x40=Force full-duplex */
-   0x3f, 0x05, }
+	/* Append to end of ring. */
+	priv->tcb_head = tcb;
+
+	return 0;
+}
+
+/*************************** Local support functions *************************/
+
+/* Define what each GPIO Pin does */
+static const uint16_t ifec_ee_bits[] = {
+	[SPI_BIT_SCLK]	= EE_SHIFT_CLK,
+	[SPI_BIT_MOSI]	= EE_DATA_WRITE,
+	[SPI_BIT_MISO]	= EE_DATA_READ,
+	[SPI_BIT_SS(0)]	= EE_ENB,
 };
 
-/***********************************************************************/
-/*                       Locally used functions                        */
-/***********************************************************************/
+/*
+ * Read a single bit from the GPIO pins used for SPI.
+ * should be called by SPI bitbash functions only
+ *
+ * @v basher		Bitbash device
+ * @v bit_id		Line to be read
+ */
+static int ifec_spi_read_bit ( struct bit_basher *basher,
+			       unsigned int bit_id )
+{
+	struct ifec_private *priv =
+		container_of ( basher, struct ifec_private, spi.basher );
+	unsigned long ee_addr = priv->ioaddr + CSREeprom;
+	unsigned int ret = 0;
+	uint16_t mask;
+
+	DBGP ( "ifec_spi_read_bit\n" );
 
-/* Support function: mdio_write
+	mask = ifec_ee_bits[bit_id];
+	ret = inw (ee_addr);
+
+	return ( ret & mask ) ? 1 : 0;
+}
+
+/*
+ * Write a single bit to the GPIO pins used for SPI.
+ * should be called by SPI bitbash functions only
  *
- * This probably writes to the "physical media interface chip".
- * -- REW
+ * @v basher		Bitbash device
+ * @v bit_id		Line to write to
+ * @v data		Value to write
  */
+static void ifec_spi_write_bit ( struct bit_basher *basher,
+				 unsigned int bit_id,
+				 unsigned long data )
+{
+	struct ifec_private *priv =
+		container_of ( basher, struct ifec_private, spi.basher );
+	unsigned long ee_addr = priv->ioaddr + CSREeprom;
+	short val;
+	uint16_t mask = ifec_ee_bits[bit_id];
+
+	DBGP ( "ifec_spi_write_bit\n" );
+
+	val = inw ( ee_addr );
+	val &= ~mask;
+	val |= data & mask;
 
-static int mdio_write(int phy_id, int location, int value)
+	outw ( val, ee_addr );
+}
+
+/* set function pointer to SPI read- and write-bit functions */
+static struct bit_basher_operations ifec_basher_ops = {
+	.read = ifec_spi_read_bit,
+	.write = ifec_spi_write_bit,
+};
+
+/*
+ * Initialize the eeprom stuff
+ *
+ * @v netdev		Network device
+ */
+static void ifec_init_eeprom ( struct net_device *netdev )
 {
-	int val, boguscnt = 64*4;         /* <64 usec. to complete, typ 27 ticks */
+	struct ifec_private *priv = netdev->priv;
 
-	outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
-	     ioaddr + SCBCtrlMDI);
-	do {
-		udelay(16);
+	DBGP ( "ifec_init_eeprom\n" );
 
-		val = inl(ioaddr + SCBCtrlMDI);
-		if (--boguscnt < 0) {
-			printf(" mdio_write() timed out with val = %X.\n", val);
-			break;
-		}
-	} while (! (val & 0x10000000));
-	return val & 0xffff;
+	priv->spi.basher.op = &ifec_basher_ops;
+	priv->spi.bus.mode = SPI_MODE_THREEWIRE;
+	init_spi_bit_basher ( &priv->spi );
+
+	priv->eeprom.bus = &priv->spi.bus;
+
+	/* init as 93c46(93c14 compatible) first, to set the command len,
+	 * block size and word len. Needs to be set for address len detection.
+	 */
+	init_at93c46 ( &priv->eeprom, 16 );
+
+	/* detect address length, */
+	threewire_detect_address_len ( &priv->eeprom );
+
+	/* address len == 8 means 93c66 instead of 93c46 */
+	if ( priv->eeprom.address_len == 8 )
+		init_at93c66 ( &priv->eeprom, 16 );
+}
+
+/*
+ * Check if the network cable is plugged in.
+ *
+ * @v netdev  		Network device to check.
+ * @ret retval		greater 0 if linkup.
+ */
+static int ifec_link_check ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	unsigned short mdio_register = priv->mdio_register;
+
+	DBGP ( "ifec_link_check\n" );
+
+	/* Read the status register once to discard stale data */
+	ifec_mdio_read ( netdev, mdio_register & 0x1f, 1 );
+	/* Check to see if network cable is plugged in. */
+	if ( ! ( ifec_mdio_read ( netdev, mdio_register & 0x1f, 1 )
+		  & ( 1 << 2 ) ) ) {
+		return 0;
+	}
+	return 1;
 }
 
-/* Support function: mdio_read
+/*
+ * Check network cable link, inform gPXE as appropriate.
+ *
+ * @v netdev  		Network device to check.
+ */
+static void ifec_link_update ( struct net_device *netdev )
+{
+	DBGP ( "ifec_link_update\n" );
+
+	/* Update link state */
+	if ( ifec_link_check ( netdev ) )
+		netdev_link_up ( netdev );
+	else
+		netdev_link_down ( netdev );
+}
+
+/*
+ * Support function: ifec_mdio_read
  *
  * This probably reads a register in the "physical media interface chip".
  * -- REW
  */
-static int mdio_read(int phy_id, int location)
+static int ifec_mdio_read ( struct net_device *netdev, int phy_id,
+                                                       int location )
 {
-	int val, boguscnt = 64*4;               /* <64 usec. to complete, typ 27 ticks */
-	outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
+	struct ifec_private *priv = netdev->priv;
+	unsigned long ioaddr = priv->ioaddr;
+	int val;
+	int boguscnt = 64*4;     /* <64 usec. to complete, typ 27 ticks */
+
+	DBGP ( "ifec_mdio_read\n" );
+
+	outl ( 0x08000000 | ( location << 16 ) | ( phy_id << 21 ),
+	       ioaddr + CSRCtrlMDI );
 	do {
-		udelay(16);
+		udelay ( 16 );
 
-		val = inl(ioaddr + SCBCtrlMDI);
+		val = inl ( ioaddr + CSRCtrlMDI );
 
-		if (--boguscnt < 0) {
-			printf( " mdio_read() timed out with val = %X.\n", val);
+		if ( --boguscnt < 0 ) {
+			DBG ( " ifec_mdio_read() time out with val = %X.\n",
+			         val );
 			break;
 		}
-	} while (! (val & 0x10000000));
+	} while (! ( val & 0x10000000 ) );
 	return val & 0xffff;
 }
 
-/* The fixes for the code were kindly provided by Dragan Stancevic
-   <visitor at valinux.com> to strictly follow Intel specifications of EEPROM
-   access timing.
-   The publicly available sheet 64486302 (sec. 3.1) specifies 1us access
-   interval for serial EEPROM.  However, it looks like that there is an
-   additional requirement dictating larger udelay's in the code below.
-   2000/05/24  SAW */
-static int do_eeprom_cmd(int cmd, int cmd_len)
+/*
+ * Initializes MDIO.
+ *
+ * @v netdev 		Network device
+ * @v options		MDIO options
+ */
+static void ifec_mdio_setup ( struct net_device *netdev, int options )
 {
-	unsigned retval = 0;
-	long ee_addr = ioaddr + SCBeeprom;
+	struct ifec_private *priv = netdev->priv;
+	unsigned short mdio_register = priv->mdio_register;
 
-	outw(EE_ENB, ee_addr); udelay(2);
-	outw(EE_ENB | EE_SHIFT_CLK, ee_addr); udelay(2);
+	DBGP ( "ifec_mdio_setup\n" );
 
-	/* Shift the command bits out. */
-	do {
-		short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
-		outw(dataval, ee_addr); udelay(2);
-		outw(dataval | EE_SHIFT_CLK, ee_addr); udelay(2);
-		retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
-	} while (--cmd_len >= 0);
-	outw(EE_ENB, ee_addr); udelay(2);
-
-	/* Terminate the EEPROM access. */
-	outw(EE_ENB & ~EE_CS, ee_addr);
-	return retval;
+	if (   ( (mdio_register>>8) & 0x3f ) == DP83840
+	    || ( (mdio_register>>8) & 0x3f ) == DP83840A ) {
+		int mdi_reg23 = ifec_mdio_read ( netdev, mdio_register
+						  & 0x1f, 23 ) | 0x0422;
+		if (CONGENB)
+			mdi_reg23 |= 0x0100;
+		DBG2 ( "DP83840 specific setup, setting register 23 to "
+		                                         "%hX.\n", mdi_reg23 );
+		ifec_mdio_write ( netdev, mdio_register & 0x1f, 23, mdi_reg23 );
+	}
+	DBG2 ( "dp83840 " );
+	if ( options != 0 ) {
+		ifec_mdio_write ( netdev, mdio_register & 0x1f, 0,
+		                           ( (options & 0x20) ? 0x2000 : 0 ) |
+		                           ( (options & 0x10) ? 0x0100 : 0 ) );
+		DBG2 ( "set mdio_register. " );
+	}
 }
 
-#if 0
-static inline void whereami (const char *str)
+/*
+ * Support function: ifec_mdio_write
+ *
+ * This probably writes to the "physical media interface chip".
+ * -- REW
+ */
+static int ifec_mdio_write ( struct net_device *netdev,
+                             int phy_id, int location, int value )
 {
-  printf ("%s\n", str);
-  sleep (2);
-}
-#else
-#define whereami(s)
-#endif
+	struct ifec_private *priv = netdev->priv;
+	unsigned long ioaddr = priv->ioaddr;
+	int val;
+	int boguscnt = 64*4;     /* <64 usec. to complete, typ 27 ticks */
 
-static void eepro100_irq(struct nic *nic __unused, irq_action_t action)
-{
-	uint16_t enabled_mask = ( SCBMaskCmdDone | SCBMaskCmdIdle |
-				  SCBMaskEarlyRx | SCBMaskFlowCtl );
+	DBGP ( "ifec_mdio_write\n" );
 
-	switch ( action ) {
-	case DISABLE :
-		outw(SCBMaskAll, ioaddr + SCBCmd);
-		break;
-	case ENABLE :
-		outw(enabled_mask, ioaddr + SCBCmd);
-		break;
-	case FORCE :
-		outw(enabled_mask | SCBTriggerIntr, ioaddr + SCBCmd);
-		break;
-	}
+	outl ( 0x04000000 | ( location << 16 ) | ( phy_id << 21 ) | value,
+	       ioaddr + CSRCtrlMDI );
+	do {
+		udelay ( 16 );
+
+		val = inl ( ioaddr + CSRCtrlMDI );
+		if ( --boguscnt < 0 ) {
+			DBG ( " ifec_mdio_write() time out with val = %X.\n",
+			      val );
+			break;
+		}
+	} while (! ( val & 0x10000000 ) );
+	return val & 0xffff;
 }
 
-/* function: eepro100_transmit
- * This transmits a packet.
+/*
+ * Resets the hardware.
  *
- * Arguments: char d[6]:          destination ethernet address.
- *            unsigned short t:   ethernet protocol type.
- *            unsigned short s:   size of the data-part of the packet.
- *            char *p:            the data for the packet.
- * returns:   void.
+ * @v netdev		Network device
  */
-
-static void eepro100_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p)
+static void ifec_reset ( struct net_device *netdev )
 {
-	struct eth_hdr {
-		unsigned char dst_addr[ETH_ALEN];
-		unsigned char src_addr[ETH_ALEN];
-		unsigned short type;
-	} hdr;
-	unsigned short status;
-	int s1, s2;
-	unsigned long ct;
-
-	status = inw(ioaddr + SCBStatus);
-	/* Acknowledge all of the current interrupt sources ASAP. */
-	outw(status & 0xfc00, ioaddr + SCBStatus);
-
-#ifdef	DEBUG
-	printf ("transmitting type %hX packet (%d bytes). status = %hX, cmd=%hX\n",
-		t, s, status, inw (ioaddr + SCBCmd));
-#endif
-
-	memcpy (&hdr.dst_addr, d, ETH_ALEN);
-	memcpy (&hdr.src_addr, nic->node_addr, ETH_ALEN);
-
-	hdr.type = htons (t);
-
-	txfd.status = 0;
-	txfd.command = CmdSuspend | CmdTx | CmdTxFlex;
-	txfd.link   = virt_to_bus (&txfd);
-	txfd.count   = 0x02208000;
-	txfd.tx_desc_addr = virt_to_bus(&txfd.tx_buf_addr0);
-
-	txfd.tx_buf_addr0 = virt_to_bus (&hdr);
-	txfd.tx_buf_size0 = sizeof (hdr);
-
-	txfd.tx_buf_addr1 = virt_to_bus (p);
-	txfd.tx_buf_size1 = s;
-
-#ifdef	DEBUG
-	printf ("txfd: \n");
-	hd (&txfd, sizeof (txfd));
-#endif
-
-	outl(virt_to_bus(&txfd), ioaddr + SCBPointer);
-	outb(CU_START, ioaddr + SCBCmd);
-	wait_for_cmd_done(ioaddr + SCBCmd);
-
-	s1 = inw (ioaddr + SCBStatus);
-
-	ct = currticks();
-	/* timeout 10 ms for transmit */
-	while (!txfd.status && ct + 10*1000)
-		/* Wait */;
-	s2 = inw (ioaddr + SCBStatus);
-
-#ifdef	DEBUG
-	printf ("s1 = %hX, s2 = %hX.\n", s1, s2);
-#endif
+	struct ifec_private *priv = netdev->priv;
+	unsigned long ioaddr = priv->ioaddr;
+
+	DBGP ( "ifec_reset\n" );
+
+	/* do partial reset first */
+	outl ( PortPartialReset, ioaddr + CSRPort );
+	inw ( ioaddr + SCBStatus );
+	udelay ( 20 );
+
+	/* full reset */
+	outl ( PortReset, ioaddr + CSRPort );
+	inw ( ioaddr + SCBStatus );
+	udelay ( 20 );
+
+	/* disable interrupts again */
+	ifec_net_irq ( netdev, 0 );
 }
 
 /*
- * Sometimes the receiver stops making progress.  This routine knows how to
- * get it going again, without losing packets or being otherwise nasty like
- * a chip reset would be.  Previously the driver had a whole sequence
- * of if RxSuspended, if it's no buffers do one thing, if it's no resources,
- * do another, etc.  But those things don't really matter.  Separate logic
- * in the ISR provides for allocating buffers--the other half of operation
- * is just making sure the receiver is active.  speedo_rx_soft_reset does that.
- * This problem with the old, more involved algorithm is shown up under
- * ping floods on the order of 60K packets/second on a 100Mbps fdx network.
+ * free()s the tx/rx rings.
+ *
+ * @v netdev		Network device
  */
-static void
-speedo_rx_soft_reset(void)
+static void ifec_free ( struct net_device *netdev )
 {
+	struct ifec_private *priv = netdev_priv ( netdev );
 	int i;
 
+	DBGP ( "ifec_free\n" );
 
-#ifdef	DEBUG
-	printf("reset\n");
-#endif
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	/*
-	 * Put the hardware into a known state.
-	 */
-	outb(RX_ABORT, ioaddr + SCBCmd);
-
-	for (i = 0; i < RXFD_COUNT; i++) {
-		rxfds[i].status      = 0;
-		rxfds[i].rx_buf_addr = 0xffffffff;
-		rxfds[i].count       = 0;
-		rxfds[i].size        = 1528;
+	/* free all allocated receive io_buffers */
+	for ( i = 0; i < RFD_COUNT; i++ ) {
+		free_iob ( priv->rx_iobs[i] );
+		priv->rx_iobs[i] = NULL;
+		priv->rfds[i] = NULL;
 	}
 
-	wait_for_cmd_done(ioaddr + SCBCmd);
+	/* free TX ring buffer */
+	free_dma ( priv->tcbs, TX_RING_BYTES );
 
-	outl(virt_to_bus(&rxfds[rxfd]), ioaddr + SCBPointer);
-	outb(RX_START, ioaddr + SCBCmd);
+	priv->tcbs = NULL;
 }
 
-/* function: eepro100_poll / eth_poll
- * This receives a packet from the network.
- *
- * Arguments: none
+/*
+ * Initializes an RFD.
  *
- * returns:   1 if a packet was received.
- *            0 if no packet was received.
- * side effects:
- *            returns the packet in the array nic->packet.
- *            returns the length of the packet in nic->packetlen.
+ * @v rfd    		RFD struct to initialize
+ * @v command		Command word
+ * @v link   		Link value
  */
+static void ifec_rfd_init ( struct ifec_rfd *rfd, s16 command, u32 link )
+{
+	DBGP ( "ifec_rfd_init\n" );
+
+	rfd->status      = 0;
+	rfd->command     = command;
+	rfd->rx_buf_addr = 0xFFFFFFFF;
+	rfd->count       = 0;
+	rfd->size        = RFD_PACKET_LEN;
+	rfd->link        = link;
+}
 
-static int eepro100_poll(struct nic *nic, int retrieve)
+/*
+ * Send address of new RFD to card
+ *
+ * @v netdev		Network device
+ */
+static void ifec_reprime_ru ( struct net_device *netdev )
 {
-	if (rxfds[rxfd].status) {
-		if (!retrieve)
-			return 1;
-#ifdef	DEBUG
-		printf("Got a packet: Len = %d, rxfd = %d.\n",
-		       rxfds[rxfd].count & 0x3fff, rxfd);
-#endif
-		/* First save the data from the rxfd */
-		nic->packetlen = rxfds[rxfd].count & 0x3fff;
-		memcpy(nic->packet, rxfds[rxfd].packet, nic->packetlen);
-
-		rxfds[rxfd].status      = 0;
-		rxfds[rxfd].command     = 0xc000;
-		rxfds[rxfd].rx_buf_addr = 0xFFFFFFFF;
-		rxfds[rxfd].count       = 0;
-		rxfds[rxfd].size        = 1528;
-		rxfds[(rxfd-1) % RXFD_COUNT].command = 0x0000;
-		rxfd = (rxfd+1) % RXFD_COUNT;
-
-#ifdef	DEBUG
-		hd (nic->packet, 0x30);
-#endif
-
-		/* Acknowledge all conceivable interrupts */
-		outw(0xff00, ioaddr + SCBStatus);
-
-		return 1;
+	struct ifec_private *priv = netdev->priv;
+	int cur_rx = priv->cur_rx;
+	
+	DBGP ( "ifec_reprime_ru\n" );
+	
+	if ( priv->rfds[cur_rx] != NULL ) {
+		ifec_scb_cmd ( netdev, virt_to_bus ( priv->rfds[cur_rx] ),
+			       RUStart );
+		ifec_scb_cmd_wait ( netdev );
 	}
+}
+
+/*
+ * Check if reprime of RU needed
+ *
+ * @v netdev		Network device
+ */
+static void ifec_check_ru_status ( struct net_device *netdev,
+				   unsigned short intr_status )
+{
+	struct ifec_private *priv = netdev->priv;
+
+	DBGP ( "ifec_check_ru_status\n" );
 
 	/*
-	 * The chip may have suspended reception for various reasons.
-	 * Check for that, and re-prime it should this be the case.
-	 */
-	switch ((inw(ioaddr + SCBStatus) >> 2) & 0xf) {
+	* The chip may have suspended reception for various reasons.
+	* Check for that, and re-prime it should this be the case.
+	*/
+	switch ( ( intr_status >> 2 ) & 0xf ) {
 		case 0:  /* Idle */
+		case 4:  /* Ready */
 			break;
 		case 1:  /* Suspended */
-		case 2:  /* No resources (RxFDs) */
+		case 2:  /* No resources (RFDs) */
 		case 9:  /* Suspended with no more RBDs */
 		case 10: /* No resources due to no RBDs */
 		case 12: /* Ready with no RBDs */
-			speedo_rx_soft_reset();
+			DBG ( "ifec_net_poll: RU reprimed.\n" );
+			ifec_reprime_ru ( netdev );
 			break;
 		default:
 			/* reserved values */
+			DBG ( "ifec_net_poll: RU state anomaly: %i\n",
+			      ( inw ( priv->ioaddr + SCBStatus ) >> 2 ) & 0xf );
 			break;
 	}
-	return 0;
 }
 
-/* function: eepro100_disable
- * resets the card. This is used to allow Etherboot or Linux
- * to probe the card again from a "virginal" state....
- * Arguments: none
- *
- * returns:   void.
- */
-static void eepro100_disable ( struct nic *nic __unused ) {
-/* from eepro100_reset */
-	outl(0, ioaddr + SCBPort);
-/* from eepro100_disable */
-	/* See if this PartialReset solves the problem with interfering with
-	   kernel operation after Etherboot hands over. - Ken 20001102 */
-	outl(2, ioaddr + SCBPort);
-
-	/* The following is from the Intel e100 driver.
-	 * This hopefully solves the problem with hanging hard DOS images. */
-
-	/* wait for the reset to take effect */
-	udelay(20);
-
-	/* Mask off our interrupt line -- it is unmasked after reset */
-	{
-		u16 intr_status;
-		/* Disable interrupts on our PCI board by setting the mask bit */
-		outw(INT_MASK, ioaddr + SCBCmd);
-		intr_status = inw(ioaddr + SCBStatus);
-		/* ack and clear intrs */
-		outw(intr_status, ioaddr + SCBStatus);
-		inw(ioaddr + SCBStatus);
+#define RFD_STATUS ( RFD_OK | RFDRxCol | RFDRxErr | RFDShort | \
+		     RFDDMAOverrun | RFDNoBufs | RFDCRCError )
+/*
+ * Looks for received packets in the rx ring, reports success or error to
+ * the core accordingly. Starts reallocation of rx ring.
+ *
+ * @v netdev		Network device
+ */
+static void ifec_rx_process ( struct net_device *netdev )
+{
+	struct ifec_private *priv   = netdev->priv;
+	int cur_rx = priv->cur_rx;
+	struct io_buffer *iob = priv->rx_iobs[cur_rx];
+	struct ifec_rfd *rfd = priv->rfds[cur_rx];
+	unsigned int rx_len;
+	s16 status;
+
+	DBGP ( "ifec_rx_process\n" );
+
+	/* Process any received packets */
+	while ( iob && rfd && ( status = rfd->status ) ) {
+		rx_len = rfd->count & RFDMaskCount;
+
+		DBG2 ( "Got a packet: Len = %d, cur_rx = %d.\n", rx_len,
+		       cur_rx );
+		DBGIO_HD ( (void*)rfd->packet, 0x30 );
+
+		if ( ( status & RFD_STATUS ) != RFD_OK ) {
+			DBG ( "Corrupted packet received. "
+			      "Status = %#08hx\n", status );
+			netdev_rx_err ( netdev, iob, -EINVAL );
+		} else {
+			/* Hand off the packet to the network subsystem */
+			iob_put ( iob, rx_len );
+			DBG2 ( "Received packet: %p, len: %d\n", iob, rx_len );
+			netdev_rx ( netdev, iob );
+		}
+
+		/* make sure we don't reuse this RFD */
+		priv->rx_iobs[cur_rx] = NULL;
+		priv->rfds[cur_rx] = NULL;
+
+		/* Next RFD */
+		priv->cur_rx = ( cur_rx + 1 ) % RFD_COUNT;
+		cur_rx = priv->cur_rx;
+		iob = priv->rx_iobs[cur_rx];
+		rfd = priv->rfds[cur_rx];
 	}
+
+	ifec_refill_rx_ring ( netdev );
 }
 
-/* exported function: eepro100_probe / eth_probe
- * initializes a card
+/*
+ * Allocates io_buffer, set pointers in ifec_private structure accordingly,
+ * reserves space for RFD header in io_buffer.
  *
- * side effects:
- *            leaves the ioaddress of the 82557 chip in the variable ioaddr.
- *            leaves the 82557 initialized, and ready to recieve packets.
+ * @v netdev		Network device
+ * @v cur		Descriptor number to work on
+ * @v cmd		Value to set cmd field in RFD to
+ * @v link		Pointer to ned RFD
+ * @ret rc		0 on success, negative on failure
  */
+static int ifec_get_rx_desc ( struct net_device *netdev, int cur, int cmd,
+			      int link )
+{
+	struct ifec_private *priv = netdev->priv;
+	struct ifec_rfd *rfd  = priv->rfds[cur];
 
-static int eepro100_probe ( struct nic *nic, struct pci_device *pci ) {
+	DBGP ( "ifec_get_rx_desc\n" );
 
-	unsigned short sum = 0;
-	int i;
-	int read_cmd, ee_size;
-	int options;
-	int rx_mode;
-	unsigned long ct;
+	priv->rx_iobs[cur] = alloc_iob ( sizeof ( *rfd ) );
+	if ( ! priv->rx_iobs[cur] ) {
+		DBG ( "alloc_iob failed. desc. nr: %d\n", cur );
+		priv->rfds[cur] = NULL;
+		return -ENOMEM;
+	}
 
-	/* we cache only the first few words of the EEPROM data
-	   be careful not to access beyond this array */
-	unsigned short eeprom[16];
+	/* Initialize new tail. */
+	priv->rfds[cur] = priv->rx_iobs[cur]->data;
+	ifec_rfd_init ( priv->rfds[cur], cmd, link );
+	iob_reserve ( priv->rx_iobs[cur], RFD_HEADER_LEN );
 
-	if (pci->ioaddr == 0)
-		return 0;
+	return 0;
+}
 
-	adjust_pci_device(pci);
+/*
+ * Allocate new descriptor entries and initialize them if needed
+ *
+ * @v netdev		Network device
+ */
+static void ifec_refill_rx_ring ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	int i, cur_rx;
+	unsigned short intr_status;
+
+	DBGP ( "ifec_refill_rx_ring\n" );
+
+	for ( i = 0; i < RFD_COUNT; i++ ) {
+		cur_rx = ( priv->cur_rx + i ) % RFD_COUNT;
+		/* only refill if empty */
+		if ( priv->rfds[cur_rx] != NULL ||
+		     priv->rx_iobs[cur_rx] != NULL )
+			continue;
+
+		DBG2 ( "refilling RFD %d\n", cur_rx );
+
+		if ( ifec_get_rx_desc ( netdev, cur_rx,
+		     CmdSuspend | CmdEndOfList, 0 ) == 0 ) {
+			if ( i > 0 ) {
+				int prev_rx = ( ( ( cur_rx + RFD_COUNT ) - 1 )
+						% RFD_COUNT );
+				struct ifec_rfd *rfd = priv->rfds[prev_rx];
+
+				rfd->command = 0;
+				rfd->link = virt_to_bus ( priv->rfds[cur_rx] );
+			}
+		}
+	}
 
-        nic->ioaddr = pci->ioaddr;
-        nic->irqno = pci->irq;
+	intr_status = inw ( priv->ioaddr + SCBStatus );
+	ifec_check_ru_status ( netdev, intr_status );
+}
 
-	ioaddr = nic->ioaddr;
+/*
+ * Initial allocation & initialization of the rx ring.
+ *
+ * @v netdev  		Device of rx ring.
+ * @ret rc    		Non-zero if error occured
+ */
+static int ifec_rx_setup ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	int i;
 
-	if ((do_eeprom_cmd(EE_READ_CMD << 24, 27) & 0xffe0000)
-		== 0xffe0000) {
-		ee_size = 0x100;
-		read_cmd = EE_READ_CMD << 24;
-	} else {
-		ee_size = 0x40;
-		read_cmd = EE_READ_CMD << 22;
-	}
+	DBGP ( "ifec_rx_setup\n" );
 
-	for (i = 0, sum = 0; i < ee_size; i++) {
-		unsigned short value = do_eeprom_cmd(read_cmd | (i << 16), 27);
-		if (i < (int)(sizeof(eeprom)/sizeof(eeprom[0])))
-			eeprom[i] = value;
-		sum += value;
-	}
+	priv->cur_rx = 0;
 
-	for (i=0;i<ETH_ALEN;i++) {
-		nic->node_addr[i] =  (eeprom[i/2] >> (8*(i&1))) & 0xff;
+	/* init values for ifec_refill_rx_ring() */
+	for ( i = 0; i < RFD_COUNT; i++ ) {
+		priv->rfds[i] = NULL;
+		priv->rx_iobs[i] = NULL;
 	}
+	ifec_refill_rx_ring ( netdev );
 
-	DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) );
-
-	if (sum != 0xBABA)
-		printf("eepro100: Invalid EEPROM checksum %#hX, "
-		       "check settings before activating this device!\n", sum);
-	outl(0, ioaddr + SCBPort);
-	udelay (10000);
-	whereami ("Got eeprom.");
-
-	/* Base = 0, disable all interrupts  */
-	outl(0, ioaddr + SCBPointer);
-	outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	whereami ("set rx base addr.");
-
-	outl(virt_to_bus(&lstats), ioaddr + SCBPointer);
-	outb(CU_STATSADDR, ioaddr + SCBCmd);
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	whereami ("set stats addr.");
-
-	/* INIT RX stuff. */
-	for (i = 0; i < RXFD_COUNT; i++) {
-		rxfds[i].status      = 0x0000;
-		rxfds[i].command     = 0x0000;
-		rxfds[i].rx_buf_addr = 0xFFFFFFFF;
-		rxfds[i].count       = 0;
-		rxfds[i].size        = 1528;
-		rxfds[i].link        = virt_to_bus(&rxfds[i+1]);
-	}
+	return 0;
+}
+
+/*
+ * Initiates a SCB command.
+ *
+ * @v netdev		Network device
+ * @v ptr   		General pointer value for command.
+ * @v cmd   		Command to issue.
+ * @ret rc  		Non-zero if command not issued.
+ */
+static int ifec_scb_cmd ( struct net_device *netdev, u32 ptr, u8 cmd )
+{
+	struct ifec_private *priv = netdev->priv;
+	unsigned long ioaddr = priv->ioaddr;
+	int rc;
 
-	rxfds[RXFD_COUNT-1].status  = 0x0000;
-	rxfds[RXFD_COUNT-1].command = 0xC000;
-	rxfds[RXFD_COUNT-1].link    = virt_to_bus(&rxfds[0]);
+	DBGP ( "ifec_scb_cmd\n" );
 
-	outl(virt_to_bus(&rxfds[0]), ioaddr + SCBPointer);
-	outb(RX_START, ioaddr + SCBCmd);
-	wait_for_cmd_done(ioaddr + SCBCmd);
+	rc = ifec_scb_cmd_wait ( netdev );	/* Wait until ready */
+	if ( !rc ) {
+		outl ( ptr, ioaddr + SCBPointer );
+		outb ( cmd, ioaddr + SCBCmd );		/* Issue command */
+	}
+	return rc;
+}
 
-	whereami ("started RX process.");
+/*
+ * Wait for command unit to accept a command.
+ *
+ * @v cmd_ioaddr	I/O address of command register.
+ * @ret rc      	Non-zero if command timed out.
+ */
+static int ifec_scb_cmd_wait ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	unsigned long cmd_ioaddr = priv->ioaddr + SCBCmd;
+	int rc, wait = CU_CMD_TIMEOUT;
 
-	/* INIT TX stuff. */
+	DBGP ( "ifec_scb_cmd_wait\n" );
 
-	/* Base = 0 */
-	outl(0, ioaddr + SCBPointer);
-	outb(CU_CMD_BASE, ioaddr + SCBCmd);
-	wait_for_cmd_done(ioaddr + SCBCmd);
+	for ( ; wait && ( rc = inb ( cmd_ioaddr ) ); wait-- )
+		udelay ( 1 );
 
-	whereami ("set TX base addr.");
+	if ( !wait )
+		DBG ( "ifec_scb_cmd_wait timeout!\n" );
+	return rc;
+}
 
-	txfd.command      = (CmdIASetup);
-	txfd.status       = 0x0000;
-	txfd.link         = virt_to_bus (&confcmd);
+/*
+ * Check status of transmitted packets & perform tx completions.
+ *
+ * @v netdev    	Network device.
+ */
+static void ifec_tx_process ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	struct ifec_tcb *tcb = priv->tcb_tail;
+	s16 status;
+
+	DBGP ( "ifec_tx_process\n" );
+
+	/* Check status of transmitted packets */
+	while ( ( status = tcb->status ) && tcb->iob ) {
+		if ( status & TCB_U ) {
+			/* report error to gPXE */
+			DBG ( "ifec_tx_process : tx error!\n " );
+			netdev_tx_complete_err ( netdev, tcb->iob, -EINVAL );
+		} else {
+			/* report successful transmit */
+			netdev_tx_complete ( netdev, tcb->iob );
+		}
+		DBG2 ( "tx completion\n" );
 
-	{
-		char *t = (char *)&txfd.tx_desc_addr;
+		tcb->iob = NULL;
+		tcb->status = 0;
 
-		for (i=0;i<ETH_ALEN;i++)
-			t[i] = nic->node_addr[i];
+		priv->tcb_tail = tcb->next;	/* Next TCB */
+		tcb = tcb->next;
 	}
+}
 
-#ifdef	DEBUG
-	printf ("Setup_eaddr:\n");
-	hd (&txfd, 0x20);
-#endif
-	/*      options = 0x40; */ /* 10mbps half duplex... */
-	options = 0x00;            /* Autosense */
-
-#ifdef PROMISC
-	rx_mode = 3;
-#elif ALLMULTI
-	rx_mode = 1;
-#else
-	rx_mode = 0;
-#endif
-
-	if (   ((eeprom[6]>>8) & 0x3f) == DP83840
-	       || ((eeprom[6]>>8) & 0x3f) == DP83840A) {
-		int mdi_reg23 = mdio_read(eeprom[6] & 0x1f, 23) | 0x0422;
-		if (congenb)
-			mdi_reg23 |= 0x0100;
-		printf("  DP83840 specific setup, setting register 23 to %hX.\n",
-		       mdi_reg23);
-		mdio_write(eeprom[6] & 0x1f, 23, mdi_reg23);
-	}
-	whereami ("Done DP8340 special setup.");
-	if (options != 0) {
-		mdio_write(eeprom[6] & 0x1f, 0,
-			   ((options & 0x20) ? 0x2000 : 0) |    /* 100mbps? */
-			   ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
-		whereami ("set mdio_register.");
+/*
+ * Allocates & initialize tx resources.
+ *
+ * @v netdev    	Network device.
+ * @ret rc      	Non-zero if error occurred.
+ */
+static int ifec_tx_setup ( struct net_device *netdev )
+{
+	struct ifec_private *priv = netdev->priv;
+	struct ifec_tcb *tcb;
+	int i;
+
+	DBGP ( "ifec_tx_setup\n" );
+
+	/* allocate tx ring */
+	priv->tcbs = malloc_dma ( TX_RING_BYTES, CB_ALIGN );
+	if ( !priv->tcbs ) {
+		DBG ( "TX-ring allocation failed\n" );
+		return -ENOMEM;
 	}
 
-	confcmd.command  = CmdSuspend | CmdConfigure;
-	confcmd.status   = 0x0000;
-	confcmd.link     = virt_to_bus (&txfd);
-	confcmd.data[1]  = (txfifo << 4) | rxfifo;
-	confcmd.data[4]  = rxdmacount;
-	confcmd.data[5]  = txdmacount + 0x80;
-	confcmd.data[15] = (rx_mode & 2) ? 0x49: 0x48;
-	confcmd.data[19] = (options & 0x10) ? 0xC0 : 0x80;
-	confcmd.data[21] = (rx_mode & 1) ? 0x0D: 0x05;
-
-	outl(virt_to_bus(&txfd), ioaddr + SCBPointer);
-	outb(CU_START, ioaddr + SCBCmd);
-	wait_for_cmd_done(ioaddr + SCBCmd);
-
-	whereami ("started TX thingy (config, iasetup).");
-
-	ct = currticks();
-	while (!txfd.status && ct + 10*1000 < currticks())
-		/* Wait */;
-
-	/* Read the status register once to disgard stale data */
-	mdio_read(eeprom[6] & 0x1f, 1);
-	/* Check to see if the network cable is plugged in.
-	 * This allows for faster failure if there is nothing
-	 * we can do.
-	 */
-	if (!(mdio_read(eeprom[6] & 0x1f, 1) & (1 << 2))) {
-		printf("Valid link not established\n");
-		eepro100_disable(nic);
-		return 0;
+	tcb = priv->tcb_tail = priv->tcbs;
+	priv->tx_curr = priv->tx_tail = 0;
+	priv->tx_cnt = 0;
+
+	for ( i = 0; i < TCB_COUNT; i++, tcb++ ) {
+		tcb->status    = 0;
+		tcb->count     = 0x01208000;
+		tcb->iob       = NULL;
+		tcb->tbda_addr = virt_to_bus ( &tcb->tbd_addr0 );
+		tcb->link      = virt_to_bus ( tcb + 1 );
+		tcb->next      = tcb + 1;
 	}
-	nic->nic_op	= &eepro100_operations;
-	return 1;
+	/* We point tcb_head at the last TCB, so the first ifec_net_transmit()
+	 * will use the first (head->next) TCB to transmit. */
+	priv->tcb_head = --tcb;
+	tcb->link = virt_to_bus ( priv->tcbs );
+	tcb->next = priv->tcbs;
+	
+	return 0;
 }
 
-/*********************************************************************/
-
-#ifdef	DEBUG
-
-/* Hexdump a number of bytes from memory... */
-void hd (void *where, int n)
+/*
+ * Wake up the Command Unit and issue a Resume/Start.
+ *
+ * @v netdev		Network device containing Command Unit
+ *
+ * The time between clearing the S bit and issuing Resume must be as short as
+ * possible to prevent a race condition. As noted in linux eepro100.c :
+ *   Note: Watch out for the potential race condition here: imagine
+ *	erasing the previous suspend
+ *		the chip processes the previous command
+ *		the chip processes the final command, and suspends
+ *	doing the CU_RESUME
+ *		the chip processes the next-yet-valid post-final-command.
+ *   So blindly sending a CU_RESUME is only safe if we do it immediately after
+ *   erasing the previous CmdSuspend, without the possibility of an intervening
+ *   delay.
+ */
+void ifec_tx_wake ( struct net_device *netdev )
 {
-	int i;
+	struct ifec_private *priv = netdev->priv;
+	unsigned long ioaddr = priv->ioaddr;
+	struct ifec_tcb *tcb = priv->tcb_head->next;
+
+	DBGP ( "ifec_tx_wake\n" );
+
+	/* For the special case of the first transmit, we issue a START. The
+	 * card won't RESUME after the configure command. */
+	if ( priv->configured ) {
+		priv->configured = 0;
+		ifec_scb_cmd ( netdev, virt_to_bus ( tcb ), CUStart );
+		ifec_scb_cmd_wait ( netdev );
+		return;
+	}
 
-	while (n > 0) {
-		printf ("%X ", where);
-		for (i=0;i < ( (n>16)?16:n);i++)
-			printf (" %hhX", ((char *)where)[i]);
-		printf ("\n");
-		n -= 16;
-		where += 16;
+	/* Resume if suspended. */
+	switch ( ( inw ( ioaddr + SCBStatus ) >> 6 ) & 0x3 ) {
+	case 0:  /* Idle - We should not reach this state. */
+		DBG2 ( "ifec_tx_wake: tx idle!\n" );
+		ifec_scb_cmd ( netdev, virt_to_bus ( tcb ), CUStart );
+		ifec_scb_cmd_wait ( netdev );
+		return;
+	case 1:  /* Suspended */
+		DBG2 ( "s" );
+		break;
+	default: /* Active */
+		DBG2 ( "a" );
 	}
+	ifec_scb_cmd_wait ( netdev );
+	outl ( 0, ioaddr + SCBPointer );
+	priv->tcb_head->command &= ~CmdSuspend;
+	/* Immediately issue Resume command */
+	outb ( CUResume, ioaddr + SCBCmd );
+	ifec_scb_cmd_wait ( netdev );
 }
-#endif
 
-static struct nic_operations eepro100_operations = {
-	.connect	= dummy_connect,
-	.poll		= eepro100_poll,
-	.transmit	= eepro100_transmit,
-	.irq		= eepro100_irq,
-
-};
+/*********************************************************************/
 
-static struct pci_device_id eepro100_nics[] = {
-PCI_ROM(0x8086, 0x1029, "id1029",        "Intel EtherExpressPro100 ID1029"),
-PCI_ROM(0x8086, 0x1030, "id1030",        "Intel EtherExpressPro100 ID1030"),
-PCI_ROM(0x8086, 0x1031, "82801cam",      "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
-PCI_ROM(0x8086, 0x1032, "eepro100-1032", "Intel PRO/100 VE Network Connection"),
-PCI_ROM(0x8086, 0x1033, "eepro100-1033", "Intel PRO/100 VM Network Connection"),
-PCI_ROM(0x8086, 0x1034, "eepro100-1034", "Intel PRO/100 VM Network Connection"),
-PCI_ROM(0x8086, 0x1035, "eepro100-1035", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
-PCI_ROM(0x8086, 0x1036, "eepro100-1036", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
-PCI_ROM(0x8086, 0x1037, "eepro100-1037", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
-PCI_ROM(0x8086, 0x1038, "id1038",        "Intel PRO/100 VM Network Connection"),
-PCI_ROM(0x8086, 0x1039, "82562et",       "Intel PRO100 VE 82562ET"),
-PCI_ROM(0x8086, 0x103a, "id103a",        "Intel Corporation 82559 InBusiness 10/100"),
-PCI_ROM(0x8086, 0x103b, "82562etb",      "Intel PRO100 VE 82562ETB"),
-PCI_ROM(0x8086, 0x103c, "eepro100-103c", "Intel PRO/100 VM Network Connection"),
-PCI_ROM(0x8086, 0x103d, "eepro100-103d", "Intel PRO/100 VE Network Connection"),
-PCI_ROM(0x8086, 0x103e, "eepro100-103e", "Intel PRO/100 VM Network Connection"),
-PCI_ROM(0x8086, 0x1051, "prove",       "Intel PRO/100 VE Network Connection"),
-PCI_ROM(0x8086, 0x1059, "82551qm",       "Intel PRO/100 M Mobile Connection"),
-PCI_ROM(0x8086, 0x1209, "82559er",       "Intel EtherExpressPro100 82559ER"),
-PCI_ROM(0x8086, 0x1227, "82865",         "Intel 82865 EtherExpress PRO/100A"),
-PCI_ROM(0x8086, 0x1228, "82556",         "Intel 82556 EtherExpress PRO/100 Smart"),
-PCI_ROM(0x8086, 0x1229, "eepro100",      "Intel EtherExpressPro100"),
-PCI_ROM(0x8086, 0x2449, "82562em",       "Intel EtherExpressPro100 82562EM"),
-PCI_ROM(0x8086, 0x2459, "82562-1",       "Intel 82562 based Fast Ethernet Connection"),
-PCI_ROM(0x8086, 0x245d, "82562-2",       "Intel 82562 based Fast Ethernet Connection"),
-PCI_ROM(0x8086, 0x1050, "82562ez",       "Intel 82562EZ Network Connection"),
-PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller"),
-PCI_ROM(0x8086, 0x1065, "82562-3",       "Intel 82562 based Fast Ethernet Connection"),
-PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server"),
-PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server"),
+static struct pci_device_id ifec_nics[] = {
+PCI_ROM(0x8086, 0x1029, "id1029",        "Intel EtherExpressPro100 ID1029", 0),
+PCI_ROM(0x8086, 0x1030, "id1030",        "Intel EtherExpressPro100 ID1030", 0),
+PCI_ROM(0x8086, 0x1031, "82801cam",      "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
+PCI_ROM(0x8086, 0x1032, "eepro100-1032", "Intel PRO/100 VE Network Connection", 0),
+PCI_ROM(0x8086, 0x1033, "eepro100-1033", "Intel PRO/100 VM Network Connection", 0),
+PCI_ROM(0x8086, 0x1034, "eepro100-1034", "Intel PRO/100 VM Network Connection", 0),
+PCI_ROM(0x8086, 0x1035, "eepro100-1035", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
+PCI_ROM(0x8086, 0x1036, "eepro100-1036", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
+PCI_ROM(0x8086, 0x1037, "eepro100-1037", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
+PCI_ROM(0x8086, 0x1038, "id1038",        "Intel PRO/100 VM Network Connection", 0),
+PCI_ROM(0x8086, 0x1039, "82562et",       "Intel PRO100 VE 82562ET", 0),
+PCI_ROM(0x8086, 0x103a, "id103a",        "Intel Corporation 82559 InBusiness 10/100", 0),
+PCI_ROM(0x8086, 0x103b, "82562etb",      "Intel PRO100 VE 82562ETB", 0),
+PCI_ROM(0x8086, 0x103c, "eepro100-103c", "Intel PRO/100 VM Network Connection", 0),
+PCI_ROM(0x8086, 0x103d, "eepro100-103d", "Intel PRO/100 VE Network Connection", 0),
+PCI_ROM(0x8086, 0x103e, "eepro100-103e", "Intel PRO/100 VM Network Connection", 0),
+PCI_ROM(0x8086, 0x1051, "prove",         "Intel PRO/100 VE Network Connection", 0),
+PCI_ROM(0x8086, 0x1059, "82551qm",       "Intel PRO/100 M Mobile Connection", 0),
+PCI_ROM(0x8086, 0x1209, "82559er",       "Intel EtherExpressPro100 82559ER", 0),
+PCI_ROM(0x8086, 0x1227, "82865",         "Intel 82865 EtherExpress PRO/100A", 0),
+PCI_ROM(0x8086, 0x1228, "82556",         "Intel 82556 EtherExpress PRO/100 Smart", 0),
+PCI_ROM(0x8086, 0x1229, "eepro100",      "Intel EtherExpressPro100", 0),
+PCI_ROM(0x8086, 0x2449, "82562em",       "Intel EtherExpressPro100 82562EM", 0),
+PCI_ROM(0x8086, 0x2459, "82562-1",       "Intel 82562 based Fast Ethernet Connection", 0),
+PCI_ROM(0x8086, 0x245d, "82562-2",       "Intel 82562 based Fast Ethernet Connection", 0),
+PCI_ROM(0x8086, 0x1050, "82562ez",       "Intel 82562EZ Network Connection", 0),
+PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller", 0),
+PCI_ROM(0x8086, 0x1065, "82562-3",       "Intel 82562 based Fast Ethernet Connection", 0),
+PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server", 0),
+PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server", 0),
 };
 
 /* Cards with device ids 0x1030 to 0x103F, 0x2449, 0x2459 or 0x245D might need
  * a workaround for hardware bug on 10 mbit half duplex (see linux driver eepro100.c)
  * 2003/03/17 gbaum */
 
-
-PCI_DRIVER ( eepro100_driver, eepro100_nics, PCI_NO_CLASS );
-
-DRIVER ( "EEPRO100", nic_driver, pci_driver, eepro100_driver,
-	 eepro100_probe, eepro100_disable );
+struct pci_driver ifec_driver __pci_driver = {
+	.ids      = ifec_nics,
+	.id_count = ( sizeof (ifec_nics) / sizeof (ifec_nics[0]) ),
+	.probe    = ifec_pci_probe,
+	.remove   = ifec_pci_remove
+};
 
 /*
  * Local variables:
diff --git a/gpxe/src/drivers/net/eepro100.h b/gpxe/src/drivers/net/eepro100.h
new file mode 100644
index 0000000..17a22df
--- /dev/null
+++ b/gpxe/src/drivers/net/eepro100.h
@@ -0,0 +1,206 @@
+
+#ifndef __EEPRO100_H_
+#define __EEPRO100_H_
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#define CONGENB         0	/* Enable congestion control in the DP83840. */
+#define TX_FIFO         8	/* Tx FIFO threshold in 4 byte units, 0-15 */
+#define RX_FIFO         8	/* Rx FIFO threshold, default 32 bytes. */
+#define TX_DMA_COUNT    0	/* Tx DMA burst length, 0-127, default 0. */
+#define RX_DMA_COUNT    0	/* Rx DMA length, 0 means no preemption. */
+#define CU_CMD_TIMEOUT  1000	/* CU command accept timeout in microseconds */
+#define LINK_CHECK_PERIOD 1000	/* # of poll() calls between link checks */
+
+#define RFD_PACKET_LEN  1518
+#define RFD_IOB_LEN     1536
+#define RFD_HEADER_LEN  16
+#define CB_ALIGN        2	/* Alignment of command blocks */
+
+#define RFD_COUNT       4
+#define TCB_COUNT       4
+#define RX_RING_BYTES   ( RFD_COUNT * sizeof ( struct ifec_rfd ) )
+#define TX_RING_BYTES   ( TCB_COUNT * sizeof ( struct ifec_tcb ) )
+
+/* some EEPROM addresses */
+#define EEPROM_ADDR_MAC_0		0
+#define EEPROM_ADDR_MDIO_REGISTER	6
+
+/* Control / Status Register byte offsets - SDM Table 11 */
+enum CSROffsets {
+	SCBStatus=0,             SCBCmd=2,              SCBPointer = 4,
+	CSRPort=8,               CSRFlash=12,           CSREeprom = 14,
+	CSRCtrlMDI=16,           CSREarlyRx=20
+};
+
+/* System Control Block Command Word - SDM Table 12 */
+enum SCBCmdBits {
+	/* SCB Interrupt Masks - SDM Table 14 */
+	SCBMaskCmdDone=0x8000,   SCBMaskRxDone=0x4000,  SCBMaskCmdIdle=0x2000,
+	SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
+	SCBTriggerIntr=0x0200,   SCBMaskAll=0x0100,
+	/* SCB Control Commands - SDM Table 14-16 */
+	CUStart=0x0010,          CUResume=0x0020,       CUStatsAddr=0x0040,
+	CUShowStats=0x0050,      CUCmdBase=0x0060,      CUDumpStats=0x0070,
+	RUStart=0x0001,          RUResume=0x0002,       RUAbort=0x0004,
+	RUAddrLoad=0x0006,       RUResumeNoResources=0x0007
+};
+
+enum SCBPortCmds {
+	PortReset=0, PortSelfTest=1, PortPartialReset=2, PortDump=3
+};
+
+/* Action Commands - SDM Table 14,37 */
+enum ActionCommands {
+	CmdNOp = 0,              CmdIASetup = 1,        CmdConfigure = 2,
+	CmdMulticastList = 3,    CmdTx = 4,             CmdTDR = 5,
+	CmdDump = 6,             CmdDiagnose = 7,
+	/* And some extra flags: */
+	CmdEndOfList = 0x8000,
+	CmdSuspend = 0x4000,     CmdIntr = 0x2000,      CmdTxFlex = 0x0008
+};
+
+enum TCBBits {
+	TCB_C=0x8000,            TCB_OK=0x2000,         TCB_U=0x1000
+};
+
+enum RFDBits {
+	/* Status Word Bits */
+	RFDRxCol=0x0001,         RFDIAMatch=0x0002,     RFDNoMatch=0x0004,
+	RFDReserved3=0x0008,     RFDRxErr=0x0010,       RFDEthType=0x0020,
+	RFDReserved6=0x0040,     RFDShort=0x0080,       RFDDMAOverrun=0x0100,
+	RFDNoBufs=0x0200,        RFDCRCAlign=0x0400,    RFDCRCError=0x0800,
+	RFDReserved12=0x1000,    RFD_OK=0x2000,         RFDComplete=0x8000,
+	/* Command Word Bits */
+	//RFD_SF=0x0008,           RFDSuspend=0x4000,     RFDEndOfList=0x8000,
+	/* Other */
+	RFDMaskCount=0x3FFF
+};
+
+enum phy_chips {
+	NonSuchPhy=0,            I82553AB,              I82553C,
+	I82503,                  DP83840,               S80C240,
+	S80C24,                  PhyUndefined,          DP83840A=10
+};
+
+/* Serial EEPROM section.
+   A "bit" grungy, but we work our way through bit-by-bit :->. */
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK    0x01    /* EEPROM shift clock. */
+#define EE_CS           0x02    /* EEPROM chip select. */
+#define EE_DATA_WRITE   0x04    /* EEPROM chip data in. */
+#define EE_DATA_READ    0x08    /* EEPROM chip data out. */
+#define EE_ENB          ( 0x4800 | EE_CS )
+
+/* Elements of the dump_statistics block. This block must be lword aligned. */
+struct ifec_stats {
+	u32
+	tx_good_frames,          tx_coll16_errs,        tx_late_colls,
+	tx_underruns,            tx_lost_carrier,       tx_deferred,
+	tx_one_colls,            tx_multi_colls,        tx_total_colls,
+	rx_good_frames,          rx_crc_errs,           rx_align_errs,
+	rx_resource_errs,        rx_overrun_errs,       rx_colls_errs,
+	rx_runt_errs,            done_marker;
+};
+
+struct ifec_tcb {                  /* A Transmit Command Block & TBD. Must be */
+	volatile s16 status;       /*             word (even address) aligned */
+	u16          command;
+	u32          link;         /* PHYSICAL next ifec_tcb, doesn't change */
+	u32          tbda_addr;    /* TBD Array, points to TBD below */
+	s32          count;        /* # of TBD, Tx start thresh., etc. */
+	/* The following constitutes a Transmit Buffer Descriptor (TBD).
+	 * TBDs must be aligned on an even address (word-aligned). */
+	u32          tbd_addr0;    /* PHYSICAL ptr to Tx data */
+	s32          tbd_size0;    /* Length of Tx data */
+	/* Driver-specific data; not part of TCB format. */
+	struct io_buffer *iob;     /* Exists from tx() to completion poll() */
+	struct ifec_tcb  *next;    /* VIRTUAL next ifec_tcb, doesn't change */
+};
+
+struct ifec_rfd {              /* A Receive Frame Descriptor. Must be aligned */
+	volatile s16 status;   /*           on a physical word (even address) */
+	s16          command;
+	u32          link;          /* PHYSICAL next ifec_rfd, doesn't change */
+	u32          rx_buf_addr;   /* Unused. Flex rx mode is not documented */
+	u16          count;         /*                  and may be impossible */
+	u16          size;
+	char         packet[RFD_PACKET_LEN];
+};
+
+struct ifec_ias {              /* Individual Address Setup command block. */
+	volatile s16 status;   /* Must be word (even address) aligned. */
+	u16          command;
+	u32          link;     /* PHYSICAL next command block to process */
+	u8           ia[6];
+};
+
+struct ifec_cfg {                   /* The configure command format. */
+	volatile s16 status;
+	u16          command;
+	u32          link;          /* PHYSICAL next command block to process */
+	u8           byte[22];      /* 22 configuration bytes */
+};
+
+struct ifec_private {
+	unsigned long         ioaddr;
+	struct ifec_stats     stats;
+	unsigned short        mdio_register;
+
+	struct ifec_tcb      *tcbs;
+	struct ifec_rfd      *rfds[RFD_COUNT];
+	struct ifec_tcb      *tcb_head, *tcb_tail;
+	struct io_buffer     *tx_iobs[TCB_COUNT];
+	struct io_buffer     *rx_iobs[RFD_COUNT];
+	int		      cur_rx;
+	int		      tx_curr;
+	int		      tx_tail;
+	int		      tx_cnt;
+	/*
+	 * The configured flag indicates if a Config command was last issued.
+	 * The following attempt to issue a command (in ifec_tx_wake) will
+	 * use a START rather than RESUME SCB command. It seems the card won't
+	 * RESUME after a configure command.
+	 */
+	int                   configured;
+	struct spi_bit_basher spi;
+	struct spi_device     eeprom;
+	
+};
+
+/**************************** Function prototypes ****************************/
+
+/* PCI device API prototypes */
+static int  ifec_pci_probe  ( struct pci_device*, const struct pci_device_id*);
+static void ifec_pci_remove ( struct pci_device *pci );
+
+/* Network device API prototypes */
+static void ifec_net_close    ( struct net_device* );
+static void ifec_net_irq      ( struct net_device*, int enable );
+static int  ifec_net_open     ( struct net_device* );
+static void ifec_net_poll     ( struct net_device* );
+static int  ifec_net_transmit ( struct net_device*, struct io_buffer *iobuf );
+
+/* Local function prototypes */
+static void ifec_init_eeprom     ( struct net_device * );
+static int  ifec_link_check      ( struct net_device * );
+static void ifec_link_update     ( struct net_device * );
+static int  ifec_mdio_read       ( struct net_device *, int phy, int location );
+static void ifec_mdio_setup      ( struct net_device *, int options );
+static int  ifec_mdio_write      ( struct net_device *, int phy, int loc, int val);
+static void ifec_reset           ( struct net_device * );
+static void ifec_free            ( struct net_device * );
+static void ifec_rfd_init        ( struct ifec_rfd *rfd, s16 command, u32 link );
+static void  ifec_rx_process     ( struct net_device * );
+static void ifec_reprime_ru      ( struct net_device * );
+static void ifec_check_ru_status ( struct net_device *, unsigned short );
+static int  ifec_get_rx_desc     ( struct net_device *, int ,int ,int );
+static void ifec_refill_rx_ring  ( struct net_device * );
+static int  ifec_rx_setup        ( struct net_device * );
+static int  ifec_scb_cmd         ( struct net_device *, u32 ptr, u8 cmd );
+static int  ifec_scb_cmd_wait    ( struct net_device * );
+static void ifec_tx_process      ( struct net_device * );
+static int  ifec_tx_setup        ( struct net_device * );
+static void ifec_tx_wake         ( struct net_device * );
+
+#endif
diff --git a/gpxe/src/drivers/net/epic100.c b/gpxe/src/drivers/net/epic100.c
index 1e36a68..aaa85f8 100644
--- a/gpxe/src/drivers/net/epic100.c
+++ b/gpxe/src/drivers/net/epic100.c
@@ -1,6 +1,8 @@
 
 /* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Etherboot */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* 05/06/2003	timlegge	Fixed relocation and implemented Multicast */
 #define LINUX_OUT_MACROS
 
@@ -519,8 +521,8 @@ static struct nic_operations epic100_operations = {
 };
 
 static struct pci_device_id epic100_nics[] = {
-PCI_ROM(0x10b8, 0x0005, "epic100",    "SMC EtherPowerII"),		/* SMC 83c170 EPIC/100 */
-PCI_ROM(0x10b8, 0x0006, "smc-83c175", "SMC EPIC/C 83c175"),
+PCI_ROM(0x10b8, 0x0005, "epic100",    "SMC EtherPowerII", 0),		/* SMC 83c170 EPIC/100 */
+PCI_ROM(0x10b8, 0x0006, "smc-83c175", "SMC EPIC/C 83c175", 0),
 };
 
 PCI_DRIVER ( epic100_driver, epic100_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/epic100.h b/gpxe/src/drivers/net/epic100.h
index 61bd1d9..f290b10 100644
--- a/gpxe/src/drivers/net/epic100.h
+++ b/gpxe/src/drivers/net/epic100.h
@@ -1,6 +1,8 @@
 #ifndef	_EPIC100_H_
 # define _EPIC100_H_
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef	PCI_VENDOR_SMC
 # define PCI_VENDOR_SMC		0x10B8
 #endif
diff --git a/gpxe/src/drivers/net/etherfabric.c b/gpxe/src/drivers/net/etherfabric.c
index 704ce98..c4296b9 100644
--- a/gpxe/src/drivers/net/etherfabric.c
+++ b/gpxe/src/drivers/net/etherfabric.c
@@ -15,6 +15,9 @@
  *
  **************************************************************************
  */
+
+FILE_LICENCE ( GPL_ANY );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -29,6 +32,7 @@
 #include <gpxe/iobuf.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/timer.h>
+#include <mii.h>
 #include "etherfabric.h"
 #include "etherfabric_nic.h"
 
@@ -81,39 +85,23 @@ static void falcon_mdio_write (struct efab_nic *efab, int device,
 static int falcon_mdio_read ( struct efab_nic *efab, int device, int location );
 
 /* GMII registers */
-#define MII_BMSR		0x01	/* Basic mode status register  */
-#define MII_ADVERTISE		0x04	/* Advertisement control register */
-#define MII_LPA			0x05	/* Link partner ability register*/
-#define GMII_GTCR		0x09	/* 1000BASE-T control register */
-#define GMII_GTSR		0x0a	/* 1000BASE-T status register */
 #define GMII_PSSR		0x11	/* PHY-specific status register */
 
-/* Basic mode status register. */
-#define BMSR_LSTATUS		0x0004	/* Link status                 */
-
-/* Link partner ability register. */
-#define LPA_10HALF              0x0020  /* Can do 10mbps half-duplex   */
-#define LPA_10FULL              0x0040  /* Can do 10mbps full-duplex   */
-#define LPA_100HALF             0x0080  /* Can do 100mbps half-duplex  */
-#define LPA_100FULL             0x0100  /* Can do 100mbps full-duplex  */
-#define LPA_100BASE4            0x0200  /* Can do 100mbps 4k packets   */
-#define LPA_PAUSE		0x0400	/* Bit 10 - MAC pause */
-
 /* Pseudo extensions to the link partner ability register */
-#define LPA_1000FULL		0x00020000
-#define LPA_1000HALF		0x00010000
-#define LPA_10000FULL		0x00040000
-#define LPA_10000HALF		0x00080000
+#define LPA_EF_1000FULL		0x00020000
+#define LPA_EF_1000HALF		0x00010000
+#define LPA_EF_10000FULL		0x00040000
+#define LPA_EF_10000HALF		0x00080000
 
 #define LPA_100			(LPA_100FULL | LPA_100HALF | LPA_100BASE4)
-#define LPA_1000		( LPA_1000FULL | LPA_1000HALF )
-#define LPA_10000               ( LPA_10000FULL | LPA_10000HALF )
-#define LPA_DUPLEX		( LPA_10FULL | LPA_100FULL | LPA_1000FULL | \
-				  LPA_10000FULL )
+#define LPA_EF_1000		( LPA_EF_1000FULL | LPA_EF_1000HALF )
+#define LPA_EF_10000               ( LPA_EF_10000FULL | LPA_EF_10000HALF )
+#define LPA_EF_DUPLEX		( LPA_10FULL | LPA_100FULL | LPA_EF_1000FULL | \
+				  LPA_EF_10000FULL )
 
 /* Mask of bits not associated with speed or duplexity. */
 #define LPA_OTHER		~( LPA_10FULL | LPA_10HALF | LPA_100FULL | \
-				   LPA_100HALF | LPA_1000FULL | LPA_1000HALF )
+				   LPA_100HALF | LPA_EF_1000FULL | LPA_EF_1000HALF )
 
 /* PHY-specific status register */
 #define PSSR_LSTATUS		0x0400	/* Bit 10 - link status */
@@ -128,9 +116,9 @@ gmii_autoneg_advertised ( struct efab_nic *efab )
 	unsigned int mii_advertise;
 	unsigned int gmii_advertise;
 
-	/* Extended bits are in bits 8 and 9 of GMII_GTCR */
+	/* Extended bits are in bits 8 and 9 of MII_CTRL1000 */
 	mii_advertise = falcon_mdio_read ( efab, 0, MII_ADVERTISE );
-	gmii_advertise = ( ( falcon_mdio_read ( efab, 0, GMII_GTCR ) >> 8 )
+	gmii_advertise = ( ( falcon_mdio_read ( efab, 0, MII_CTRL1000 ) >> 8 )
 			   & 0x03 );
 	return ( ( gmii_advertise << 16 ) | mii_advertise );
 }
@@ -145,9 +133,9 @@ gmii_autoneg_lpa ( struct efab_nic *efab )
 	unsigned int mii_lpa;
 	unsigned int gmii_lpa;
 
-	/* Extended bits are in bits 10 and 11 of GMII_GTSR */
+	/* Extended bits are in bits 10 and 11 of MII_STAT1000 */
 	mii_lpa = falcon_mdio_read ( efab, 0, MII_LPA );
-	gmii_lpa = ( falcon_mdio_read ( efab, 0, GMII_GTSR ) >> 10 ) & 0x03;
+	gmii_lpa = ( falcon_mdio_read ( efab, 0, MII_STAT1000 ) >> 10 ) & 0x03;
 	return ( ( gmii_lpa << 16 ) | mii_lpa );
 }
 
@@ -163,10 +151,10 @@ gmii_nway_result ( unsigned int negotiated )
 	/* Mask out the speed and duplexity bits */
 	other_bits = negotiated & LPA_OTHER;
 
-	if ( negotiated & LPA_1000FULL )
-		return ( other_bits | LPA_1000FULL );
-	else if ( negotiated & LPA_1000HALF )
-		return ( other_bits | LPA_1000HALF );
+	if ( negotiated & LPA_EF_1000FULL )
+		return ( other_bits | LPA_EF_1000FULL );
+	else if ( negotiated & LPA_EF_1000HALF )
+		return ( other_bits | LPA_EF_1000HALF );
 	else if ( negotiated & LPA_100FULL )
 		return ( other_bits | LPA_100FULL );
 	else if ( negotiated & LPA_100BASE4 )
@@ -1737,9 +1725,9 @@ falcon_reconfigure_mac_wrapper ( struct efab_nic *efab )
 	efab_oword_t reg;
 	int link_speed;
 
-	if ( efab->link_options & LPA_10000 ) {
+	if ( efab->link_options & LPA_EF_10000 ) {
 		link_speed = 0x3;
-	} else if ( efab->link_options & LPA_1000 ) {
+	} else if ( efab->link_options & LPA_EF_1000 ) {
 		link_speed = 0x2;
 	} else if ( efab->link_options & LPA_100 ) {
 		link_speed = 0x1;
@@ -1948,8 +1936,8 @@ mentormac_init ( struct efab_nic *efab )
 	efab_dword_t reg;
 
 	/* Configuration register 1 */
-	pause = ( efab->link_options & LPA_PAUSE ) ? 1 : 0;
-	if ( ! ( efab->link_options & LPA_DUPLEX ) ) {
+	pause = ( efab->link_options & LPA_PAUSE_CAP ) ? 1 : 0;
+	if ( ! ( efab->link_options & LPA_EF_DUPLEX ) ) {
 		/* Half-duplex operation requires TX flow control */
 		pause = 1;
 	}
@@ -1962,8 +1950,8 @@ mentormac_init ( struct efab_nic *efab )
 	udelay ( 10 );
 
 	/* Configuration register 2 */
-	if_mode = ( efab->link_options & LPA_1000 ) ? 2 : 1;
-	full_duplex = ( efab->link_options & LPA_DUPLEX ) ? 1 : 0;
+	if_mode = ( efab->link_options & LPA_EF_1000 ) ? 2 : 1;
+	full_duplex = ( efab->link_options & LPA_EF_DUPLEX ) ? 1 : 0;
 	EFAB_POPULATE_DWORD_4 ( reg,
 				GM_IF_MODE, if_mode,
 				GM_PAD_CRC_EN, 1,
@@ -2015,8 +2003,8 @@ mentormac_init ( struct efab_nic *efab )
 	udelay ( 10 );
 	
 	/* FIFO configuration register 5 */
-	bytemode = ( efab->link_options & LPA_1000 ) ? 1 : 0;
-	half_duplex = ( efab->link_options & LPA_DUPLEX ) ? 0 : 1;
+	bytemode = ( efab->link_options & LPA_EF_1000 ) ? 1 : 0;
+	half_duplex = ( efab->link_options & LPA_EF_DUPLEX ) ? 0 : 1;
 	falcon_gmac_readl ( efab, &reg, GMF_CFG5_REG_MAC );
 	EFAB_SET_DWORD_FIELD ( reg, GMF_CFGBYTMODE, bytemode );
 	EFAB_SET_DWORD_FIELD ( reg, GMF_CFGHDPLX, half_duplex );
@@ -2412,7 +2400,7 @@ static int
 falcon_xaui_phy_init ( struct efab_nic *efab )
 {
 	/* CX4 is always 10000FD only */
-	efab->link_options = LPA_10000FULL;
+	efab->link_options = LPA_EF_10000FULL;
 
 	/* There is no PHY! */
 	return 0;
@@ -2477,7 +2465,7 @@ falcon_xfp_phy_init ( struct efab_nic *efab )
 	int rc;
 
 	/* Optical link is always 10000FD only */
-	efab->link_options = LPA_10000FULL;
+	efab->link_options = LPA_EF_10000FULL;
 
 	/* Reset the PHY */
 	rc = mdio_clause45_reset_mmd ( efab, MDIO_MMD_PHYXS );
@@ -2564,7 +2552,7 @@ falcon_txc_phy_init ( struct efab_nic *efab )
 	int rc;
 
 	/* CX4 is always 10000FD only */
-	efab->link_options = LPA_10000FULL;
+	efab->link_options = LPA_EF_10000FULL;
 
 	/* reset the phy */
 	rc = mdio_clause45_reset_mmd ( efab, MDIO_MMD_PMAPMD );
@@ -2682,7 +2670,7 @@ falcon_tenxpress_phy_init ( struct efab_nic *efab )
 	int rc, reg;
 
 	/* 10XPRESS is always 10000FD (at the moment) */
-	efab->link_options = LPA_10000FULL;
+	efab->link_options = LPA_EF_10000FULL;
 
 	/* Wait for the blocks to come out of reset */
 	rc = mdio_clause45_wait_reset_mmds ( efab );
@@ -2762,7 +2750,7 @@ falcon_pm8358_phy_init ( struct efab_nic *efab )
 	int rc, reg, i;
 
 	/* This is a XAUI retimer part */
-	efab->link_options = LPA_10000FULL;
+	efab->link_options = LPA_EF_10000FULL;
 
 	rc = mdio_clause45_reset_mmd ( efab, MDIO_MMDREG_DEVS0_DTEXS );
 	if ( rc )
@@ -4036,10 +4024,10 @@ efab_init_mac ( struct efab_nic *efab )
 		}
 
 		EFAB_LOG ( "\n%dMbps %s-duplex\n",
-			   ( efab->link_options & LPA_10000 ? 10000 :
-			     ( efab->link_options & LPA_1000 ? 1000 :
+			   ( efab->link_options & LPA_EF_10000 ? 10000 :
+			     ( efab->link_options & LPA_EF_1000 ? 1000 :
 			       ( efab->link_options & LPA_100 ? 100 : 10 ) ) ),
-			   ( efab->link_options & LPA_DUPLEX ?
+			   ( efab->link_options & LPA_EF_DUPLEX ?
 			     "full" : "half" ) );
 
 		/* TODO: Move link state handling to the poll() routine */
@@ -4193,7 +4181,7 @@ efab_probe ( struct pci_device *pci,
 	if ( rc )
 		goto fail3;
 
-	memcpy ( netdev->ll_addr, efab->mac_addr, ETH_ALEN );
+	memcpy ( netdev->hw_addr, efab->mac_addr, ETH_ALEN );
 
 	netdev_link_up ( netdev );
 	rc = register_netdev ( netdev );
@@ -4228,8 +4216,8 @@ fail1:
 
 
 static struct pci_device_id efab_nics[] = {
-	PCI_ROM(0x1924, 0x0703, "falcon", "EtherFabric Falcon"),
-	PCI_ROM(0x1924, 0x0710, "falconb0", "EtherFabric FalconB0"),
+	PCI_ROM(0x1924, 0x0703, "falcon", "EtherFabric Falcon", 0),
+	PCI_ROM(0x1924, 0x0710, "falconb0", "EtherFabric FalconB0", 0),
 };
 
 struct pci_driver etherfabric_driver __pci_driver = {
diff --git a/gpxe/src/drivers/net/etherfabric.h b/gpxe/src/drivers/net/etherfabric.h
index 950f8de..9657eb7 100644
--- a/gpxe/src/drivers/net/etherfabric.h
+++ b/gpxe/src/drivers/net/etherfabric.h
@@ -17,6 +17,8 @@
  **************************************************************************
  */
 
+FILE_LICENCE ( GPL_ANY );
+
 #ifndef EFAB_BITFIELD_H
 #define EFAB_BITFIELD_H
 
diff --git a/gpxe/src/drivers/net/etherfabric_nic.h b/gpxe/src/drivers/net/etherfabric_nic.h
index 4be50fb..fe94d80 100644
--- a/gpxe/src/drivers/net/etherfabric_nic.h
+++ b/gpxe/src/drivers/net/etherfabric_nic.h
@@ -15,6 +15,9 @@
  *
  **************************************************************************
  */
+
+FILE_LICENCE ( GPL_ANY );
+
 #ifndef EFAB_NIC_H
 #define  EFAB_NIC_H
 #include <gpxe/bitbash.h>
diff --git a/gpxe/src/drivers/net/forcedeth.c b/gpxe/src/drivers/net/forcedeth.c
index a30f137..3d44d86 100644
--- a/gpxe/src/drivers/net/forcedeth.c
+++ b/gpxe/src/drivers/net/forcedeth.c
@@ -43,6 +43,8 @@
 *    Indent Options: indent -kr -i8
 ***************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* to get some global routines like printf */
 #include "etherboot.h"
 /* to get the interface to the body of the program */
@@ -371,13 +373,9 @@ enum {
 #define PHY_1000	0x2
 #define PHY_HALF	0x100
 
-/* FIXME: MII defines that should be added to <linux/mii.h> */
-#define MII_1000BT_CR	0x09
-#define MII_1000BT_SR	0x0a
-#define ADVERTISE_1000FULL	0x0200
-#define ADVERTISE_1000HALF	0x0100
-#define LPA_1000FULL	0x0800
-#define LPA_1000HALF	0x0400
+
+/* Bit to know if MAC addr is stored in correct order */
+#define MAC_ADDR_CORRECT	0x01
 
 /* Big endian: should work, but is untested */
 struct ring_desc {
@@ -460,26 +458,6 @@ static int reg_delay(int offset, u32 mask,
 }
 
 #define MII_READ	(-1)
-#define MII_PHYSID1         0x02	/* PHYS ID 1                   */
-#define MII_PHYSID2         0x03	/* PHYS ID 2                   */
-#define MII_BMCR            0x00	/* Basic mode control register */
-#define MII_BMSR            0x01	/* Basic mode status register  */
-#define MII_ADVERTISE       0x04	/* Advertisement control reg   */
-#define MII_LPA             0x05	/* Link partner ability reg    */
-
-#define BMSR_ANEGCOMPLETE       0x0020	/* Auto-negotiation complete   */
-
-/* Link partner ability register. */
-#define LPA_SLCT                0x001f	/* Same as advertise selector  */
-#define LPA_10HALF              0x0020	/* Can do 10mbps half-duplex   */
-#define LPA_10FULL              0x0040	/* Can do 10mbps full-duplex   */
-#define LPA_100HALF             0x0080	/* Can do 100mbps half-duplex  */
-#define LPA_100FULL             0x0100	/* Can do 100mbps full-duplex  */
-#define LPA_100BASE4            0x0200	/* Can do 100mbps 4k packets   */
-#define LPA_RESV                0x1c00	/* Unused...                   */
-#define LPA_RFAULT              0x2000	/* Link partner faulted        */
-#define LPA_LPACK               0x4000	/* Link partner acked us       */
-#define LPA_NPAGE               0x8000	/* Next page bit               */
 
 /* mii_rw: read/write a register on the PHY.
  *
@@ -581,7 +559,7 @@ static int phy_init(struct nic *nic)
 	if (mii_status & PHY_GIGABIT) {
 		np->gigabit = PHY_GIGABIT;
 		mii_control_1000 =
-		    mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ);
+		    mii_rw(nic, np->phyaddr, MII_CTRL1000, MII_READ);
 		mii_control_1000 &= ~ADVERTISE_1000HALF;
 		if (phyinterface & PHY_RGMII)
 			mii_control_1000 |= ADVERTISE_1000FULL;
@@ -589,7 +567,7 @@ static int phy_init(struct nic *nic)
 			mii_control_1000 &= ~ADVERTISE_1000FULL;
 
 		if (mii_rw
-		    (nic, np->phyaddr, MII_1000BT_CR, mii_control_1000)) {
+		    (nic, np->phyaddr, MII_CTRL1000, mii_control_1000)) {
 			printf("phy init failed.\n");
 			return PHY_ERROR;
 		}
@@ -783,9 +761,9 @@ static int update_linkspeed(struct nic *nic)
 	retval = 1;
 	if (np->gigabit == PHY_GIGABIT) {
 		control_1000 =
-		    mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ);
+		    mii_rw(nic, np->phyaddr, MII_CTRL1000, MII_READ);
 		status_1000 =
-		    mii_rw(nic, np->phyaddr, MII_1000BT_SR, MII_READ);
+		    mii_rw(nic, np->phyaddr, MII_STAT1000, MII_READ);
 
 		if ((control_1000 & ADVERTISE_1000FULL) &&
 		    (status_1000 & LPA_1000FULL)) {
@@ -1241,6 +1219,9 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
 	int sz;
 	u8 *base;
 	int i;
+	struct pci_device_id *ids = pci->driver->ids;
+	int id_count = pci->driver->id_count;
+	unsigned int flags = 0;
 
 	if (pci->ioaddr == 0)
 		return 0;
@@ -1280,12 +1261,31 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
 	np->orig_mac[0] = readl(base + NvRegMacAddrA);
 	np->orig_mac[1] = readl(base + NvRegMacAddrB);
 
-	nic->node_addr[0] = (np->orig_mac[1] >> 8) & 0xff;
-	nic->node_addr[1] = (np->orig_mac[1] >> 0) & 0xff;
-	nic->node_addr[2] = (np->orig_mac[0] >> 24) & 0xff;
-	nic->node_addr[3] = (np->orig_mac[0] >> 16) & 0xff;
-	nic->node_addr[4] = (np->orig_mac[0] >> 8) & 0xff;
-	nic->node_addr[5] = (np->orig_mac[0] >> 0) & 0xff;
+	/* lookup the flags from pci_device_id */
+	for(i = 0; i < id_count; i++) {
+		if(pci->vendor == ids[i].vendor &&
+		   pci->device == ids[i].device) {
+			flags = ids[i].driver_data;
+			break;
+		   }
+	}
+
+	/* read MAC address */
+	if(flags & MAC_ADDR_CORRECT) {
+		nic->node_addr[0] = (np->orig_mac[0] >>  0) & 0xff;
+		nic->node_addr[1] = (np->orig_mac[0] >>  8) & 0xff;
+		nic->node_addr[2] = (np->orig_mac[0] >> 16) & 0xff;
+		nic->node_addr[3] = (np->orig_mac[0] >> 24) & 0xff;
+		nic->node_addr[4] = (np->orig_mac[1] >>  0) & 0xff;
+		nic->node_addr[5] = (np->orig_mac[1] >>  8) & 0xff;
+	} else {
+		nic->node_addr[0] = (np->orig_mac[1] >>  8) & 0xff;
+		nic->node_addr[1] = (np->orig_mac[1] >>  0) & 0xff;
+		nic->node_addr[2] = (np->orig_mac[0] >> 24) & 0xff;
+		nic->node_addr[3] = (np->orig_mac[0] >> 16) & 0xff;
+		nic->node_addr[4] = (np->orig_mac[0] >>  8) & 0xff;
+		nic->node_addr[5] = (np->orig_mac[0] >>  0) & 0xff;
+	}
 #ifdef LINUX
 	if (!is_valid_ether_addr(dev->dev_addr)) {
 		/*
@@ -1321,6 +1321,7 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
 
   	switch (pci->device) {
   	case 0x01C3:		// nforce
+	case 0x054C:
  		// DEV_IRQMASK_1|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
  		np->irqmask = NVREG_IRQMASK_WANTED_2 | NVREG_IRQ_TIMER;
 		//              np->need_linktimer = 1;
@@ -1412,18 +1413,21 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
 }
 
 static struct pci_device_id forcedeth_nics[] = {
-PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller"),
-PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller"),
-PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller"),
-PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller"),
-PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller"),
-PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller"),
-PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller"),
-PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller"),
-PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller"),
-PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller"),
-PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller"),
-PCI_ROM(0x10de, 0x0373, "nforce15", "nForce NVENET_15 Ethernet Controller")
+PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0373, "nforce15", "nForce NVENET_15 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0269, "nforce16", "nForce NVENET_16 Ethernet Controller", 0),
+PCI_ROM(0x10de, 0x0760, "nforce17", "nForce NVENET_17 Ethernet Controller", MAC_ADDR_CORRECT),
+PCI_ROM(0x10de, 0x054c, "nforce67", "nForce NVENET_67 Ethernet Controller", MAC_ADDR_CORRECT),
 };
 
 PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/hfa384x.h b/gpxe/src/drivers/net/hfa384x.h
index afb5069..2e3ccf5 100644
--- a/gpxe/src/drivers/net/hfa384x.h
+++ b/gpxe/src/drivers/net/hfa384x.h
@@ -53,6 +53,8 @@
 * --------------------------------------------------------------------
 */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 #ifndef _HFA384x_H
 #define _HFA384x_H
 
diff --git a/gpxe/src/drivers/net/ipoib.c b/gpxe/src/drivers/net/ipoib.c
index 8ad2c29..4b3741e 100644
--- a/gpxe/src/drivers/net/ipoib.c
+++ b/gpxe/src/drivers/net/ipoib.c
@@ -16,16 +16,21 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <byteswap.h>
 #include <errno.h>
+#include <gpxe/errortab.h>
 #include <gpxe/if_arp.h>
 #include <gpxe/iobuf.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/infiniband.h>
+#include <gpxe/ib_pathrec.h>
+#include <gpxe/ib_mcast.h>
 #include <gpxe/ipoib.h>
 
 /** @file
@@ -33,33 +38,14 @@
  * IP over Infiniband
  */
 
-/** Number of IPoIB data send work queue entries */
-#define IPOIB_DATA_NUM_SEND_WQES 2
-
-/** Number of IPoIB data receive work queue entries */
-#define IPOIB_DATA_NUM_RECV_WQES 4
-
-/** Number of IPoIB data completion entries */
-#define IPOIB_DATA_NUM_CQES 8
-
-/** Number of IPoIB metadata send work queue entries */
-#define IPOIB_META_NUM_SEND_WQES 2
-
-/** Number of IPoIB metadata receive work queue entries */
-#define IPOIB_META_NUM_RECV_WQES 2
+/** Number of IPoIB send work queue entries */
+#define IPOIB_NUM_SEND_WQES 2
 
-/** Number of IPoIB metadata completion entries */
-#define IPOIB_META_NUM_CQES 8
+/** Number of IPoIB receive work queue entries */
+#define IPOIB_NUM_RECV_WQES 4
 
-/** An IPoIB queue set */
-struct ipoib_queue_set {
-	/** Completion queue */
-	struct ib_completion_queue *cq;
-	/** Queue pair */
-	struct ib_queue_pair *qp;
-	/** Receive work queue maximum fill level */
-	unsigned int recv_max_fill;
-};
+/** Number of IPoIB completion entries */
+#define IPOIB_NUM_CQES 8
 
 /** An IPoIB device */
 struct ipoib_device {
@@ -67,46 +53,37 @@ struct ipoib_device {
 	struct net_device *netdev;
 	/** Underlying Infiniband device */
 	struct ib_device *ibdev;
-	/** Data queue set */
-	struct ipoib_queue_set data;
-	/** Data queue set */
-	struct ipoib_queue_set meta;
-	/** Broadcast GID */
-	struct ib_gid broadcast_gid;
-	/** Broadcast LID */
-	unsigned int broadcast_lid;
-	/** Data queue key */
-	unsigned long data_qkey;
-	/** Attached to multicast group
+	/** Completion queue */
+	struct ib_completion_queue *cq;
+	/** Queue pair */
+	struct ib_queue_pair *qp;
+	/** Broadcast MAC */
+	struct ipoib_mac broadcast;
+	/** Joined to IPv4 broadcast multicast group
 	 *
-	 * This flag indicates whether or not we have attached our
-	 * data queue pair to the broadcast multicast GID.
+	 * This flag indicates whether or not we have initiated the
+	 * join to the IPv4 broadcast multicast group.
 	 */
-	int broadcast_attached;
+	int broadcast_joined;
+	/** IPv4 broadcast multicast group membership */
+	struct ib_mc_membership broadcast_membership;
 };
 
-/** TID half used to identify get path record replies */
-#define IPOIB_TID_GET_PATH_REC 0x11111111UL
-
-/** TID half used to identify multicast member record replies */
-#define IPOIB_TID_MC_MEMBER_REC 0x22222222UL
-
-/** IPoIB metadata TID */
-static uint32_t ipoib_meta_tid = 0;
-
-/** Broadcast QPN used in IPoIB MAC addresses
- *
- * This is a guaranteed invalid real QPN
- */
-#define IPOIB_BROADCAST_QPN 0xffffffffUL
-
 /** Broadcast IPoIB address */
 static struct ipoib_mac ipoib_broadcast = {
-	.qpn = ntohl ( IPOIB_BROADCAST_QPN ),
+	.flags__qpn = htonl ( IB_QPN_BROADCAST ),
 	.gid.u.bytes = 	{ 0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00,
 			  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff },
 };
 
+/** Link status for "broadcast join in progress" */
+#define EINPROGRESS_JOINING ( EINPROGRESS | EUNIQ_01 )
+
+/** Human-readable message for the link status */
+struct errortab ipoib_errors[] __errortab = {
+	{ EINPROGRESS_JOINING, "Joining" },
+};
+
 /****************************************************************************
  *
  * IPoIB peer cache
@@ -117,20 +94,18 @@ static struct ipoib_mac ipoib_broadcast = {
 /**
  * IPoIB peer address
  *
- * This serves a similar role to the ARP cache for Ethernet.  (ARP
- * *is* used on IPoIB; we have two caches to maintain.)
+ * The IPoIB link-layer header is only four bytes long and so does not
+ * have sufficient room to store IPoIB MAC address(es).  We therefore
+ * maintain a cache of MAC addresses identified by a single-byte key,
+ * and abuse the spare two bytes within the link-layer header to
+ * communicate these MAC addresses between the link-layer code and the
+ * netdevice driver.
  */
 struct ipoib_peer {
 	/** Key */
 	uint8_t key;
 	/** MAC address */
 	struct ipoib_mac mac;
-	/** LID */
-	unsigned int lid;
-	/** Service level */
-	unsigned int sl;
-	/** Rate */
-	unsigned int rate;
 };
 
 /** Number of IPoIB peer cache entries
@@ -169,44 +144,21 @@ static struct ipoib_peer * ipoib_lookup_peer_by_key ( unsigned int key ) {
 }
 
 /**
- * Look up cached peer by GID
- *
- * @v gid		Peer GID
- * @ret peer		Peer cache entry, or NULL
- */
-static struct ipoib_peer *
-ipoib_lookup_peer_by_gid ( const struct ib_gid *gid ) {
-	struct ipoib_peer *peer;
-	unsigned int i;
-
-	for ( i = 0 ; i < IPOIB_NUM_CACHED_PEERS ; i++ ) {
-		peer = &ipoib_peer_cache[i];
-		if ( memcmp ( &peer->mac.gid, gid,
-			      sizeof ( peer->mac.gid) ) == 0 ) {
-			return peer;
-		}
-	}
-
-	return NULL;
-}
-
-/**
  * Store GID and QPN in peer cache
  *
- * @v gid		Peer GID
- * @v qpn		Peer QPN
+ * @v mac		Peer MAC address
  * @ret peer		Peer cache entry
  */
-static struct ipoib_peer *
-ipoib_cache_peer ( const struct ib_gid *gid, unsigned long qpn ) {
+static struct ipoib_peer * ipoib_cache_peer ( const struct ipoib_mac *mac ) {
 	struct ipoib_peer *peer;
 	unsigned int key;
+	unsigned int i;
 
 	/* Look for existing cache entry */
-	peer = ipoib_lookup_peer_by_gid ( gid );
-	if ( peer ) {
-		assert ( peer->mac.qpn = ntohl ( qpn ) );
-		return peer;
+	for ( i = 0 ; i < IPOIB_NUM_CACHED_PEERS ; i++ ) {
+		peer = &ipoib_peer_cache[i];
+		if ( memcmp ( &peer->mac, mac, sizeof ( peer->mac ) ) == 0 )
+			return peer;
 	}
 
 	/* No entry found: create a new one */
@@ -217,12 +169,9 @@ ipoib_cache_peer ( const struct ib_gid *gid, unsigned long qpn ) {
 
 	memset ( peer, 0, sizeof ( *peer ) );
 	peer->key = key;
-	peer->mac.qpn = htonl ( qpn );
-	memcpy ( &peer->mac.gid, gid, sizeof ( peer->mac.gid ) );
-	DBG ( "IPoIB peer %x has GID %08x:%08x:%08x:%08x and QPN %lx\n",
-	      peer->key, htonl ( gid->u.dwords[0] ),
-	      htonl ( gid->u.dwords[1] ), htonl ( gid->u.dwords[2] ),
-	      htonl ( gid->u.dwords[3] ), qpn );
+	memcpy ( &peer->mac, mac, sizeof ( peer->mac ) );
+	DBG ( "IPoIB peer %x has MAC %s\n",
+	      peer->key, ipoib_ntoa ( &peer->mac ) );
 	return peer;
 }
 
@@ -236,13 +185,15 @@ ipoib_cache_peer ( const struct ib_gid *gid, unsigned long qpn ) {
 /**
  * Add IPoIB link-layer header
  *
+ * @v netdev		Network device
  * @v iobuf		I/O buffer
  * @v ll_dest		Link-layer destination address
  * @v ll_source		Source link-layer address
  * @v net_proto		Network-layer protocol, in network-byte order
  * @ret rc		Return status code
  */
-static int ipoib_push ( struct io_buffer *iobuf, const void *ll_dest,
+static int ipoib_push ( struct net_device *netdev __unused,
+			struct io_buffer *iobuf, const void *ll_dest,
 			const void *ll_source __unused, uint16_t net_proto ) {
 	struct ipoib_hdr *ipoib_hdr =
 		iob_push ( iobuf, sizeof ( *ipoib_hdr ) );
@@ -252,8 +203,8 @@ static int ipoib_push ( struct io_buffer *iobuf, const void *ll_dest,
 	struct ipoib_peer *src;
 
 	/* Add link-layer addresses to cache */
-	dest = ipoib_cache_peer ( &dest_mac->gid, ntohl ( dest_mac->qpn ) );
-	src = ipoib_cache_peer ( &src_mac->gid, ntohl ( src_mac->qpn ) );
+	dest = ipoib_cache_peer ( dest_mac );
+	src = ipoib_cache_peer ( src_mac );
 
 	/* Build IPoIB header */
 	ipoib_hdr->proto = net_proto;
@@ -266,14 +217,17 @@ static int ipoib_push ( struct io_buffer *iobuf, const void *ll_dest,
 /**
  * Remove IPoIB link-layer header
  *
+ * @v netdev		Network device
  * @v iobuf		I/O buffer
  * @ret ll_dest		Link-layer destination address
  * @ret ll_source	Source link-layer address
  * @ret net_proto	Network-layer protocol, in network-byte order
  * @ret rc		Return status code
  */
-static int ipoib_pull ( struct io_buffer *iobuf, const void **ll_dest,
+static int ipoib_pull ( struct net_device *netdev,
+			struct io_buffer *iobuf, const void **ll_dest,
 			const void **ll_source, uint16_t *net_proto ) {
+	struct ipoib_device *ipoib = netdev->priv;
 	struct ipoib_hdr *ipoib_hdr = iobuf->data;
 	struct ipoib_peer *dest;
 	struct ipoib_peer *source;
@@ -296,15 +250,29 @@ static int ipoib_pull ( struct io_buffer *iobuf, const void **ll_dest,
 	ipoib_hdr->u.reserved = 0;
 
 	/* Fill in required fields */
-	*ll_dest = ( dest ? &dest->mac : &ipoib_broadcast );
-	*ll_source = ( source ? &source->mac : &ipoib_broadcast );
+	*ll_dest = ( dest ? &dest->mac : &ipoib->broadcast );
+	*ll_source = ( source ? &source->mac : &ipoib->broadcast );
 	*net_proto = ipoib_hdr->proto;
 
 	return 0;
 }
 
 /**
- * Transcribe IPoIB address
+ * Initialise IPoIB link-layer address
+ *
+ * @v hw_addr		Hardware address
+ * @v ll_addr		Link-layer address
+ */
+static void ipoib_init_addr ( const void *hw_addr, void *ll_addr ) {
+	const struct ib_gid_half *guid = hw_addr;
+	struct ipoib_mac *mac = ll_addr;
+
+	memset ( mac, 0, sizeof ( *mac ) );
+	memcpy ( &mac->gid.u.half[1], guid, sizeof ( mac->gid.u.half[1] ) );
+}
+
+/**
+ * Transcribe IPoIB link-layer address
  *
  * @v ll_addr	Link-layer address
  * @ret string	Link-layer address in human-readable format
@@ -314,7 +282,7 @@ const char * ipoib_ntoa ( const void *ll_addr ) {
 	const struct ipoib_mac *mac = ll_addr;
 
 	snprintf ( buf, sizeof ( buf ), "%08x:%08x:%08x:%08x:%08x",
-		   htonl ( mac->qpn ), htonl ( mac->gid.u.dwords[0] ),
+		   htonl ( mac->flags__qpn ), htonl ( mac->gid.u.dwords[0] ),
 		   htonl ( mac->gid.u.dwords[1] ),
 		   htonl ( mac->gid.u.dwords[2] ),
 		   htonl ( mac->gid.u.dwords[3] ) );
@@ -336,216 +304,101 @@ static int ipoib_mc_hash ( unsigned int af __unused,
 	return -ENOTSUP;
 }
 
+/**
+ * Generate Mellanox Ethernet-compatible compressed link-layer address
+ *
+ * @v ll_addr		Link-layer address
+ * @v eth_addr		Ethernet-compatible address to fill in
+ */
+static int ipoib_mlx_eth_addr ( const struct ib_gid_half *guid,
+				uint8_t *eth_addr ) {
+	eth_addr[0] = ( ( guid->u.bytes[3] == 2 ) ? 0x00 : 0x02 );
+	eth_addr[1] = guid->u.bytes[1];
+	eth_addr[2] = guid->u.bytes[2];
+	eth_addr[3] = guid->u.bytes[5];
+	eth_addr[4] = guid->u.bytes[6];
+	eth_addr[5] = guid->u.bytes[7];
+	return 0;
+}
+
+/** An IPoIB Ethernet-compatible compressed link-layer address generator */
+struct ipoib_eth_addr_handler {
+	/** GUID byte 1 */
+	uint8_t byte1;
+	/** GUID byte 2 */
+	uint8_t byte2;
+	/** Handler */
+	int ( * eth_addr ) ( const struct ib_gid_half *guid,
+			     uint8_t *eth_addr );
+};
+
+/** IPoIB Ethernet-compatible compressed link-layer address generators */
+static struct ipoib_eth_addr_handler ipoib_eth_addr_handlers[] = {
+	{ 0x02, 0xc9, ipoib_mlx_eth_addr },
+};
+
+/**
+ * Generate Ethernet-compatible compressed link-layer address
+ *
+ * @v ll_addr		Link-layer address
+ * @v eth_addr		Ethernet-compatible address to fill in
+ */
+static int ipoib_eth_addr ( const void *ll_addr, void *eth_addr ) {
+	const struct ipoib_mac *ipoib_addr = ll_addr;
+	const struct ib_gid_half *guid = &ipoib_addr->gid.u.half[1];
+	struct ipoib_eth_addr_handler *handler;
+	unsigned int i;
+
+	for ( i = 0 ; i < ( sizeof ( ipoib_eth_addr_handlers ) /
+			    sizeof ( ipoib_eth_addr_handlers[0] ) ) ; i++ ) {
+		handler = &ipoib_eth_addr_handlers[i];
+		if ( ( handler->byte1 == guid->u.bytes[1] ) &&
+		     ( handler->byte2 == guid->u.bytes[2] ) ) {
+			return handler->eth_addr ( guid, eth_addr );
+		}
+	}
+	return -ENOTSUP;
+}
+
 /** IPoIB protocol */
 struct ll_protocol ipoib_protocol __ll_protocol = {
 	.name		= "IPoIB",
 	.ll_proto	= htons ( ARPHRD_INFINIBAND ),
+	.hw_addr_len	= sizeof ( struct ib_gid_half ),
 	.ll_addr_len	= IPOIB_ALEN,
 	.ll_header_len	= IPOIB_HLEN,
-	.ll_broadcast	= ( uint8_t * ) &ipoib_broadcast,
 	.push		= ipoib_push,
 	.pull		= ipoib_pull,
+	.init_addr	= ipoib_init_addr,
 	.ntoa		= ipoib_ntoa,
 	.mc_hash	= ipoib_mc_hash,
+	.eth_addr	= ipoib_eth_addr,
 };
 
-/****************************************************************************
- *
- * IPoIB network device
- *
- ****************************************************************************
- */
-
-/**
- * Destroy queue set
- *
- * @v ipoib		IPoIB device
- * @v qset		Queue set
- */
-static void ipoib_destroy_qset ( struct ipoib_device *ipoib,
-				 struct ipoib_queue_set *qset ) {
-	struct ib_device *ibdev = ipoib->ibdev;
-
-	if ( qset->qp )
-		ib_destroy_qp ( ibdev, qset->qp );
-	if ( qset->cq )
-		ib_destroy_cq ( ibdev, qset->cq );
-	memset ( qset, 0, sizeof ( *qset ) );
-}
-
 /**
- * Create queue set
+ * Allocate IPoIB device
  *
- * @v ipoib		IPoIB device
- * @v qset		Queue set
- * @v num_cqes		Number of completion queue entries
- * @v cq_op		Completion queue operations
- * @v num_send_wqes	Number of send work queue entries
- * @v num_recv_wqes	Number of receive work queue entries
- * @v qkey		Queue key
- * @ret rc		Return status code
+ * @v priv_size		Size of driver private data
+ * @ret netdev		Network device, or NULL
  */
-static int ipoib_create_qset ( struct ipoib_device *ipoib,
-			       struct ipoib_queue_set *qset,
-			       unsigned int num_cqes,
-			       struct ib_completion_queue_operations *cq_op,
-			       unsigned int num_send_wqes,
-			       unsigned int num_recv_wqes,
-			       unsigned long qkey ) {
-	struct ib_device *ibdev = ipoib->ibdev;
-	int rc;
-
-	/* Sanity check */
-	assert ( qset->cq == NULL );
-	assert ( qset->qp == NULL );
-
-	/* Store queue parameters */
-	qset->recv_max_fill = num_recv_wqes;
-
-	/* Allocate completion queue */
-	qset->cq = ib_create_cq ( ibdev, num_cqes, cq_op );
-	if ( ! qset->cq ) {
-		DBGC ( ipoib, "IPoIB %p could not allocate completion queue\n",
-		       ipoib );
-		rc = -ENOMEM;
-		goto err;
-	}
+struct net_device * alloc_ipoibdev ( size_t priv_size ) {
+	struct net_device *netdev;
 
-	/* Allocate queue pair */
-	qset->qp = ib_create_qp ( ibdev, num_send_wqes, qset->cq,
-				  num_recv_wqes, qset->cq, qkey );
-	if ( ! qset->qp ) {
-		DBGC ( ipoib, "IPoIB %p could not allocate queue pair\n",
-		       ipoib );
-		rc = -ENOMEM;
-		goto err;
+	netdev = alloc_netdev ( priv_size );
+	if ( netdev ) {
+		netdev->ll_protocol = &ipoib_protocol;
+		netdev->ll_broadcast = ( uint8_t * ) &ipoib_broadcast;
+		netdev->max_pkt_len = IB_MAX_PAYLOAD_SIZE;
 	}
-	ib_qp_set_ownerdata ( qset->qp, ipoib->netdev );
-
-	return 0;
-
- err:
-	ipoib_destroy_qset ( ipoib, qset );
-	return rc;
+	return netdev;
 }
 
-/**
- * Transmit path record request
+/****************************************************************************
  *
- * @v ipoib		IPoIB device
- * @v gid		Destination GID
- * @ret rc		Return status code
- */
-static int ipoib_get_path_record ( struct ipoib_device *ipoib,
-				   struct ib_gid *gid ) {
-	struct ib_device *ibdev = ipoib->ibdev;
-	struct io_buffer *iobuf;
-	struct ib_mad_sa *sa;
-	struct ib_address_vector av;
-	int rc;
-
-	/* Allocate I/O buffer */
-	iobuf = alloc_iob ( sizeof ( *sa ) );
-	if ( ! iobuf )
-		return -ENOMEM;
-	iob_put ( iobuf, sizeof ( *sa ) );
-	sa = iobuf->data;
-	memset ( sa, 0, sizeof ( *sa ) );
-
-	/* Construct path record request */
-	sa->mad_hdr.base_version = IB_MGMT_BASE_VERSION;
-	sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
-	sa->mad_hdr.class_version = 2;
-	sa->mad_hdr.method = IB_MGMT_METHOD_GET;
-	sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_PATH_REC );
-	sa->mad_hdr.tid[0] = IPOIB_TID_GET_PATH_REC;
-	sa->mad_hdr.tid[1] = ipoib_meta_tid++;
-	sa->sa_hdr.comp_mask[1] =
-		htonl ( IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID );
-	memcpy ( &sa->sa_data.path_record.dgid, gid,
-		 sizeof ( sa->sa_data.path_record.dgid ) );
-	memcpy ( &sa->sa_data.path_record.sgid, &ibdev->gid,
-		 sizeof ( sa->sa_data.path_record.sgid ) );
-
-	/* Construct address vector */
-	memset ( &av, 0, sizeof ( av ) );
-	av.lid = ibdev->sm_lid;
-	av.sl = ibdev->sm_sl;
-	av.qpn = IB_SA_QPN;
-	av.qkey = IB_GLOBAL_QKEY;
-
-	/* Post send request */
-	if ( ( rc = ib_post_send ( ibdev, ipoib->meta.qp, &av,
-				   iobuf ) ) != 0 ) {
-		DBGC ( ipoib, "IPoIB %p could not send get path record: %s\n",
-		       ipoib, strerror ( rc ) );
-		free_iob ( iobuf );
-		return rc;
-	}
-
-	return 0;
-}
-
-/**
- * Transmit multicast group membership request
+ * IPoIB network device
  *
- * @v ipoib		IPoIB device
- * @v gid		Multicast GID
- * @v join		Join (rather than leave) group
- * @ret rc		Return status code
+ ****************************************************************************
  */
-static int ipoib_mc_member_record ( struct ipoib_device *ipoib,
-				    struct ib_gid *gid, int join ) {
-	struct ib_device *ibdev = ipoib->ibdev;
-	struct io_buffer *iobuf;
-	struct ib_mad_sa *sa;
-	struct ib_address_vector av;
-	int rc;
-
-	/* Allocate I/O buffer */
-	iobuf = alloc_iob ( sizeof ( *sa ) );
-	if ( ! iobuf )
-		return -ENOMEM;
-	iob_put ( iobuf, sizeof ( *sa ) );
-	sa = iobuf->data;
-	memset ( sa, 0, sizeof ( *sa ) );
-
-	/* Construct path record request */
-	sa->mad_hdr.base_version = IB_MGMT_BASE_VERSION;
-	sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
-	sa->mad_hdr.class_version = 2;
-	sa->mad_hdr.method =
-		( join ? IB_MGMT_METHOD_SET : IB_MGMT_METHOD_DELETE );
-	sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_MC_MEMBER_REC );
-	sa->mad_hdr.tid[0] = IPOIB_TID_MC_MEMBER_REC;
-	sa->mad_hdr.tid[1] = ipoib_meta_tid++;
-	sa->sa_hdr.comp_mask[1] =
-		htonl ( IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID |
-			IB_SA_MCMEMBER_REC_JOIN_STATE );
-	sa->sa_data.mc_member_record.scope__join_state = 1;
-	memcpy ( &sa->sa_data.mc_member_record.mgid, gid,
-		 sizeof ( sa->sa_data.mc_member_record.mgid ) );
-	memcpy ( &sa->sa_data.mc_member_record.port_gid, &ibdev->gid,
-		 sizeof ( sa->sa_data.mc_member_record.port_gid ) );
-
-	/* Construct address vector */
-	memset ( &av, 0, sizeof ( av ) );
-	av.lid = ibdev->sm_lid;
-	av.sl = ibdev->sm_sl;
-	av.qpn = IB_SA_QPN;
-	av.qkey = IB_GLOBAL_QKEY;
-
-	/* Post send request */
-	if ( ( rc = ib_post_send ( ibdev, ipoib->meta.qp, &av,
-				   iobuf ) ) != 0 ) {
-		DBGC ( ipoib, "IPoIB %p could not send get path record: %s\n",
-		       ipoib, strerror ( rc ) );
-		free_iob ( iobuf );
-		return rc;
-	}
-
-	return 0;
-}
 
 /**
  * Transmit packet via IPoIB network device
@@ -561,7 +414,7 @@ static int ipoib_transmit ( struct net_device *netdev,
 	struct ipoib_hdr *ipoib_hdr;
 	struct ipoib_peer *dest;
 	struct ib_address_vector av;
-	struct ib_gid *gid;
+	int rc;
 
 	/* Sanity check */
 	if ( iob_len ( iobuf ) < sizeof ( *ipoib_hdr ) ) {
@@ -584,49 +437,35 @@ static int ipoib_transmit ( struct net_device *netdev,
 
 	/* Construct address vector */
 	memset ( &av, 0, sizeof ( av ) );
-	av.qkey = ipoib->data_qkey;
+	av.qpn = ( ntohl ( dest->mac.flags__qpn ) & IB_QPN_MASK );
 	av.gid_present = 1;
-	if ( dest->mac.qpn == htonl ( IPOIB_BROADCAST_QPN ) ) {
-		/* Broadcast */
-		av.qpn = IB_BROADCAST_QPN;
-		av.lid = ipoib->broadcast_lid;
-		gid = &ipoib->broadcast_gid;
-	} else {
-		/* Unicast */
-		if ( ! dest->lid ) {
-			/* No LID yet - get path record to fetch LID */
-			ipoib_get_path_record ( ipoib, &dest->mac.gid );
-			return -ENOENT;
-		}
-		av.qpn = ntohl ( dest->mac.qpn );
-		av.lid = dest->lid;
-		av.rate = dest->rate;
-		av.sl = dest->sl;
-		gid = &dest->mac.gid;
+	memcpy ( &av.gid, &dest->mac.gid, sizeof ( av.gid ) );
+	if ( ( rc = ib_resolve_path ( ibdev, &av ) ) != 0 ) {
+		/* Path not resolved yet */
+		return rc;
 	}
-	memcpy ( &av.gid, gid, sizeof ( av.gid ) );
 
-	return ib_post_send ( ibdev, ipoib->data.qp, &av, iobuf );
+	return ib_post_send ( ibdev, ipoib->qp, &av, iobuf );
 }
 
 /**
- * Handle IPoIB data send completion
+ * Handle IPoIB send completion
  *
  * @v ibdev		Infiniband device
  * @v qp		Queue pair
  * @v iobuf		I/O buffer
  * @v rc		Completion status code
  */
-static void ipoib_data_complete_send ( struct ib_device *ibdev __unused,
-				       struct ib_queue_pair *qp,
-				       struct io_buffer *iobuf, int rc ) {
-	struct net_device *netdev = ib_qp_get_ownerdata ( qp );
+static void ipoib_complete_send ( struct ib_device *ibdev __unused,
+				  struct ib_queue_pair *qp,
+				  struct io_buffer *iobuf, int rc ) {
+	struct ipoib_device *ipoib = ib_qp_get_ownerdata ( qp );
 
-	netdev_tx_complete_err ( netdev, iobuf, rc );
+	netdev_tx_complete_err ( ipoib->netdev, iobuf, rc );
 }
 
 /**
- * Handle IPoIB data receive completion
+ * Handle IPoIB receive completion
  *
  * @v ibdev		Infiniband device
  * @v qp		Queue pair
@@ -634,13 +473,14 @@ static void ipoib_data_complete_send ( struct ib_device *ibdev __unused,
  * @v iobuf		I/O buffer
  * @v rc		Completion status code
  */
-static void ipoib_data_complete_recv ( struct ib_device *ibdev __unused,
-				       struct ib_queue_pair *qp,
-				       struct ib_address_vector *av,
-				       struct io_buffer *iobuf, int rc ) {
-	struct net_device *netdev = ib_qp_get_ownerdata ( qp );
-	struct ipoib_device *ipoib = netdev->priv;
+static void ipoib_complete_recv ( struct ib_device *ibdev __unused,
+				  struct ib_queue_pair *qp,
+				  struct ib_address_vector *av,
+				  struct io_buffer *iobuf, int rc ) {
+	struct ipoib_device *ipoib = ib_qp_get_ownerdata ( qp );
+	struct net_device *netdev = ipoib->netdev;
 	struct ipoib_hdr *ipoib_hdr;
+	struct ipoib_mac ll_src;
 	struct ipoib_peer *src;
 
 	if ( rc != 0 ) {
@@ -650,7 +490,7 @@ static void ipoib_data_complete_recv ( struct ib_device *ibdev __unused,
 
 	/* Sanity check */
 	if ( iob_len ( iobuf ) < sizeof ( struct ipoib_hdr ) ) {
-		DBGC ( ipoib, "IPoIB %p received data packet too short to "
+		DBGC ( ipoib, "IPoIB %p received packet too short to "
 		       "contain IPoIB header\n", ipoib );
 		DBGC_HD ( ipoib, iobuf->data, iob_len ( iobuf ) );
 		netdev_rx_err ( netdev, iobuf, -EIO );
@@ -660,7 +500,9 @@ static void ipoib_data_complete_recv ( struct ib_device *ibdev __unused,
 
 	/* Parse source address */
 	if ( av->gid_present ) {
-		src = ipoib_cache_peer ( &av->gid, av->qpn );
+		ll_src.flags__qpn = htonl ( av->qpn );
+		memcpy ( &ll_src.gid, &av->gid, sizeof ( ll_src.gid ) );
+		src = ipoib_cache_peer ( &ll_src );
 		ipoib_hdr->u.peer.src = src->key;
 	}
 
@@ -668,174 +510,13 @@ static void ipoib_data_complete_recv ( struct ib_device *ibdev __unused,
 	netdev_rx ( netdev, iobuf );
 }
 
-/** IPoIB data completion operations */
-static struct ib_completion_queue_operations ipoib_data_cq_op = {
-	.complete_send = ipoib_data_complete_send,
-	.complete_recv = ipoib_data_complete_recv,
-};
-
-/**
- * Handle IPoIB metadata send completion
- *
- * @v ibdev		Infiniband device
- * @v qp		Queue pair
- * @v iobuf		I/O buffer
- * @v rc		Completion status code
- */
-static void ipoib_meta_complete_send ( struct ib_device *ibdev __unused,
-				       struct ib_queue_pair *qp,
-				       struct io_buffer *iobuf, int rc ) {
-	struct net_device *netdev = ib_qp_get_ownerdata ( qp );
-	struct ipoib_device *ipoib = netdev->priv;
-
-	if ( rc != 0 ) {
-		DBGC ( ipoib, "IPoIB %p metadata TX completion error: %s\n",
-		       ipoib, strerror ( rc ) );
-	}
-	free_iob ( iobuf );
-}
-
-/**
- * Handle received IPoIB path record
- *
- * @v ipoib		IPoIB device
- * @v path_record	Path record
- */
-static void ipoib_recv_path_record ( struct ipoib_device *ipoib,
-				     struct ib_path_record *path_record ) {
-	struct ipoib_peer *peer;
-
-	/* Locate peer cache entry */
-	peer = ipoib_lookup_peer_by_gid ( &path_record->dgid );
-	if ( ! peer ) {
-		DBGC ( ipoib, "IPoIB %p received unsolicited path record\n",
-		       ipoib );
-		return;
-	}
-
-	/* Update path cache entry */
-	peer->lid = ntohs ( path_record->dlid );
-	peer->sl = ( path_record->reserved__sl & 0x0f );
-	peer->rate = ( path_record->rate_selector__rate & 0x3f );
-
-	DBG ( "IPoIB peer %x has dlid %x sl %x rate %x\n",
-	      peer->key, peer->lid, peer->sl, peer->rate );
-}
-
-/**
- * Handle received IPoIB multicast membership record
- *
- * @v ipoib		IPoIB device
- * @v mc_member_record	Multicast membership record
- */
-static void ipoib_recv_mc_member_record ( struct ipoib_device *ipoib,
-			       struct ib_mc_member_record *mc_member_record ) {
-	int joined;
-	int rc;
-
-	/* Record parameters */
-	joined = ( mc_member_record->scope__join_state & 0x0f );
-	ipoib->data_qkey = ntohl ( mc_member_record->qkey );
-	ipoib->broadcast_lid = ntohs ( mc_member_record->mlid );
-	DBGC ( ipoib, "IPoIB %p %s broadcast group: qkey %lx mlid %x\n",
-	       ipoib, ( joined ? "joined" : "left" ), ipoib->data_qkey,
-	       ipoib->broadcast_lid );
-
-	/* Update data queue pair qkey */
-	if ( ( rc = ib_modify_qp ( ipoib->ibdev, ipoib->data.qp,
-				   IB_MODIFY_QKEY, ipoib->data_qkey ) ) != 0 ){
-		DBGC ( ipoib, "IPoIB %p could not update data qkey: %s\n",
-		       ipoib, strerror ( rc ) );
-		return;
-	}
-}
-
-/**
- * Handle IPoIB metadata receive completion
- *
- * @v ibdev		Infiniband device
- * @v qp		Queue pair
- * @v av		Address vector, or NULL
- * @v iobuf		I/O buffer
- * @v rc		Completion status code
- */
-static void
-ipoib_meta_complete_recv ( struct ib_device *ibdev __unused,
-			   struct ib_queue_pair *qp,
-			   struct ib_address_vector *av __unused,
-			   struct io_buffer *iobuf, int rc ) {
-	struct net_device *netdev = ib_qp_get_ownerdata ( qp );
-	struct ipoib_device *ipoib = netdev->priv;
-	struct ib_mad_sa *sa;
-
-	if ( rc != 0 ) {
-		DBGC ( ipoib, "IPoIB %p metadata RX completion error: %s\n",
-		       ipoib, strerror ( rc ) );
-		goto done;
-	}
-
-	if ( iob_len ( iobuf ) < sizeof ( *sa ) ) {
-		DBGC ( ipoib, "IPoIB %p received metadata packet too short "
-		       "to contain reply\n", ipoib );
-		DBGC_HD ( ipoib, iobuf->data, iob_len ( iobuf ) );
-		goto done;
-	}
-	sa = iobuf->data;
-
-	if ( sa->mad_hdr.status != 0 ) {
-		DBGC ( ipoib, "IPoIB %p metadata RX err status %04x\n",
-		       ipoib, ntohs ( sa->mad_hdr.status ) );
-		goto done;
-	}
-
-	switch ( sa->mad_hdr.tid[0] ) {
-	case IPOIB_TID_GET_PATH_REC:
-		ipoib_recv_path_record ( ipoib, &sa->sa_data.path_record );
-		break;
-	case IPOIB_TID_MC_MEMBER_REC:
-		ipoib_recv_mc_member_record ( ipoib,
-					      &sa->sa_data.mc_member_record );
-		break;
-	default:
-		DBGC ( ipoib, "IPoIB %p unwanted response:\n",
-		       ipoib );
-		DBGC_HD ( ipoib, sa, sizeof ( *sa ) );
-		break;
-	}
-
- done:
-	free_iob ( iobuf );
-}
-
-/** IPoIB metadata completion operations */
-static struct ib_completion_queue_operations ipoib_meta_cq_op = {
-	.complete_send = ipoib_meta_complete_send,
-	.complete_recv = ipoib_meta_complete_recv,
+/** IPoIB completion operations */
+static struct ib_completion_queue_operations ipoib_cq_op = {
+	.complete_send = ipoib_complete_send,
+	.complete_recv = ipoib_complete_recv,
 };
 
 /**
- * Refill IPoIB receive ring
- *
- * @v ipoib		IPoIB device
- */
-static void ipoib_refill_recv ( struct ipoib_device *ipoib,
-				struct ipoib_queue_set *qset ) {
-	struct ib_device *ibdev = ipoib->ibdev;
-	struct io_buffer *iobuf;
-	int rc;
-
-	while ( qset->qp->recv.fill < qset->recv_max_fill ) {
-		iobuf = alloc_iob ( IPOIB_PKT_LEN );
-		if ( ! iobuf )
-			break;
-		if ( ( rc = ib_post_recv ( ibdev, qset->qp, iobuf ) ) != 0 ) {
-			free_iob ( iobuf );
-			break;
-		}
-	}
-}
-
-/**
  * Poll IPoIB network device
  *
  * @v netdev		Network device
@@ -844,10 +525,7 @@ static void ipoib_poll ( struct net_device *netdev ) {
 	struct ipoib_device *ipoib = netdev->priv;
 	struct ib_device *ibdev = ipoib->ibdev;
 
-	ib_poll_cq ( ibdev, ipoib->meta.cq );
-	ib_poll_cq ( ibdev, ipoib->data.cq );
-	ipoib_refill_recv ( ipoib, &ipoib->meta );
-	ipoib_refill_recv ( ipoib, &ipoib->data );
+	ib_poll_eq ( ibdev );
 }
 
 /**
@@ -862,6 +540,26 @@ static void ipoib_irq ( struct net_device *netdev __unused,
 }
 
 /**
+ * Handle IPv4 broadcast multicast group join completion
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v membership	Multicast group membership
+ * @v rc		Status code
+ * @v mad		Response MAD (or NULL on error)
+ */
+void ipoib_join_complete ( struct ib_device *ibdev __unused,
+			   struct ib_queue_pair *qp __unused,
+			   struct ib_mc_membership *membership, int rc,
+			   union ib_mad *mad __unused ) {
+	struct ipoib_device *ipoib = container_of ( membership,
+				   struct ipoib_device, broadcast_membership );
+
+	/* Record join status as link status */
+	netdev_link_err ( ipoib->netdev, rc );
+}
+
+/**
  * Join IPv4 broadcast multicast group
  *
  * @v ipoib		IPoIB device
@@ -870,31 +568,15 @@ static void ipoib_irq ( struct net_device *netdev __unused,
 static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) {
 	int rc;
 
-	/* Sanity check */
-	if ( ! ipoib->data.qp )
-		return 0;
-
-	/* Attach data queue to broadcast multicast GID */
-	assert ( ipoib->broadcast_attached == 0 );
-	if ( ( rc = ib_mcast_attach ( ipoib->ibdev, ipoib->data.qp,
-				      &ipoib->broadcast_gid ) ) != 0 ){
-		DBGC ( ipoib, "IPoIB %p could not attach to broadcast GID: "
-		       "%s\n", ipoib, strerror ( rc ) );
-		return rc;
-	}
-	ipoib->broadcast_attached = 1;
-
-	/* Initiate broadcast group join */
-	if ( ( rc = ipoib_mc_member_record ( ipoib, &ipoib->broadcast_gid,
-					     1 ) ) != 0 ) {
-		DBGC ( ipoib, "IPoIB %p could not send broadcast join: %s\n",
+	if ( ( rc = ib_mcast_join ( ipoib->ibdev, ipoib->qp,
+				    &ipoib->broadcast_membership,
+				    &ipoib->broadcast.gid,
+				    ipoib_join_complete ) ) != 0 ) {
+		DBGC ( ipoib, "IPoIB %p could not join broadcast group: %s\n",
 		       ipoib, strerror ( rc ) );
 		return rc;
 	}
-
-	/* We will set link up on the network device when we receive
-	 * the broadcast join response.
-	 */
+	ipoib->broadcast_joined = 1;
 
 	return 0;
 }
@@ -906,12 +588,10 @@ static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) {
  */
 static void ipoib_leave_broadcast_group ( struct ipoib_device *ipoib ) {
 
-	/* Detach data queue from broadcast multicast GID */
-	if ( ipoib->broadcast_attached ) {
-		assert ( ipoib->data.qp != NULL );
-		ib_mcast_detach ( ipoib->ibdev, ipoib->data.qp,
-				  &ipoib->broadcast_gid );
-		ipoib->broadcast_attached = 0;
+	if ( ipoib->broadcast_joined ) {
+		ib_mcast_leave ( ipoib->ibdev, ipoib->qp,
+				 &ipoib->broadcast_membership );
+		ipoib->broadcast_joined = 0;
 	}
 }
 
@@ -923,62 +603,54 @@ static void ipoib_leave_broadcast_group ( struct ipoib_device *ipoib ) {
  */
 static int ipoib_open ( struct net_device *netdev ) {
 	struct ipoib_device *ipoib = netdev->priv;
+	struct ib_device *ibdev = ipoib->ibdev;
 	struct ipoib_mac *mac = ( ( struct ipoib_mac * ) netdev->ll_addr );
 	int rc;
 
 	/* Open IB device */
-	if ( ( rc = ib_open ( ipoib->ibdev ) ) != 0 ) {
+	if ( ( rc = ib_open ( ibdev ) ) != 0 ) {
 		DBGC ( ipoib, "IPoIB %p could not open device: %s\n",
 		       ipoib, strerror ( rc ) );
 		goto err_ib_open;
 	}
 
-	/* Allocate metadata queue set */
-	if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->meta,
-					IPOIB_META_NUM_CQES,
-					&ipoib_meta_cq_op,
-					IPOIB_META_NUM_SEND_WQES,
-					IPOIB_META_NUM_RECV_WQES,
-					IB_GLOBAL_QKEY ) ) != 0 ) {
-		DBGC ( ipoib, "IPoIB %p could not allocate metadata QP: %s\n",
-		       ipoib, strerror ( rc ) );
-		goto err_create_meta_qset;
+	/* Allocate completion queue */
+	ipoib->cq = ib_create_cq ( ibdev, IPOIB_NUM_CQES, &ipoib_cq_op );
+	if ( ! ipoib->cq ) {
+		DBGC ( ipoib, "IPoIB %p could not allocate completion queue\n",
+		       ipoib );
+		rc = -ENOMEM;
+		goto err_create_cq;
 	}
 
-	/* Allocate data queue set */
-	if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->data,
-					IPOIB_DATA_NUM_CQES,
-					&ipoib_data_cq_op,
-					IPOIB_DATA_NUM_SEND_WQES,
-					IPOIB_DATA_NUM_RECV_WQES,
-					IB_GLOBAL_QKEY ) ) != 0 ) {
-		DBGC ( ipoib, "IPoIB %p could not allocate data QP: %s\n",
-		       ipoib, strerror ( rc ) );
-		goto err_create_data_qset;
+	/* Allocate queue pair */
+	ipoib->qp = ib_create_qp ( ibdev, IB_QPT_UD,
+				   IPOIB_NUM_SEND_WQES, ipoib->cq,
+				   IPOIB_NUM_RECV_WQES, ipoib->cq );
+	if ( ! ipoib->qp ) {
+		DBGC ( ipoib, "IPoIB %p could not allocate queue pair\n",
+		       ipoib );
+		rc = -ENOMEM;
+		goto err_create_qp;
 	}
+	ib_qp_set_ownerdata ( ipoib->qp, ipoib );
 
-	/* Update MAC address with data QPN */
-	mac->qpn = htonl ( ipoib->data.qp->qpn );
+	/* Update MAC address with QPN */
+	mac->flags__qpn = htonl ( ipoib->qp->qpn );
 
 	/* Fill receive rings */
-	ipoib_refill_recv ( ipoib, &ipoib->meta );
-	ipoib_refill_recv ( ipoib, &ipoib->data );
+	ib_refill_recv ( ibdev, ipoib->qp );
 
-	/* Join broadcast group */
-	if ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) {
-		DBGC ( ipoib, "IPoIB %p could not join broadcast group: %s\n",
-		       ipoib, strerror ( rc ) );
-		goto err_join_broadcast;
-	}
+	/* Fake a link status change to join the broadcast group */
+	ipoib_link_state_changed ( ibdev );
 
 	return 0;
 
- err_join_broadcast:
-	ipoib_destroy_qset ( ipoib, &ipoib->data );
- err_create_data_qset:
-	ipoib_destroy_qset ( ipoib, &ipoib->meta );
- err_create_meta_qset:
-	ib_close ( ipoib->ibdev );
+	ib_destroy_qp ( ibdev, ipoib->qp );
+ err_create_qp:
+	ib_destroy_cq ( ibdev, ipoib->cq );
+ err_create_cq:
+	ib_close ( ibdev );
  err_ib_open:
 	return rc;
 }
@@ -990,20 +662,21 @@ static int ipoib_open ( struct net_device *netdev ) {
  */
 static void ipoib_close ( struct net_device *netdev ) {
 	struct ipoib_device *ipoib = netdev->priv;
+	struct ib_device *ibdev = ipoib->ibdev;
 	struct ipoib_mac *mac = ( ( struct ipoib_mac * ) netdev->ll_addr );
 
 	/* Leave broadcast group */
 	ipoib_leave_broadcast_group ( ipoib );
 
-	/* Remove data QPN from MAC address */
-	mac->qpn = 0;
+	/* Remove QPN from MAC address */
+	mac->flags__qpn = 0;
 
 	/* Tear down the queues */
-	ipoib_destroy_qset ( ipoib, &ipoib->data );
-	ipoib_destroy_qset ( ipoib, &ipoib->meta );
+	ib_destroy_qp ( ibdev, ipoib->qp );
+	ib_destroy_cq ( ibdev, ipoib->cq );
 
 	/* Close IB device */
-	ib_close ( ipoib->ibdev );
+	ib_close ( ibdev );
 }
 
 /** IPoIB network device operations */
@@ -1016,38 +689,6 @@ static struct net_device_operations ipoib_operations = {
 };
 
 /**
- * Update IPoIB dynamic Infiniband parameters
- *
- * @v ipoib		IPoIB device
- *
- * The Infiniband port GID and partition key will change at runtime,
- * when the link is established (or lost).  The MAC address is based
- * on the port GID, and the broadcast GID is based on the partition
- * key.  This function recalculates these IPoIB device parameters.
- */
-static void ipoib_set_ib_params ( struct ipoib_device *ipoib ) {
-	struct ib_device *ibdev = ipoib->ibdev;
-	struct net_device *netdev = ipoib->netdev;
-	struct ipoib_mac *mac;
-
-	/* Calculate GID portion of MAC address based on port GID */
-	mac = ( ( struct ipoib_mac * ) netdev->ll_addr );
-	memcpy ( &mac->gid, &ibdev->gid, sizeof ( mac->gid ) );
-
-	/* Calculate broadcast GID based on partition key */
-	memcpy ( &ipoib->broadcast_gid, &ipoib_broadcast.gid,
-		 sizeof ( ipoib->broadcast_gid ) );
-	ipoib->broadcast_gid.u.words[2] = htons ( ibdev->pkey );
-
-	/* Set net device link state to reflect Infiniband link state */
-	if ( ib_link_ok ( ibdev ) ) {
-		netdev_link_up ( netdev );
-	} else {
-		netdev_link_down ( netdev );
-	}
-}
-
-/**
  * Handle link status change
  *
  * @v ibdev		Infiniband device
@@ -1055,20 +696,30 @@ static void ipoib_set_ib_params ( struct ipoib_device *ipoib ) {
 void ipoib_link_state_changed ( struct ib_device *ibdev ) {
 	struct net_device *netdev = ib_get_ownerdata ( ibdev );
 	struct ipoib_device *ipoib = netdev->priv;
+	struct ipoib_mac *mac = ( ( struct ipoib_mac * ) netdev->ll_addr );
 	int rc;
 
 	/* Leave existing broadcast group */
 	ipoib_leave_broadcast_group ( ipoib );
 
-	/* Update MAC address and broadcast GID based on new port GID
-	 * and partition key.
-	 */
-	ipoib_set_ib_params ( ipoib );
+	/* Update MAC address based on potentially-new GID prefix */
+	memcpy ( &mac->gid.u.half[0], &ibdev->gid.u.half[0],
+		 sizeof ( mac->gid.u.half[0] ) );
+
+	/* Update broadcast GID based on potentially-new partition key */
+	ipoib->broadcast.gid.u.words[2] =
+		htons ( ibdev->pkey | IB_PKEY_FULL );
+
+	/* Set net device link state to reflect Infiniband link state */
+	rc = ib_link_rc ( ibdev );
+	netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) );
 
 	/* Join new broadcast group */
-	if ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) {
+	if ( ib_link_ok ( ibdev ) &&
+	     ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) ) {
 		DBGC ( ipoib, "IPoIB %p could not rejoin broadcast group: "
 		       "%s\n", ipoib, strerror ( rc ) );
+		netdev_link_err ( netdev, rc );
 		return;
 	}
 }
@@ -1096,11 +747,14 @@ int ipoib_probe ( struct ib_device *ibdev ) {
 	ipoib->netdev = netdev;
 	ipoib->ibdev = ibdev;
 
-	/* Calculate as much of the broadcast GID and the MAC address
-	 * as we can.  We won't know either of these in full until we
-	 * have link-up.
-	 */
-	ipoib_set_ib_params ( ipoib );
+	/* Extract hardware address */
+	memcpy ( netdev->hw_addr, &ibdev->gid.u.half[1],
+		 sizeof ( ibdev->gid.u.half[1] ) );
+
+	/* Set default broadcast address */
+	memcpy ( &ipoib->broadcast, &ipoib_broadcast,
+		 sizeof ( ipoib->broadcast ) );
+	netdev->ll_broadcast = ( ( uint8_t * ) &ipoib->broadcast );
 
 	/* Register network device */
 	if ( ( rc = register_netdev ( netdev ) ) != 0 )
diff --git a/gpxe/src/drivers/net/legacy.c b/gpxe/src/drivers/net/legacy.c
index cbec3cf..79b3580 100644
--- a/gpxe/src/drivers/net/legacy.c
+++ b/gpxe/src/drivers/net/legacy.c
@@ -17,6 +17,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct nic nic;
 
 static int legacy_registered = 0;
@@ -97,7 +99,7 @@ int legacy_probe ( void *hwdev,
 	set_drvdata ( hwdev, netdev );
 	netdev->dev = dev;
 
-	nic.node_addr = netdev->ll_addr;
+	nic.node_addr = netdev->hw_addr;
 	nic.irqno = dev->desc.irq;
 
 	if ( ! probe ( &nic, hwdev ) ) {
@@ -120,7 +122,7 @@ int legacy_probe ( void *hwdev,
 
 	/* Do not remove this message */
 	printf ( "WARNING: Using legacy NIC wrapper on %s\n",
-		 ethernet_protocol.ntoa ( nic.node_addr ) );
+		 netdev->ll_protocol->ntoa ( nic.node_addr ) );
 
 	legacy_registered = 1;
 	return 0;
diff --git a/gpxe/src/drivers/net/mtd80x.c b/gpxe/src/drivers/net/mtd80x.c
index 7cc8ce2..7cf59b0 100644
--- a/gpxe/src/drivers/net/mtd80x.c
+++ b/gpxe/src/drivers/net/mtd80x.c
@@ -23,6 +23,8 @@
 *
 ***************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* to get some global routines like printf */
 #include "etherboot.h"
 /* to get the interface to the body of the program */
@@ -30,6 +32,7 @@
 /* to get the PCI support functions, if this is a PCI NIC */
 #include <gpxe/pci.h>
 #include <gpxe/ethernet.h>
+#include <mii.h>
 
 /* Condensed operations for readability. */
 #define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
@@ -59,71 +62,6 @@
  */
 #define PKT_BUF_SZ 1536
 
-/* Generic MII registers. */
-
-#define MII_BMCR            0x00        /* Basic mode control register */
-#define MII_BMSR            0x01        /* Basic mode status register  */
-#define MII_PHYSID1         0x02        /* PHYS ID 1                   */
-#define MII_PHYSID2         0x03        /* PHYS ID 2                   */
-#define MII_ADVERTISE       0x04        /* Advertisement control reg   */
-#define MII_LPA             0x05        /* Link partner ability reg    */
-#define MII_EXPANSION       0x06        /* Expansion register          */
-#define MII_DCOUNTER        0x12        /* Disconnect counter          */
-#define MII_FCSCOUNTER      0x13        /* False carrier counter       */
-#define MII_NWAYTEST        0x14        /* N-way auto-neg test reg     */
-#define MII_RERRCOUNTER     0x15        /* Receive error counter       */
-#define MII_SREVISION       0x16        /* Silicon revision            */
-#define MII_RESV1           0x17        /* Reserved...                 */
-#define MII_LBRERROR        0x18        /* Lpback, rx, bypass error    */
-#define MII_PHYADDR         0x19        /* PHY address                 */
-#define MII_RESV2           0x1a        /* Reserved...                 */
-#define MII_TPISTATUS       0x1b        /* TPI status for 10mbps       */
-#define MII_NCONFIG         0x1c        /* Network interface config    */
-
-/* Basic mode control register. */
-#define BMCR_RESV               0x007f  /* Unused...                   */
-#define BMCR_CTST               0x0080  /* Collision test              */
-#define BMCR_FULLDPLX           0x0100  /* Full duplex                 */
-#define BMCR_ANRESTART          0x0200  /* Auto negotiation restart    */
-#define BMCR_ISOLATE            0x0400  /* Disconnect DP83840 from MII */
-#define BMCR_PDOWN              0x0800  /* Powerdown the DP83840       */
-#define BMCR_ANENABLE           0x1000  /* Enable auto negotiation     */
-#define BMCR_SPEED100           0x2000  /* Select 100Mbps              */
-#define BMCR_LOOPBACK           0x4000  /* TXD loopback bits           */
-#define BMCR_RESET              0x8000  /* Reset the DP83840           */
-
-/* Basic mode status register. */
-#define BMSR_ERCAP              0x0001  /* Ext-reg capability          */
-#define BMSR_JCD                0x0002  /* Jabber detected             */
-#define BMSR_LSTATUS            0x0004  /* Link status                 */
-#define BMSR_ANEGCAPABLE        0x0008  /* Able to do auto-negotiation */
-#define BMSR_RFAULT             0x0010  /* Remote fault detected       */
-#define BMSR_ANEGCOMPLETE       0x0020  /* Auto-negotiation complete   */
-#define BMSR_RESV               0x07c0  /* Unused...                   */
-#define BMSR_10HALF             0x0800  /* Can do 10mbps, half-duplex  */
-#define BMSR_10FULL             0x1000  /* Can do 10mbps, full-duplex  */
-#define BMSR_100HALF            0x2000  /* Can do 100mbps, half-duplex */
-#define BMSR_100FULL            0x4000  /* Can do 100mbps, full-duplex */
-#define BMSR_100BASE4           0x8000  /* Can do 100mbps, 4k packets  */
-
-/* Advertisement control register. */
-#define ADVERTISE_SLCT          0x001f  /* Selector bits               */
-#define ADVERTISE_CSMA          0x0001  /* Only selector supported     */
-#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
-#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
-#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
-#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
-#define ADVERTISE_100BASE4      0x0200  /* Try for 100mbps 4k packets  */
-#define ADVERTISE_RESV          0x1c00  /* Unused...                   */
-#define ADVERTISE_RFAULT        0x2000  /* Say we can detect faults    */
-#define ADVERTISE_LPACK         0x4000  /* Ack link partners response  */
-#define ADVERTISE_NPAGE         0x8000  /* Next page bit               */
-
-#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
-			ADVERTISE_CSMA)
-#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
-                       ADVERTISE_100HALF | ADVERTISE_100FULL)
-
 /* for different PHY */
 enum phy_type_flags {
     MysonPHY = 1,
@@ -656,9 +594,9 @@ static struct nic_operations mtd_operations = {
 };
 
 static struct pci_device_id mtd80x_nics[] = {
-        PCI_ROM(0x1516, 0x0800, "MTD800", "Myson MTD800"),
-        PCI_ROM(0x1516, 0x0803, "MTD803", "Surecom EP-320X"),
-        PCI_ROM(0x1516, 0x0891, "MTD891", "Myson MTD891"),
+        PCI_ROM(0x1516, 0x0800, "MTD800", "Myson MTD800", 0),
+        PCI_ROM(0x1516, 0x0803, "MTD803", "Surecom EP-320X", 0),
+        PCI_ROM(0x1516, 0x0891, "MTD891", "Myson MTD891", 0),
 };
 
 PCI_DRIVER ( mtd80x_driver, mtd80x_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/mtnic.c b/gpxe/src/drivers/net/mtnic.c
index 0d84a44..d7ee8d2 100644
--- a/gpxe/src/drivers/net/mtnic.c
+++ b/gpxe/src/drivers/net/mtnic.c
@@ -30,6 +30,9 @@
  * SOFTWARE.
  *
  */
+
+FILE_LICENCE ( GPL2_ONLY );
+
 #include <strings.h>
 #include <errno.h>
 #include <gpxe/malloc.h>
@@ -1811,7 +1814,7 @@ mtnic_probe(struct pci_device *pci,
 		/* Program the MAC address */
 		mac = priv->mtnic->fw.mac[port_index];
 		for (mac_idx = 0; mac_idx < MAC_ADDRESS_SIZE; ++mac_idx) {
-			mtnic->netdev[port_index]->ll_addr[MAC_ADDRESS_SIZE - mac_idx - 1] = mac & 0xFF;
+			mtnic->netdev[port_index]->hw_addr[MAC_ADDRESS_SIZE - mac_idx - 1] = mac & 0xFF;
 			mac = mac >> 8;
 		}
 
@@ -1835,10 +1838,10 @@ err_init_card:
 
 
 static struct pci_device_id mtnic_nics[] = {
-	PCI_ROM ( 0x15b3, 0x6368, "mt25448", "Mellanox ConnectX EN driver" ),
-	PCI_ROM ( 0x15b3, 0x6372, "mt25458", "Mellanox ConnectX ENt driver" ),
-	PCI_ROM ( 0x15b3, 0x6750, "mt26448", "Mellanox ConnectX EN GEN2 driver" ),
-	PCI_ROM ( 0x15b3, 0x675a, "mt26458", "Mellanox ConnectX ENt GEN2 driver" ),
+	PCI_ROM ( 0x15b3, 0x6368, "mt25448", "Mellanox ConnectX EN driver", 0 ),
+	PCI_ROM ( 0x15b3, 0x6372, "mt25458", "Mellanox ConnectX ENt driver", 0 ),
+	PCI_ROM ( 0x15b3, 0x6750, "mt26448", "Mellanox ConnectX EN GEN2 driver", 0 ),
+	PCI_ROM ( 0x15b3, 0x675a, "mt26458", "Mellanox ConnectX ENt GEN2 driver", 0 ),
 };
 
 struct pci_driver mtnic_driver __pci_driver = {
diff --git a/gpxe/src/drivers/net/mtnic.h b/gpxe/src/drivers/net/mtnic.h
index 57a7b98..aa240e2 100644
--- a/gpxe/src/drivers/net/mtnic.h
+++ b/gpxe/src/drivers/net/mtnic.h
@@ -30,6 +30,9 @@
  * SOFTWARE.
  *
  */
+
+FILE_LICENCE ( GPL2_ONLY );
+
 #ifndef H_MTNIC_IF_DEFS_H
 #define H_MTNIC_IF_DEFS_H
 
diff --git a/gpxe/src/drivers/net/myri10ge.c b/gpxe/src/drivers/net/myri10ge.c
new file mode 100644
index 0000000..ac2e124
--- /dev/null
+++ b/gpxe/src/drivers/net/myri10ge.c
@@ -0,0 +1,1041 @@
+/************************************************* -*- linux-c -*-
+ * Myricom 10Gb Network Interface Card Software
+ * Copyright 2009, Myricom, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ ****************************************************************/
+
+FILE_LICENCE ( GPL2_ONLY );
+
+/*
+ * Author: Glenn Brown <glenn at myri.com>
+ */
+
+/*
+ * General Theory of Operation
+ *
+ * This is a minimal Myricom 10 gigabit Ethernet driver for network
+ * boot.
+ *
+ * Initialization
+ *
+ * myri10ge_pci_probe() is called by gPXE during initialization.
+ * Minimal NIC initialization is performed to minimize resources
+ * consumed when the driver is resident but unused.
+ *
+ * Network Boot
+ *
+ * myri10ge_net_open() is called by gPXE before attempting to network
+ * boot from the card.  Packet buffers are allocated and the NIC
+ * interface is initialized.
+ *
+ * Transmit
+ *
+ * myri10ge_net_transmit() enqueues frames for transmission by writing
+ * discriptors to the NIC's tx ring.  For simplicity and to avoid
+ * copies, we always have the NIC DMA up the packet.  The sent I/O
+ * buffer is released once the NIC signals myri10ge_interrupt_handler()
+ * that the send has completed.
+ *
+ * Receive
+ *
+ * Receives are posted to the NIC's receive ring.  The NIC fills a
+ * DMAable receive_completion ring with completion notifications.
+ * myri10ge_net_poll() polls for these receive notifications, posts
+ * replacement receive buffers to the NIC, and passes received frames
+ * to netdev_rx().
+ */
+
+/*
+ * Debugging levels:
+ *	- DBG() is for any errors, i.e. failed alloc_iob(), malloc_dma(),
+ *	  TX overflow, corrupted packets, ...
+ *	- DBG2() is for successful events, like packet received,
+ *	  packet transmitted, and other general notifications.
+ *	- DBGP() prints the name of each called function on entry
+ */
+
+#include <stdint.h>
+
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/pci.h>
+#include <gpxe/timer.h>
+
+#include "myri10ge_mcp.h"
+
+/****************************************************************
+ * Forward declarations
+ ****************************************************************/
+
+/* PCI driver entry points */
+
+static int	myri10ge_pci_probe ( struct pci_device*,
+				     const struct pci_device_id* );
+static void	myri10ge_pci_remove ( struct pci_device* );
+
+/* Network device operations */
+
+static void	myri10ge_net_close ( struct net_device* );
+static void	myri10ge_net_irq ( struct net_device*, int enable );
+static int	myri10ge_net_open ( struct net_device* );
+static void	myri10ge_net_poll ( struct net_device* );
+static int	myri10ge_net_transmit ( struct net_device*, struct io_buffer* );
+
+/****************************************************************
+ * Constants
+ ****************************************************************/
+
+/* Maximum ring indices, used to wrap ring indices.  These must be 2**N-1. */
+
+#define MYRI10GE_TRANSMIT_WRAP                  1U
+#define MYRI10GE_RECEIVE_WRAP                   7U
+#define MYRI10GE_RECEIVE_COMPLETION_WRAP        31U
+
+/****************************************************************
+ * Driver internal data types.
+ ****************************************************************/
+
+/* Structure holding all DMA buffers for a NIC, which we will
+   allocated as contiguous read/write DMAable memory when the NIC is
+   initialized. */
+
+struct myri10ge_dma_buffers
+{
+	/* The NIC DMAs receive completion notifications into this ring */
+
+	mcp_slot_t receive_completion[1+MYRI10GE_RECEIVE_COMPLETION_WRAP];
+
+	/* Interrupt details are DMAd here before interrupting. */
+
+	mcp_irq_data_t irq_data; /* 64B */
+
+	/* NIC command completion status is DMAd here. */
+
+	mcp_cmd_response_t command_response; /* 8B */
+};
+
+struct myri10ge_private
+{
+	/* Interrupt support */
+
+	uint32	*irq_claim;	/* in NIC SRAM */
+	uint32	*irq_deassert;	/* in NIC SRAM */
+
+	/* DMA buffers. */
+
+	struct myri10ge_dma_buffers	*dma;
+
+	/*
+	 * Transmit state.
+	 *
+	 * The counts here are uint32 for easy comparison with
+	 * priv->dma->irq_data.send_done_count and with each other.
+	 */
+
+	mcp_kreq_ether_send_t	*transmit_ring;	/* in NIC SRAM */
+	uint32                   transmit_ring_wrap;
+	uint32                   transmits_posted;
+	uint32                   transmits_done;
+	struct io_buffer	*transmit_iob[1 + MYRI10GE_TRANSMIT_WRAP];
+
+	/*
+	 * Receive state.
+	 */
+
+	mcp_kreq_ether_recv_t	*receive_post_ring;	/* in NIC SRAM */
+	unsigned int             receive_post_ring_wrap;
+	unsigned int             receives_posted;
+	unsigned int             receives_done;
+	struct io_buffer	*receive_iob[1 + MYRI10GE_RECEIVE_WRAP];
+
+	/* Address for writing commands to the firmware.
+	   BEWARE: the value must be written 32 bits at a time. */
+
+	mcp_cmd_t	*command;
+};
+
+/****************************************************************
+ * Driver internal functions.
+ ****************************************************************/
+
+/* Print ring status when debugging.  Use this only after a printed
+   value changes. */
+
+#define DBG2_RINGS( priv ) 						\
+	DBG2 ( "tx %x/%x rx %x/%x in %s() \n",				\
+	       ( priv ) ->transmits_done, ( priv ) -> transmits_posted,	\
+	       ( priv ) ->receives_done, ( priv ) -> receives_posted,	\
+	       __FUNCTION__ )
+
+/*
+ * Return a pointer to the driver private data for a network device.
+ *
+ * @v netdev	Network device created by this driver.
+ * @ret priv	The corresponding driver private data.
+ */
+static inline struct myri10ge_private *myri10ge_priv ( struct net_device *nd )
+{
+	/* Our private data always follows the network device in memory,
+	   since we use alloc_netdev() to allocate the storage. */
+
+	return ( struct myri10ge_private * ) ( nd + 1 );
+}
+
+/*
+ * Pass a receive buffer to the NIC to be filled.
+ *
+ * @v priv	The network device to receive the buffer.
+ * @v iob	The I/O buffer to fill.
+ *
+ * Receive buffers are filled in FIFO order.
+ */
+static void myri10ge_post_receive ( struct myri10ge_private *priv,
+				    struct io_buffer *iob )
+{
+	unsigned int		 receives_posted;
+	mcp_kreq_ether_recv_t	*request;
+
+	/* Record the posted I/O buffer, to be passed to netdev_rx() on
+	   receive. */
+
+	receives_posted = priv->receives_posted;
+	priv->receive_iob[receives_posted & MYRI10GE_RECEIVE_WRAP] = iob;
+
+	/* Post the receive. */
+
+	request = &priv->receive_post_ring[receives_posted
+					   & priv->receive_post_ring_wrap];
+	request->addr_high = 0;
+	wmb();
+	request->addr_low = htonl ( virt_to_bus ( iob->data ) );
+	priv->receives_posted = ++receives_posted;
+}
+
+/*
+ * Execute a command on the NIC.
+ *
+ * @v priv	NIC to perform the command.
+ * @v cmd	The command to perform.
+ * @v data	I/O copy buffer for parameters/results
+ * @ret rc	0 on success, else an error code.
+ */
+static int myri10ge_command ( struct myri10ge_private *priv,
+			      uint32 cmd,
+			      uint32 data[3] )
+{
+	int				 i;
+	mcp_cmd_t			*command;
+	uint32				 result;
+	unsigned int			 slept_ms;
+	volatile mcp_cmd_response_t	*response;
+
+	DBGP ( "myri10ge_command ( ,%d, ) \n", cmd );
+	command = priv->command;
+	response = &priv->dma->command_response;
+
+	/* Mark the command as incomplete. */
+
+	response->result = 0xFFFFFFFF;
+
+	/* Pass the command to the NIC. */
+
+	command->cmd		    = htonl ( cmd );
+	command->data0		    = htonl ( data[0] );
+	command->data1		    = htonl ( data[1] );
+	command->data2		    = htonl ( data[2] );
+	command->response_addr.high = 0;
+	command->response_addr.low
+		= htonl ( virt_to_bus ( &priv->dma->command_response ) );
+	for ( i=0; i<36; i+=4 )
+		* ( uint32 * ) &command->pad[i] = 0;
+	wmb();
+	* ( uint32 * ) &command->pad[36] = 0;
+
+	/* Wait up to 2 seconds for a response. */
+
+	for ( slept_ms=0; slept_ms<2000; slept_ms++ ) {
+		result = response->result;
+		if ( result == 0 ) {
+			data[0] = ntohl ( response->data );
+			return 0;
+		} else if ( result != 0xFFFFFFFF ) {
+			DBG ( "cmd%d:0x%x\n",
+			      cmd,
+			      ntohl ( response->result ) );
+			return -EIO;
+		}
+		udelay ( 1000 );
+		rmb();
+	}
+	DBG ( "cmd%d:timed out\n", cmd );
+	return -ETIMEDOUT;
+}
+
+/*
+ * Handle any pending interrupt.
+ *
+ * @v netdev		Device being polled for interrupts.
+ *
+ * This is called periodically to let the driver check for interrupts.
+ */
+static void myri10ge_interrupt_handler ( struct net_device *netdev )
+{
+	struct myri10ge_private *priv;
+	mcp_irq_data_t		*irq_data;
+	uint8			 valid;
+
+	priv = myri10ge_priv ( netdev );
+	irq_data = &priv->dma->irq_data;
+
+	/* Return if there was no interrupt. */
+
+	rmb();
+	valid = irq_data->valid;
+	if ( !valid )
+		return;
+	DBG2 ( "irq " );
+
+	/* Tell the NIC to deassert the interrupt and clear
+	   irq_data->valid.*/
+
+	*priv->irq_deassert = 0;	/* any value is OK. */
+	mb();
+
+	/* Handle any new receives. */
+
+	if ( valid & 1 ) {
+
+		/* Pass the receive interrupt token back to the NIC. */
+
+		DBG2 ( "rx " );
+		*priv->irq_claim = htonl ( 3 );
+		wmb();
+	}
+
+	/* Handle any sent packet by freeing its I/O buffer, now that
+	   we know it has been DMAd. */
+
+	if ( valid & 2 ) {
+		unsigned int nic_done_count;
+
+		DBG2 ( "snt " );
+		nic_done_count = ntohl ( priv->dma->irq_data.send_done_count );
+		while ( priv->transmits_done != nic_done_count ) {
+			struct io_buffer *iob;
+
+			iob = priv->transmit_iob [priv->transmits_done
+						  & MYRI10GE_TRANSMIT_WRAP];
+			DBG2 ( "%p ", iob );
+			netdev_tx_complete ( netdev, iob );
+			++priv->transmits_done;
+		}
+	}
+
+	/* Record any statistics update. */
+
+	if ( irq_data->stats_updated ) {
+
+		/* Update the link status. */
+
+		DBG2 ( "stats " );
+		if ( ntohl ( irq_data->link_up ) == MXGEFW_LINK_UP )
+			netdev_link_up ( netdev );
+		else
+			netdev_link_down ( netdev );
+
+		/* Ignore all error counters from the NIC. */
+	}
+
+	/* Wait for the interrupt to be deasserted, as indicated by
+	   irq_data->valid, which is set by the NIC after the deassert. */
+
+	DBG2 ( "wait " );
+	do {
+		mb();
+	} while ( irq_data->valid );
+
+	/* Claim the interrupt to enable future interrupt generation. */
+
+	DBG2 ( "claim\n" );
+	* ( priv->irq_claim + 1 ) = htonl ( 3 );
+	mb();
+}
+
+/* Constants for reading the STRING_SPECS via the Myricom
+   Vendor Specific PCI configuration space capability. */
+
+#define VS_ADDR ( vs + 0x18 )
+#define VS_DATA ( vs + 0x14 )
+#define VS_MODE ( vs + 0x10 )
+#define 	VS_MODE_READ32 0x3
+#define 	VS_MODE_LOCATE 0x8
+#define 		VS_LOCATE_STRING_SPECS 0x3
+
+/*
+ * Read MAC address from its 'string specs' via the vendor-specific
+ * capability.  (This capability allows NIC SRAM and ROM to be read
+ * before it is mapped.)
+ *
+ * @v pci		The device.
+ * @v mac		Buffer to store the MAC address.
+ * @ret rc		Returns 0 on success, else an error code.
+ */
+static int mac_address_from_string_specs ( struct pci_device *pci,
+						   uint8 mac[ETH_ALEN] )
+{
+	char string_specs[256];
+	char *ptr, *limit;
+	char *to = string_specs;
+	uint32 addr;
+	uint32 len;
+	unsigned int vs;
+	int mac_set = 0;
+
+	/* Find the "vendor specific" capability. */
+
+	vs = pci_find_capability ( pci, 9 );
+	if ( vs == 0 ) {
+		DBG ( "no VS\n" );
+		return -ENOTSUP;
+	}
+
+	/* Locate the String specs in LANai SRAM. */
+
+	pci_write_config_byte ( pci, VS_MODE, VS_MODE_LOCATE );
+	pci_write_config_dword ( pci, VS_ADDR, VS_LOCATE_STRING_SPECS );
+	pci_read_config_dword ( pci, VS_ADDR, &addr );
+	pci_read_config_dword ( pci, VS_DATA, &len );
+	DBG2 ( "ss@%x,%x\n", addr, len );
+
+	/* Copy in the string specs.  Use 32-bit reads for performance. */
+
+	if ( len > sizeof ( string_specs ) || ( len & 3 ) ) {
+		DBG ( "SS too big\n" );
+		return -ENOTSUP;
+	}
+
+	pci_write_config_byte ( pci, VS_MODE, VS_MODE_READ32 );
+	while ( len >= 4 ) {
+		uint32 tmp;
+
+		pci_write_config_byte ( pci, VS_ADDR, addr );
+		pci_read_config_dword ( pci, VS_DATA, &tmp );
+		tmp = ntohl ( tmp );
+		memcpy ( to, &tmp, 4 );
+		to += 4;
+		addr += 4;
+		len -= 4;
+	}
+	pci_write_config_byte ( pci, VS_MODE, 0 );
+
+	/* Parse the string specs. */
+
+	DBG2 ( "STRING_SPECS:\n" );
+	ptr = string_specs;
+	limit = string_specs + sizeof ( string_specs );
+	while ( *ptr != '\0' && ptr < limit ) {
+		DBG2 ( "%s\n", ptr );
+		if ( memcmp ( ptr, "MAC=", 4 ) == 0 ) {
+			unsigned int i;
+
+			ptr += 4;
+			for ( i=0; i<6; i++ ) {
+				if ( ( ptr + 2 ) > limit ) {
+					DBG ( "bad MAC addr\n" );
+					return -ENOTSUP;
+				}
+				mac[i] = strtoul ( ptr, &ptr, 16 );
+				ptr += 1;
+			}
+			mac_set = 1;
+		}
+		else
+			while ( ptr < limit && *ptr++ );
+	}
+
+	/* Verify we parsed all we need. */
+
+	if ( !mac_set ) {
+		DBG ( "no MAC addr\n" );
+		return -ENOTSUP;
+	}
+
+	DBG2 ( "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );
+
+	return 0;
+}
+
+/****************************************************************
+ * gPXE PCI Device Driver API functions
+ ****************************************************************/
+
+/*
+ * Initialize the PCI device.
+ *
+ * @v pci 		The device's associated pci_device structure.
+ * @v id  		The PCI device + vendor id.
+ * @ret rc		Returns zero if successfully initialized.
+ *
+ * This function is called very early on, while gPXE is initializing.
+ * This is a gPXE PCI Device Driver API function.
+ */
+static int myri10ge_pci_probe ( struct pci_device *pci,
+				const struct pci_device_id *id __unused )
+{
+	static struct net_device_operations myri10ge_operations = {
+		.open     = myri10ge_net_open,
+		.close    = myri10ge_net_close,
+		.transmit = myri10ge_net_transmit,
+		.poll     = myri10ge_net_poll,
+		.irq      = myri10ge_net_irq
+	};
+
+	const char *dbg;
+	int rc;
+	struct net_device *netdev;
+	struct myri10ge_private *priv;
+
+	DBGP ( "myri10ge_pci_probe: " );
+
+	netdev = alloc_etherdev ( sizeof ( *priv ) );
+	if ( !netdev ) {
+		rc = -ENOMEM;
+		dbg = "alloc_etherdev";
+		goto abort_with_nothing;
+	}
+
+	netdev_init ( netdev, &myri10ge_operations );
+	priv = myri10ge_priv ( netdev );
+
+	pci_set_drvdata ( pci, netdev );
+	netdev->dev = &pci->dev;
+
+	/* Make sure interrupts are disabled. */
+
+	myri10ge_net_irq ( netdev, 0 );
+
+	/* Read the NIC HW address. */
+
+	rc = mac_address_from_string_specs ( pci, netdev->hw_addr );
+	if ( rc ) {
+		dbg = "mac_from_ss";
+		goto abort_with_netdev_init;
+	}
+	DBGP ( "mac " );
+
+	/* Enable bus master, etc. */
+
+	adjust_pci_device ( pci );
+	DBGP ( "pci " );
+
+	/* Register the initialized network device. */
+
+	rc = register_netdev ( netdev );
+	if ( rc ) {
+		dbg = "register_netdev";
+		goto abort_with_netdev_init;
+	}
+
+	DBGP ( "done\n" );
+
+	return 0;
+
+abort_with_netdev_init:
+	netdev_nullify ( netdev );
+	netdev_put ( netdev );
+abort_with_nothing:
+	DBG ( "%s:%s\n", dbg, strerror ( rc ) );
+	return rc;
+}
+
+/*
+ * Remove a device from the PCI device list.
+ *
+ * @v pci		PCI device to remove.
+ *
+ * This is a PCI Device Driver API function.
+ */
+static void myri10ge_pci_remove ( struct pci_device *pci )
+{
+	struct net_device	*netdev;
+
+	DBGP ( "myri10ge_pci_remove\n" );
+	netdev = pci_get_drvdata ( pci );
+
+	unregister_netdev ( netdev );
+	netdev_nullify ( netdev );
+	netdev_put ( netdev );
+}
+
+/****************************************************************
+ * gPXE Network Device Driver Operations
+ ****************************************************************/
+
+/*
+ * Close a network device.
+ *
+ * @v netdev		Device to close.
+ *
+ * This is a gPXE Network Device Driver API function.
+ */
+static void myri10ge_net_close ( struct net_device *netdev )
+{
+	struct myri10ge_private *priv;
+	uint32			 data[3];
+
+	DBGP ( "myri10ge_net_close\n" );
+	priv = myri10ge_priv ( netdev );
+
+	/* disable interrupts */
+
+	myri10ge_net_irq ( netdev, 0 );
+
+	/* Reset the NIC interface, so we won't get any more events from
+	   the NIC. */
+
+	myri10ge_command ( priv, MXGEFW_CMD_RESET, data );
+
+	/* Free receive buffers that were never filled. */
+
+	while ( priv->receives_done != priv->receives_posted ) {
+		free_iob ( priv->receive_iob[priv->receives_done
+					     & MYRI10GE_RECEIVE_WRAP] );
+		++priv->receives_done;
+	}
+
+	/* Release DMAable memory. */
+
+	free_dma ( priv->dma, sizeof ( *priv->dma ) );
+
+	/* Erase all state from the open. */
+
+	memset ( priv, 0, sizeof ( *priv ) );
+
+	DBG2_RINGS ( priv );
+}
+
+/*
+ * Enable or disable IRQ masking.
+ *
+ * @v netdev		Device to control.
+ * @v enable		Zero to mask off IRQ, non-zero to enable IRQ.
+ *
+ * This is a gPXE Network Driver API function.
+ */
+static void myri10ge_net_irq ( struct net_device *netdev, int enable )
+{
+	struct pci_device	*pci_dev;
+	uint16			 val;
+
+	DBGP ( "myri10ge_net_irq\n" );
+	pci_dev = ( struct pci_device * ) netdev->dev;
+
+	/* Adjust the Interrupt Disable bit in the Command register of the
+	   PCI Device. */
+
+	pci_read_config_word ( pci_dev, PCI_COMMAND, &val );
+	if ( enable )
+		val &= ~PCI_COMMAND_INTX_DISABLE;
+	else
+		val |= PCI_COMMAND_INTX_DISABLE;
+	pci_write_config_word ( pci_dev, PCI_COMMAND, val );
+}
+
+/*
+ * Opens a network device.
+ *
+ * @v netdev		Device to be opened.
+ * @ret rc  		Non-zero if failed to open.
+ *
+ * This enables tx and rx on the device.
+ * This is a gPXE Network Device Driver API function.
+ */
+static int myri10ge_net_open ( struct net_device *netdev )
+{
+	const char		*dbg;	/* printed upon error return */
+	int			 rc;
+	struct io_buffer	*iob;
+	struct myri10ge_private *priv;
+	uint32			 data[3];
+	struct pci_device	*pci_dev;
+	void			*membase;
+
+	DBGP ( "myri10ge_net_open\n" );
+	priv	= myri10ge_priv ( netdev );
+	pci_dev = ( struct pci_device * ) netdev->dev;
+	membase = phys_to_virt ( pci_dev->membase );
+
+	/* Compute address for passing commands to the firmware. */
+
+	priv->command = membase + MXGEFW_ETH_CMD;
+
+	/* Ensure interrupts are disabled. */
+
+	myri10ge_net_irq ( netdev, 0 );
+
+	/* Allocate cleared DMAable buffers. */
+
+	priv->dma = malloc_dma ( sizeof ( *priv->dma ) , 128 );
+	if ( !priv->dma ) {
+		rc = -ENOMEM;
+		dbg = "DMA";
+		goto abort_with_nothing;
+	}
+	memset ( priv->dma, 0, sizeof ( *priv->dma ) );
+
+	/* Simplify following code. */
+
+#define TRY( prefix, base, suffix ) do {		\
+		rc = myri10ge_command ( priv,		\
+					MXGEFW_		\
+					## prefix	\
+					## base		\
+					## suffix,	\
+					data );		\
+		if ( rc ) {				\
+			dbg = #base;			\
+			goto abort_with_dma;		\
+		}					\
+	} while ( 0 )
+
+	/* Send a reset command to the card to see if it is alive,
+	   and to reset its queue state. */
+
+	TRY ( CMD_, RESET , );
+
+	/* Set the interrupt queue size. */
+
+	data[0] = ( sizeof ( priv->dma->receive_completion )
+		    | MXGEFW_CMD_SET_INTRQ_SIZE_FLAG_NO_STRICT_SIZE_CHECK );
+	TRY ( CMD_SET_ , INTRQ_SIZE , );
+
+	/* Set the interrupt queue DMA address. */
+
+	data[0] = virt_to_bus ( &priv->dma->receive_completion );
+	data[1] = 0;
+	TRY ( CMD_SET_, INTRQ_DMA, );
+
+	/* Get the NIC interrupt claim address. */
+
+	TRY ( CMD_GET_, IRQ_ACK, _OFFSET );
+	priv->irq_claim = membase + data[0];
+
+	/* Get the NIC interrupt assert address. */
+
+	TRY ( CMD_GET_, IRQ_DEASSERT, _OFFSET );
+	priv->irq_deassert = membase + data[0];
+
+	/* Disable interrupt coalescing, which is inappropriate for the
+	   minimal buffering we provide. */
+
+	TRY ( CMD_GET_, INTR_COAL, _DELAY_OFFSET );
+	* ( ( uint32 * ) ( membase + data[0] ) ) = 0;
+
+	/* Set the NIC mac address. */
+
+	data[0] = ( netdev->ll_addr[0] << 24
+		    | netdev->ll_addr[1] << 16
+		    | netdev->ll_addr[2] << 8
+		    | netdev->ll_addr[3] );
+	data[1] = ( ( netdev->ll_addr[4] << 8 )
+		     | netdev->ll_addr[5] );
+	TRY ( SET_ , MAC_ADDRESS , );
+
+	/* Enable multicast receives, because some gPXE clients don't work
+	   without multicast. . */
+
+	TRY ( ENABLE_ , ALLMULTI , );
+
+	/* Disable Ethernet flow control, so the NIC cannot deadlock the
+	   network under any circumstances. */
+
+	TRY ( DISABLE_ , FLOW , _CONTROL );
+
+	/* Compute transmit ring sizes. */
+
+	data[0] = 0;		/* slice 0 */
+	TRY ( CMD_GET_, SEND_RING, _SIZE );
+	priv->transmit_ring_wrap
+		= data[0] / sizeof ( mcp_kreq_ether_send_t ) - 1;
+	if ( priv->transmit_ring_wrap
+	     & ( priv->transmit_ring_wrap + 1 ) ) {
+		rc = -EPROTO;
+		dbg = "TX_RING";
+		goto abort_with_dma;
+	}
+
+	/* Compute receive ring sizes. */
+
+	data[0] = 0;		/* slice 0 */
+	TRY ( CMD_GET_ , RX_RING , _SIZE );
+	priv->receive_post_ring_wrap = data[0] / sizeof ( mcp_dma_addr_t ) - 1;
+	if ( priv->receive_post_ring_wrap
+	     & ( priv->receive_post_ring_wrap + 1 ) ) {
+		rc = -EPROTO;
+		dbg = "RX_RING";
+		goto abort_with_dma;
+	}
+
+	/* Get NIC transmit ring address. */
+
+	data[0] = 0;		/* slice 0. */
+	TRY ( CMD_GET_, SEND, _OFFSET );
+	priv->transmit_ring = membase + data[0];
+
+	/* Get the NIC receive ring address. */
+
+	data[0] = 0;		/* slice 0. */
+	TRY ( CMD_GET_, SMALL_RX, _OFFSET );
+	priv->receive_post_ring = membase + data[0];
+
+	/* Set the Nic MTU. */
+
+	data[0] = ETH_FRAME_LEN;
+	TRY ( CMD_SET_, MTU, );
+
+	/* Tell the NIC our buffer sizes. ( We use only small buffers, so we
+	   set both buffer sizes to the same value, which will force all
+	   received frames to use small buffers. ) */
+
+	data[0] = MXGEFW_PAD + ETH_FRAME_LEN;
+	TRY ( CMD_SET_, SMALL_BUFFER, _SIZE );
+	data[0] = MXGEFW_PAD + ETH_FRAME_LEN;
+	TRY ( CMD_SET_, BIG_BUFFER, _SIZE );
+
+        /* Tell firmware where to DMA IRQ data */
+
+	data[0] = virt_to_bus ( &priv->dma->irq_data );
+	data[1] = 0;
+	data[2] = sizeof ( priv->dma->irq_data );
+	TRY ( CMD_SET_, STATS_DMA_V2, );
+
+	/* Post receives. */
+
+	while ( priv->receives_posted <= MYRI10GE_RECEIVE_WRAP ) {
+
+		/* Reserve 2 extra bytes at the start of packets, since
+		   the firmware always skips the first 2 bytes of the buffer
+		   so TCP headers will be aligned. */
+
+		iob = alloc_iob ( MXGEFW_PAD + ETH_FRAME_LEN );
+		if ( !iob ) {
+			rc = -ENOMEM;
+			dbg = "alloc_iob";
+			goto abort_with_receives_posted;
+		}
+		iob_reserve ( iob, MXGEFW_PAD );
+		myri10ge_post_receive ( priv, iob );
+	}
+
+	/* Bring up the link. */
+
+	TRY ( CMD_, ETHERNET_UP, );
+
+	DBG2_RINGS ( priv );
+	return 0;
+
+abort_with_receives_posted:
+	while ( priv->receives_posted-- )
+		free_iob ( priv->receive_iob[priv->receives_posted] );
+abort_with_dma:
+	/* Because the link is not up, we don't have to reset the NIC here. */
+	free_dma ( priv->dma, sizeof ( *priv->dma ) );
+abort_with_nothing:
+	/* Erase all signs of the failed open. */
+	memset ( priv, 0, sizeof ( *priv ) );
+	DBG ( "%s: %s\n", dbg, strerror ( rc ) );
+	return ( rc );
+}
+
+/*
+ * This function allows a driver to process events during operation.
+ *
+ * @v netdev		Device being polled.
+ *
+ * This is called periodically by gPXE to let the driver check the status of
+ * transmitted packets and to allow the driver to check for received packets.
+ * This is a gPXE Network Device Driver API function.
+ */
+static void myri10ge_net_poll ( struct net_device *netdev )
+{
+	struct io_buffer		*iob;
+	struct io_buffer		*replacement;
+	struct myri10ge_dma_buffers	*dma;
+	struct myri10ge_private		*priv;
+	unsigned int			 length;
+	unsigned int			 orig_receives_posted;
+
+	DBGP ( "myri10ge_net_poll\n" );
+	priv = myri10ge_priv ( netdev );
+	dma  = priv->dma;
+
+	/* Process any pending interrupt. */
+
+	myri10ge_interrupt_handler ( netdev );
+
+	/* Pass up received frames, but limit ourselves to receives posted
+	   before this function was called, so we cannot livelock if
+	   receives are arriving faster than we process them. */
+
+	orig_receives_posted = priv->receives_posted;
+	while ( priv->receives_done != orig_receives_posted ) {
+
+		/* Stop if there is no pending receive. */
+
+		length = ntohs ( dma->receive_completion
+				 [priv->receives_done
+				  & MYRI10GE_RECEIVE_COMPLETION_WRAP]
+				 .length );
+		if ( length == 0 )
+			break;
+
+		/* Allocate a replacement buffer.  If none is available,
+		   stop passing up packets until a buffer is available.
+
+		   Reserve 2 extra bytes at the start of packets, since
+		   the firmware always skips the first 2 bytes of the buffer
+		   so TCP headers will be aligned. */
+
+		replacement = alloc_iob ( MXGEFW_PAD + ETH_FRAME_LEN );
+		if ( !replacement ) {
+			DBG ( "NO RX BUF\n" );
+			break;
+		}
+		iob_reserve ( replacement, MXGEFW_PAD );
+
+		/* Pass up the received frame. */
+
+		iob = priv->receive_iob[priv->receives_done
+					& MYRI10GE_RECEIVE_WRAP];
+		iob_put ( iob, length );
+		netdev_rx ( netdev, iob );
+
+		/* We have consumed the packet, so clear the receive
+		   notification. */
+
+		dma->receive_completion [priv->receives_done
+					 & MYRI10GE_RECEIVE_COMPLETION_WRAP]
+			.length = 0;
+		wmb();
+
+		/* Replace the passed-up I/O buffer. */
+
+		myri10ge_post_receive ( priv, replacement );
+		++priv->receives_done;
+		DBG2_RINGS ( priv );
+	}
+}
+
+/*
+ * This transmits a packet.
+ *
+ * @v netdev		Device to transmit from.
+ * @v iobuf 		Data to transmit.
+ * @ret rc  		Non-zero if failed to transmit.
+ *
+ * This is a gPXE Network Driver API function.
+ */
+static int myri10ge_net_transmit ( struct net_device *netdev,
+				   struct io_buffer *iobuf )
+{
+	mcp_kreq_ether_send_t	*kreq;
+	size_t			 len;
+	struct myri10ge_private *priv;
+	uint32			 transmits_posted;
+
+	DBGP ( "myri10ge_net_transmit\n" );
+	priv = myri10ge_priv ( netdev );
+
+	/* Confirm space in the send ring. */
+
+	transmits_posted = priv->transmits_posted;
+	if ( transmits_posted - priv->transmits_done
+	     > MYRI10GE_TRANSMIT_WRAP ) {
+		DBG ( "TX ring full\n" );
+		return -ENOBUFS;
+	}
+
+	DBG2 ( "TX %p+%d ", iobuf->data, iob_len ( iobuf ) );
+	DBG2_HD ( iobuf->data, 14 );
+
+	/* Record the packet being transmitted, so we can later report
+	   send completion. */
+
+	priv->transmit_iob[transmits_posted & MYRI10GE_TRANSMIT_WRAP] = iobuf;
+
+	/* Copy and pad undersized frames, because the NIC does not pad,
+	   and we would rather copy small frames than do a gather. */
+
+	len = iob_len ( iobuf );
+	if ( len < ETH_ZLEN ) {
+		iob_pad ( iobuf, ETH_ZLEN );
+		len = ETH_ZLEN;
+	}
+
+	/* Enqueue the packet by writing a descriptor to the NIC.
+	   This is a bit tricky because the HW requires 32-bit writes,
+	   but the structure has smaller fields. */
+
+	kreq = &priv->transmit_ring[transmits_posted
+				    & priv->transmit_ring_wrap];
+	kreq->addr_high = 0;
+	kreq->addr_low = htonl ( virt_to_bus ( iobuf->data ) );
+	( ( uint32 * ) kreq ) [2] = htonl (
+		0x0000 << 16	 /* pseudo_header_offset */
+		| ( len & 0xFFFF ) /* length */
+		);
+	wmb();
+	( ( uint32 * ) kreq ) [3] = htonl (
+		0x00 << 24	/* pad */
+		| 0x01 << 16	/* rdma_count */
+		| 0x00 << 8	/* cksum_offset */
+		| ( MXGEFW_FLAGS_SMALL
+		    | MXGEFW_FLAGS_FIRST
+		    | MXGEFW_FLAGS_NO_TSO ) /* flags */
+		);
+	wmb();
+
+	/* Mark the slot as consumed and return. */
+
+	priv->transmits_posted = ++transmits_posted;
+	DBG2_RINGS ( priv );
+	return 0;
+}
+
+static struct pci_device_id myri10ge_nics[] = {
+	/* Each of these macros must be a single line to satisfy a script. */
+	PCI_ROM ( 0x14c1, 0x0008, "myri10ge", "Myricom 10Gb Ethernet Adapter", 0 ) ,
+};
+
+struct pci_driver myri10ge_driver __pci_driver = {
+	.ids      = myri10ge_nics,
+	.id_count = ( sizeof ( myri10ge_nics ) / sizeof ( myri10ge_nics[0] ) ) ,
+	.probe    = myri10ge_pci_probe,
+	.remove   = myri10ge_pci_remove
+};
+
+/*
+ * Local variables:
+ *  c-basic-offset: 8
+ *  c-indent-level: 8
+ *  tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/myri10ge_mcp.h b/gpxe/src/drivers/net/myri10ge_mcp.h
new file mode 100644
index 0000000..397f8b0
--- /dev/null
+++ b/gpxe/src/drivers/net/myri10ge_mcp.h
@@ -0,0 +1,514 @@
+/************************************************* -*- linux-c -*-
+ * Myricom 10Gb Network Interface Card Software
+ * Copyright 2005-2010, Myricom, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ ****************************************************************/
+
+FILE_LICENCE ( GPL2_ONLY );
+
+#ifndef _myri10ge_mcp_h
+#define _myri10ge_mcp_h
+
+#define MXGEFW_VERSION_MAJOR	1
+#define MXGEFW_VERSION_MINOR	4
+
+#ifdef MXGEFW
+#ifndef _stdint_h_
+typedef signed char          int8_t;
+typedef signed short        int16_t;
+typedef signed int          int32_t;
+typedef signed long long    int64_t;
+typedef unsigned char       uint8_t;
+typedef unsigned short     uint16_t;
+typedef unsigned int       uint32_t;
+typedef unsigned long long uint64_t;
+#endif
+#endif
+
+/* 8 Bytes */
+struct mcp_dma_addr {
+  uint32_t high;
+  uint32_t low;
+};
+typedef struct mcp_dma_addr mcp_dma_addr_t;
+
+/* 4 Bytes */
+struct mcp_slot {
+  uint16_t checksum;
+  uint16_t length;
+};
+typedef struct mcp_slot mcp_slot_t;
+
+#ifdef MXGEFW_NDIS
+/* 8-byte descriptor, exclusively used by NDIS drivers. */
+struct mcp_slot_8 {
+  /* Place hash value at the top so it gets written before length.
+   * The driver polls length.
+   */
+  uint32_t hash;
+  uint16_t checksum;
+  uint16_t length;
+};
+typedef struct mcp_slot_8 mcp_slot_8_t;
+
+/* Two bits of length in mcp_slot are used to indicate hash type. */
+#define MXGEFW_RSS_HASH_NULL (0 << 14) /* bit 15:14 = 00 */
+#define MXGEFW_RSS_HASH_IPV4 (1 << 14) /* bit 15:14 = 01 */
+#define MXGEFW_RSS_HASH_TCP_IPV4 (2 << 14) /* bit 15:14 = 10 */
+#define MXGEFW_RSS_HASH_MASK (3 << 14) /* bit 15:14 = 11 */
+#endif
+
+/* 64 Bytes */
+struct mcp_cmd {
+  uint32_t cmd;
+  uint32_t data0;	/* will be low portion if data > 32 bits */
+  /* 8 */
+  uint32_t data1;	/* will be high portion if data > 32 bits */
+  uint32_t data2;	/* currently unused.. */
+  /* 16 */
+  struct mcp_dma_addr response_addr;
+  /* 24 */
+  uint8_t pad[40];
+};
+typedef struct mcp_cmd mcp_cmd_t;
+
+/* 8 Bytes */
+struct mcp_cmd_response {
+  uint32_t data;
+  uint32_t result;
+};
+typedef struct mcp_cmd_response mcp_cmd_response_t;
+
+
+
+/*
+   flags used in mcp_kreq_ether_send_t:
+
+   The SMALL flag is only needed in the first segment. It is raised
+   for packets that are total less or equal 512 bytes.
+
+   The CKSUM flag must be set in all segments.
+
+   The PADDED flags is set if the packet needs to be padded, and it
+   must be set for all segments.
+
+   The  MXGEFW_FLAGS_ALIGN_ODD must be set if the cumulative
+   length of all previous segments was odd.
+*/
+
+
+#define MXGEFW_FLAGS_SMALL      0x1
+#define MXGEFW_FLAGS_TSO_HDR    0x1
+#define MXGEFW_FLAGS_FIRST      0x2
+#define MXGEFW_FLAGS_ALIGN_ODD  0x4
+#define MXGEFW_FLAGS_CKSUM      0x8
+#define MXGEFW_FLAGS_TSO_LAST   0x8
+#define MXGEFW_FLAGS_NO_TSO     0x10
+#define MXGEFW_FLAGS_TSO_CHOP   0x10
+#define MXGEFW_FLAGS_TSO_PLD    0x20
+
+#define MXGEFW_SEND_SMALL_SIZE  1520
+#define MXGEFW_MAX_MTU          9400
+
+union mcp_pso_or_cumlen {
+  uint16_t pseudo_hdr_offset;
+  uint16_t cum_len;
+};
+typedef union mcp_pso_or_cumlen mcp_pso_or_cumlen_t;
+
+#define	MXGEFW_MAX_SEND_DESC 12
+#define MXGEFW_PAD	    2
+
+/* 16 Bytes */
+struct mcp_kreq_ether_send {
+  uint32_t addr_high;
+  uint32_t addr_low;
+  uint16_t pseudo_hdr_offset;
+  uint16_t length;
+  uint8_t  pad;
+  uint8_t  rdma_count;
+  uint8_t  cksum_offset; 	/* where to start computing cksum */
+  uint8_t  flags;	       	/* as defined above */
+};
+typedef struct mcp_kreq_ether_send mcp_kreq_ether_send_t;
+
+/* 8 Bytes */
+struct mcp_kreq_ether_recv {
+  uint32_t addr_high;
+  uint32_t addr_low;
+};
+typedef struct mcp_kreq_ether_recv mcp_kreq_ether_recv_t;
+
+
+/* Commands */
+
+#define	MXGEFW_BOOT_HANDOFF	0xfc0000
+#define	MXGEFW_BOOT_DUMMY_RDMA	0xfc01c0
+
+#define	MXGEFW_ETH_CMD		0xf80000
+#define	MXGEFW_ETH_SEND_4	0x200000
+#define	MXGEFW_ETH_SEND_1	0x240000
+#define	MXGEFW_ETH_SEND_2	0x280000
+#define	MXGEFW_ETH_SEND_3	0x2c0000
+#define	MXGEFW_ETH_RECV_SMALL	0x300000
+#define	MXGEFW_ETH_RECV_BIG	0x340000
+#define	MXGEFW_ETH_SEND_GO	0x380000
+#define	MXGEFW_ETH_SEND_STOP	0x3C0000
+
+#define	MXGEFW_ETH_SEND(n)		(0x200000 + (((n) & 0x03) * 0x40000))
+#define	MXGEFW_ETH_SEND_OFFSET(n)	(MXGEFW_ETH_SEND(n) - MXGEFW_ETH_SEND_4)
+
+enum myri10ge_mcp_cmd_type {
+  MXGEFW_CMD_NONE = 0,
+  /* Reset the mcp, it is left in a safe state, waiting
+     for the driver to set all its parameters */
+  MXGEFW_CMD_RESET = 1,
+
+  /* get the version number of the current firmware..
+     (may be available in the eeprom strings..? */
+  MXGEFW_GET_MCP_VERSION = 2,
+
+
+  /* Parameters which must be set by the driver before it can
+     issue MXGEFW_CMD_ETHERNET_UP. They persist until the next
+     MXGEFW_CMD_RESET is issued */
+
+  MXGEFW_CMD_SET_INTRQ_DMA = 3,
+  /* data0 = LSW of the host address
+   * data1 = MSW of the host address
+   * data2 = slice number if multiple slices are used
+   */
+
+  MXGEFW_CMD_SET_BIG_BUFFER_SIZE = 4,	/* in bytes, power of 2 */
+  MXGEFW_CMD_SET_SMALL_BUFFER_SIZE = 5,	/* in bytes */
+
+
+  /* Parameters which refer to lanai SRAM addresses where the
+     driver must issue PIO writes for various things */
+
+  MXGEFW_CMD_GET_SEND_OFFSET = 6,
+  MXGEFW_CMD_GET_SMALL_RX_OFFSET = 7,
+  MXGEFW_CMD_GET_BIG_RX_OFFSET = 8,
+  /* data0 = slice number if multiple slices are used */
+
+  MXGEFW_CMD_GET_IRQ_ACK_OFFSET = 9,
+  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET = 10,
+
+  /* Parameters which refer to rings stored on the MCP,
+     and whose size is controlled by the mcp */
+
+  MXGEFW_CMD_GET_SEND_RING_SIZE = 11,	/* in bytes */
+  MXGEFW_CMD_GET_RX_RING_SIZE = 12,	/* in bytes */
+
+  /* Parameters which refer to rings stored in the host,
+     and whose size is controlled by the host.  Note that
+     all must be physically contiguous and must contain
+     a power of 2 number of entries.  */
+
+  MXGEFW_CMD_SET_INTRQ_SIZE = 13, 	/* in bytes */
+#define MXGEFW_CMD_SET_INTRQ_SIZE_FLAG_NO_STRICT_SIZE_CHECK  (1 << 31)
+
+  /* command to bring ethernet interface up.  Above parameters
+     (plus mtu & mac address) must have been exchanged prior
+     to issuing this command  */
+  MXGEFW_CMD_ETHERNET_UP = 14,
+
+  /* command to bring ethernet interface down.  No further sends
+     or receives may be processed until an MXGEFW_CMD_ETHERNET_UP
+     is issued, and all interrupt queues must be flushed prior
+     to ack'ing this command */
+
+  MXGEFW_CMD_ETHERNET_DOWN = 15,
+
+  /* commands the driver may issue live, without resetting
+     the nic.  Note that increasing the mtu "live" should
+     only be done if the driver has already supplied buffers
+     sufficiently large to handle the new mtu.  Decreasing
+     the mtu live is safe */
+
+  MXGEFW_CMD_SET_MTU = 16,
+  MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET = 17,  /* in microseconds */
+  MXGEFW_CMD_SET_STATS_INTERVAL = 18,   /* in microseconds */
+  MXGEFW_CMD_SET_STATS_DMA_OBSOLETE = 19, /* replaced by SET_STATS_DMA_V2 */
+
+  MXGEFW_ENABLE_PROMISC = 20,
+  MXGEFW_DISABLE_PROMISC = 21,
+  MXGEFW_SET_MAC_ADDRESS = 22,
+
+  MXGEFW_ENABLE_FLOW_CONTROL = 23,
+  MXGEFW_DISABLE_FLOW_CONTROL = 24,
+
+  /* do a DMA test
+     data0,data1 = DMA address
+     data2       = RDMA length (MSH), WDMA length (LSH)
+     command return data = repetitions (MSH), 0.5-ms ticks (LSH)
+  */
+  MXGEFW_DMA_TEST = 25,
+
+  MXGEFW_ENABLE_ALLMULTI = 26,
+  MXGEFW_DISABLE_ALLMULTI = 27,
+
+  /* returns MXGEFW_CMD_ERROR_MULTICAST
+     if there is no room in the cache
+     data0,MSH(data1) = multicast group address */
+  MXGEFW_JOIN_MULTICAST_GROUP = 28,
+  /* returns MXGEFW_CMD_ERROR_MULTICAST
+     if the address is not in the cache,
+     or is equal to FF-FF-FF-FF-FF-FF
+     data0,MSH(data1) = multicast group address */
+  MXGEFW_LEAVE_MULTICAST_GROUP = 29,
+  MXGEFW_LEAVE_ALL_MULTICAST_GROUPS = 30,
+
+  MXGEFW_CMD_SET_STATS_DMA_V2 = 31,
+  /* data0, data1 = bus addr,
+   * data2 = sizeof(struct mcp_irq_data) from driver point of view, allows
+   * adding new stuff to mcp_irq_data without changing the ABI
+   *
+   * If multiple slices are used, data2 contains both the size of the
+   * structure (in the lower 16 bits) and the slice number
+   * (in the upper 16 bits).
+   */
+
+  MXGEFW_CMD_UNALIGNED_TEST = 32,
+  /* same than DMA_TEST (same args) but abort with UNALIGNED on unaligned
+     chipset */
+
+  MXGEFW_CMD_UNALIGNED_STATUS = 33,
+  /* return data = boolean, true if the chipset is known to be unaligned */
+
+  MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS = 34,
+  /* data0 = number of big buffers to use.  It must be 0 or a power of 2.
+   * 0 indicates that the NIC consumes as many buffers as they are required
+   * for packet. This is the default behavior.
+   * A power of 2 number indicates that the NIC always uses the specified
+   * number of buffers for each big receive packet.
+   * It is up to the driver to ensure that this value is big enough for
+   * the NIC to be able to receive maximum-sized packets.
+   */
+
+  MXGEFW_CMD_GET_MAX_RSS_QUEUES = 35,
+  MXGEFW_CMD_ENABLE_RSS_QUEUES = 36,
+  /* data0 = number of slices n (0, 1, ..., n-1) to enable
+   * data1 = interrupt mode | use of multiple transmit queues.
+   * 0=share one INTx/MSI.
+   * 1=use one MSI-X per queue.
+   * If all queues share one interrupt, the driver must have set
+   * RSS_SHARED_INTERRUPT_DMA before enabling queues.
+   * 2=enable both receive and send queues.
+   * Without this bit set, only one send queue (slice 0's send queue)
+   * is enabled.  The receive queues are always enabled.
+   */
+#define MXGEFW_SLICE_INTR_MODE_SHARED          0x0
+#define MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE   0x1
+#define MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES 0x2
+
+  MXGEFW_CMD_GET_RSS_SHARED_INTERRUPT_MASK_OFFSET = 37,
+  MXGEFW_CMD_SET_RSS_SHARED_INTERRUPT_DMA = 38,
+  /* data0, data1 = bus address lsw, msw */
+  MXGEFW_CMD_GET_RSS_TABLE_OFFSET = 39,
+  /* get the offset of the indirection table */
+  MXGEFW_CMD_SET_RSS_TABLE_SIZE = 40,
+  /* set the size of the indirection table */
+  MXGEFW_CMD_GET_RSS_KEY_OFFSET = 41,
+  /* get the offset of the secret key */
+  MXGEFW_CMD_RSS_KEY_UPDATED = 42,
+  /* tell nic that the secret key's been updated */
+  MXGEFW_CMD_SET_RSS_ENABLE = 43,
+  /* data0 = enable/disable rss
+   * 0: disable rss.  nic does not distribute receive packets.
+   * 1: enable rss.  nic distributes receive packets among queues.
+   * data1 = hash type
+   * 1: IPV4            (required by RSS)
+   * 2: TCP_IPV4        (required by RSS)
+   * 3: IPV4 | TCP_IPV4 (required by RSS)
+   * 4: source port
+   * 5: source port + destination port
+   */
+#define MXGEFW_RSS_HASH_TYPE_IPV4      0x1
+#define MXGEFW_RSS_HASH_TYPE_TCP_IPV4  0x2
+#define MXGEFW_RSS_HASH_TYPE_SRC_PORT  0x4
+#define MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT 0x5
+#define MXGEFW_RSS_HASH_TYPE_MAX 0x5
+
+  MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE = 44,
+  /* Return data = the max. size of the entire headers of a IPv6 TSO packet.
+   * If the header size of a IPv6 TSO packet is larger than the specified
+   * value, then the driver must not use TSO.
+   * This size restriction only applies to IPv6 TSO.
+   * For IPv4 TSO, the maximum size of the headers is fixed, and the NIC
+   * always has enough header buffer to store maximum-sized headers.
+   */
+
+  MXGEFW_CMD_SET_TSO_MODE = 45,
+  /* data0 = TSO mode.
+   * 0: Linux/FreeBSD style (NIC default)
+   * 1: NDIS/NetBSD style
+   */
+#define MXGEFW_TSO_MODE_LINUX  0
+#define MXGEFW_TSO_MODE_NDIS   1
+
+  MXGEFW_CMD_MDIO_READ = 46,
+  /* data0 = dev_addr (PMA/PMD or PCS ...), data1 = register/addr */
+  MXGEFW_CMD_MDIO_WRITE = 47,
+  /* data0 = dev_addr,  data1 = register/addr, data2 = value  */
+
+  MXGEFW_CMD_I2C_READ = 48,
+  /* Starts to get a fresh copy of one byte or of the module i2c table, the
+   * obtained data is cached inside the xaui-xfi chip :
+   *   data0 :  0 => get one byte, 1=> get 256 bytes
+   *   data1 :  If data0 == 0: location to refresh
+   *               bit 7:0  register location
+   *               bit 8:15 is the i2c slave addr (0 is interpreted as 0xA1)
+   *               bit 23:16 is the i2c bus number (for multi-port NICs)
+   *            If data0 == 1: unused
+   * The operation might take ~1ms for a single byte or ~65ms when refreshing all 256 bytes
+   * During the i2c operation,  MXGEFW_CMD_I2C_READ or MXGEFW_CMD_I2C_BYTE attempts
+   *  will return MXGEFW_CMD_ERROR_BUSY
+   */
+  MXGEFW_CMD_I2C_BYTE = 49,
+  /* Return the last obtained copy of a given byte in the xfp i2c table
+   * (copy cached during the last relevant MXGEFW_CMD_I2C_READ)
+   *   data0 : index of the desired table entry
+   *  Return data = the byte stored at the requested index in the table
+   */
+
+  MXGEFW_CMD_GET_VPUMP_OFFSET = 50,
+  /* Return data = NIC memory offset of mcp_vpump_public_global */
+  MXGEFW_CMD_RESET_VPUMP = 51,
+  /* Resets the VPUMP state */
+
+  MXGEFW_CMD_SET_RSS_MCP_SLOT_TYPE = 52,
+  /* data0 = mcp_slot type to use.
+   * 0 = the default 4B mcp_slot
+   * 1 = 8B mcp_slot_8
+   */
+#define MXGEFW_RSS_MCP_SLOT_TYPE_MIN        0
+#define MXGEFW_RSS_MCP_SLOT_TYPE_WITH_HASH  1
+
+  MXGEFW_CMD_SET_THROTTLE_FACTOR = 53,
+  /* set the throttle factor for ethp_z8e
+     data0 = throttle_factor
+     throttle_factor = 256 * pcie-raw-speed / tx_speed
+     tx_speed = 256 * pcie-raw-speed / throttle_factor
+
+     For PCI-E x8: pcie-raw-speed == 16Gb/s
+     For PCI-E x4: pcie-raw-speed == 8Gb/s
+
+     ex1: throttle_factor == 0x1a0 (416), tx_speed == 1.23GB/s == 9.846 Gb/s
+     ex2: throttle_factor == 0x200 (512), tx_speed == 1.0GB/s == 8 Gb/s
+
+     with tx_boundary == 2048, max-throttle-factor == 8191 => min-speed == 500Mb/s
+     with tx_boundary == 4096, max-throttle-factor == 4095 => min-speed == 1Gb/s
+  */
+
+  MXGEFW_CMD_VPUMP_UP = 54,
+  /* Allocates VPump Connection, Send Request and Zero copy buffer address tables */
+  MXGEFW_CMD_GET_VPUMP_CLK = 55,
+  /* Get the lanai clock */
+
+  MXGEFW_CMD_GET_DCA_OFFSET = 56,
+  /* offset of dca control for WDMAs */
+
+  /* VMWare NetQueue commands */
+  MXGEFW_CMD_NETQ_GET_FILTERS_PER_QUEUE = 57,
+  MXGEFW_CMD_NETQ_ADD_FILTER = 58,
+  /* data0 = filter_id << 16 | queue << 8 | type */
+  /* data1 = MS4 of MAC Addr */
+  /* data2 = LS2_MAC << 16 | VLAN_tag */
+  MXGEFW_CMD_NETQ_DEL_FILTER = 59,
+  /* data0 = filter_id */
+  MXGEFW_CMD_NETQ_QUERY1 = 60,
+  MXGEFW_CMD_NETQ_QUERY2 = 61,
+  MXGEFW_CMD_NETQ_QUERY3 = 62,
+  MXGEFW_CMD_NETQ_QUERY4 = 63,
+
+  MXGEFW_CMD_RELAX_RXBUFFER_ALIGNMENT = 64,
+  /* When set, small receive buffers can cross page boundaries.
+   * Both small and big receive buffers may start at any address.
+   * This option has performance implications, so use with caution.
+   */
+};
+typedef enum myri10ge_mcp_cmd_type myri10ge_mcp_cmd_type_t;
+
+
+enum myri10ge_mcp_cmd_status {
+  MXGEFW_CMD_OK = 0,
+  MXGEFW_CMD_UNKNOWN = 1,
+  MXGEFW_CMD_ERROR_RANGE = 2,
+  MXGEFW_CMD_ERROR_BUSY = 3,
+  MXGEFW_CMD_ERROR_EMPTY = 4,
+  MXGEFW_CMD_ERROR_CLOSED = 5,
+  MXGEFW_CMD_ERROR_HASH_ERROR = 6,
+  MXGEFW_CMD_ERROR_BAD_PORT = 7,
+  MXGEFW_CMD_ERROR_RESOURCES = 8,
+  MXGEFW_CMD_ERROR_MULTICAST = 9,
+  MXGEFW_CMD_ERROR_UNALIGNED = 10,
+  MXGEFW_CMD_ERROR_NO_MDIO = 11,
+  MXGEFW_CMD_ERROR_I2C_FAILURE = 12,
+  MXGEFW_CMD_ERROR_I2C_ABSENT = 13,
+  MXGEFW_CMD_ERROR_BAD_PCIE_LINK = 14
+};
+typedef enum myri10ge_mcp_cmd_status myri10ge_mcp_cmd_status_t;
+
+
+#define MXGEFW_OLD_IRQ_DATA_LEN 40
+
+struct mcp_irq_data {
+  /* add new counters at the beginning */
+  uint32_t future_use[1];
+  uint32_t dropped_pause;
+  uint32_t dropped_unicast_filtered;
+  uint32_t dropped_bad_crc32;
+  uint32_t dropped_bad_phy;
+  uint32_t dropped_multicast_filtered;
+/* 40 Bytes */
+  uint32_t send_done_count;
+
+#define MXGEFW_LINK_DOWN 0
+#define MXGEFW_LINK_UP 1
+#define MXGEFW_LINK_MYRINET 2
+#define MXGEFW_LINK_UNKNOWN 3
+  uint32_t link_up;
+  uint32_t dropped_link_overflow;
+  uint32_t dropped_link_error_or_filtered;
+  uint32_t dropped_runt;
+  uint32_t dropped_overrun;
+  uint32_t dropped_no_small_buffer;
+  uint32_t dropped_no_big_buffer;
+  uint32_t rdma_tags_available;
+
+  uint8_t tx_stopped;
+  uint8_t link_down;
+  uint8_t stats_updated;
+  uint8_t valid;
+};
+typedef struct mcp_irq_data mcp_irq_data_t;
+
+#ifdef MXGEFW_NDIS
+/* Exclusively used by NDIS drivers */
+struct mcp_rss_shared_interrupt {
+  uint8_t pad[2];
+  uint8_t queue;
+  uint8_t valid;
+};
+#endif
+
+/* definitions for NETQ filter type */
+#define MXGEFW_NETQ_FILTERTYPE_NONE 0
+#define MXGEFW_NETQ_FILTERTYPE_MACADDR 1
+#define MXGEFW_NETQ_FILTERTYPE_VLAN 2
+#define MXGEFW_NETQ_FILTERTYPE_VLANMACADDR 3
+
+#endif /* _myri10ge_mcp_h */
diff --git a/gpxe/src/drivers/net/natsemi.c b/gpxe/src/drivers/net/natsemi.c
index 3a77623..db3f320 100644
--- a/gpxe/src/drivers/net/natsemi.c
+++ b/gpxe/src/drivers/net/natsemi.c
@@ -46,6 +46,8 @@
 
 */
 
+FILE_LICENCE ( GPL_ANY );
+
 /* Revision History */
 
 /*
@@ -202,7 +204,7 @@ static int natsemi_probe (struct pci_device *pci,
 	last = prev_bytes[1] >> 7;
 	for ( i = 0 ; i < ETH_ALEN ; i++ ) {
 		last1 = ll_addr_encoded[i] >> 7;
-	 	netdev->ll_addr[i] = ll_addr_encoded[i] << 1 | last;
+		netdev->hw_addr[i] = ll_addr_encoded[i] << 1 | last;
 		last = last1;
 	}
 
@@ -268,7 +270,7 @@ static void natsemi_reset (struct net_device *netdev)
         wcsr = inl (np->ioaddr + WOLCmd) & WCSR_RESET_SAVE;
 
         /* RFCR */
-        rfcr = readl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
+        rfcr = inl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
 
         /* PMATCH */
         for (i = 0; i < 3; i++) {
@@ -596,7 +598,7 @@ static void natsemi_irq (struct net_device *netdev, int enable)
 }
 
 static struct pci_device_id natsemi_nics[] = {
-	PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815"),
+	PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815", 0),
 };
 
 struct pci_driver natsemi_driver __pci_driver = {
diff --git a/gpxe/src/drivers/net/natsemi.h b/gpxe/src/drivers/net/natsemi.h
index 13b5545..ae827ba 100644
--- a/gpxe/src/drivers/net/natsemi.h
+++ b/gpxe/src/drivers/net/natsemi.h
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL_ANY );
+
 #define NATSEMI_HW_TIMEOUT 400
 
 #define TX_RING_SIZE 4
diff --git a/gpxe/src/drivers/net/ne2k_isa.c b/gpxe/src/drivers/net/ne2k_isa.c
index f8a45cc..603d1ed 100644
--- a/gpxe/src/drivers/net/ne2k_isa.c
+++ b/gpxe/src/drivers/net/ne2k_isa.c
@@ -19,6 +19,8 @@
  Extracted from ns8390.c and adapted by Pantelis Koukousoulas <pktoss at gmail.com>
  **************************************************************************/
 
+FILE_LICENCE ( BSD2 );
+
 #include "ns8390.h"
 #include "etherboot.h"
 #include "nic.h"
diff --git a/gpxe/src/drivers/net/ns83820.c b/gpxe/src/drivers/net/ns83820.c
old mode 100755
new mode 100644
index 36091c0..44d875f
--- a/gpxe/src/drivers/net/ns83820.c
+++ b/gpxe/src/drivers/net/ns83820.c
@@ -35,6 +35,8 @@
 *    Indent Options: indent -kr -i8
 ***************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* to get some global routines like printf */
 #include "etherboot.h"
 /* to get the interface to the body of the program */
@@ -802,7 +804,7 @@ static struct nic_operations ns83820_operations = {
 };
 
 static struct pci_device_id ns83820_nics[] = {
-	PCI_ROM(0x100b, 0x0022, "ns83820", "National Semiconductor 83820"),
+	PCI_ROM(0x100b, 0x0022, "ns83820", "National Semiconductor 83820", 0),
 };
 
 PCI_DRIVER ( ns83820_driver, ns83820_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/ns8390.c b/gpxe/src/drivers/net/ns8390.c
index 3461c21..97f1141 100644
--- a/gpxe/src/drivers/net/ns8390.c
+++ b/gpxe/src/drivers/net/ns8390.c
@@ -25,6 +25,8 @@ SMC8416 PIO support added by Andrew Bettison (andrewb at zip.com.au) on 4/3/02
 
 **************************************************************************/
 
+FILE_LICENCE ( BSD2 );
+
 /* #warning "ns8390.c: FIXME: split ISA and PCI, clean up" */
 
 #if 1
@@ -1004,17 +1006,17 @@ ISA_ROM("ne","NE1000/2000 and clones");
 #ifdef	INCLUDE_NS8390
 static struct pci_device_id nepci_nics[] = {
 /* A few NE2000 PCI clones, list not exhaustive */
-PCI_ROM(0x10ec, 0x8029, "rtl8029",      "Realtek 8029"),
-PCI_ROM(0x1186, 0x0300, "dlink-528",    "D-Link DE-528"),
-PCI_ROM(0x1050, 0x0940, "winbond940",   "Winbond NE2000-PCI"),		/* Winbond 86C940 / 89C940 */
-PCI_ROM(0x1050, 0x5a5a, "winbond940f",  "Winbond W89c940F"),		/* Winbond 89C940F */
-PCI_ROM(0x11f6, 0x1401, "compexrl2000", "Compex ReadyLink 2000"),
-PCI_ROM(0x8e2e, 0x3000, "ktiet32p2",    "KTI ET32P2"),
-PCI_ROM(0x4a14, 0x5000, "nv5000sc",     "NetVin NV5000SC"),
-PCI_ROM(0x12c3, 0x0058, "holtek80232",  "Holtek HT80232"),
-PCI_ROM(0x12c3, 0x5598, "holtek80229",  "Holtek HT80229"),
-PCI_ROM(0x10bd, 0x0e34, "surecom-ne34", "Surecom NE34"),
-PCI_ROM(0x1106, 0x0926, "via86c926",    "Via 86c926"),
+PCI_ROM(0x10ec, 0x8029, "rtl8029",      "Realtek 8029", 0),
+PCI_ROM(0x1186, 0x0300, "dlink-528",    "D-Link DE-528", 0),
+PCI_ROM(0x1050, 0x0940, "winbond940",   "Winbond NE2000-PCI", 0),		/* Winbond 86C940 / 89C940 */
+PCI_ROM(0x1050, 0x5a5a, "winbond940f",  "Winbond W89c940F", 0),		/* Winbond 89C940F */
+PCI_ROM(0x11f6, 0x1401, "compexrl2000", "Compex ReadyLink 2000", 0),
+PCI_ROM(0x8e2e, 0x3000, "ktiet32p2",    "KTI ET32P2", 0),
+PCI_ROM(0x4a14, 0x5000, "nv5000sc",     "NetVin NV5000SC", 0),
+PCI_ROM(0x12c3, 0x0058, "holtek80232",  "Holtek HT80232", 0),
+PCI_ROM(0x12c3, 0x5598, "holtek80229",  "Holtek HT80229", 0),
+PCI_ROM(0x10bd, 0x0e34, "surecom-ne34", "Surecom NE34", 0),
+PCI_ROM(0x1106, 0x0926, "via86c926",    "Via 86c926", 0),
 };
 
 PCI_DRIVER ( nepci_driver, nepci_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/ns8390.h b/gpxe/src/drivers/net/ns8390.h
index 2c4e972..79728e7 100644
--- a/gpxe/src/drivers/net/ns8390.h
+++ b/gpxe/src/drivers/net/ns8390.h
@@ -6,6 +6,8 @@ Author: Martin Renters
 
 **************************************************************************/
 
+FILE_LICENCE ( BSD2 );
+
 #define VENDOR_NONE	0
 #define VENDOR_WD	1
 #define VENDOR_NOVELL	2
diff --git a/gpxe/src/drivers/net/p80211hdr.h b/gpxe/src/drivers/net/p80211hdr.h
index d9a8bbb..8354671 100644
--- a/gpxe/src/drivers/net/p80211hdr.h
+++ b/gpxe/src/drivers/net/p80211hdr.h
@@ -57,6 +57,8 @@
 * --------------------------------------------------------------------
 */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 #ifndef _P80211HDR_H
 #define _P80211HDR_H
 
diff --git a/gpxe/src/drivers/net/pcnet32.c b/gpxe/src/drivers/net/pcnet32.c
index d8529da..223bc1e 100644
--- a/gpxe/src/drivers/net/pcnet32.c
+++ b/gpxe/src/drivers/net/pcnet32.c
@@ -39,6 +39,8 @@
 *    Indent Options: indent -kr -i8
 ***************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "etherboot.h"
 #include "nic.h"
 #include <gpxe/pci.h>
@@ -216,16 +218,10 @@ struct {
 	__attribute__ ((aligned(16)));
 	struct pcnet32_rx_head rx_ring[RX_RING_SIZE]
 	__attribute__ ((aligned(16)));
-	unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
-	unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
+	unsigned char txb[TX_RING_SIZE][PKT_BUF_SZ];
+	unsigned char rxb[RX_RING_SIZE][PKT_BUF_SZ];
 } pcnet32_bufs __shared;
 
-/* May need to be moved to mii.h */
-struct mii_if_info {
-	int phy_id;
-	int advertising;
-	unsigned int full_duplex:1;	/* is full duplex? */
-};
 
 /*
  * The first three fields of pcnet32_private are read by the ethernet device 
@@ -588,7 +584,7 @@ static void pcnet32_transmit(struct nic *nic __unused, const char *d,	/* Destina
 
 	status = 0x8300;
 	/* point to the current txb incase multiple tx_rings are used */
-	ptxb = pcnet32_bufs.txb + (lp->cur_tx * PKT_BUF_SZ);
+	ptxb = pcnet32_bufs.txb[lp->cur_tx];
 
 	/* copy the packet to ring buffer */
 	memcpy(ptxb, d, ETH_ALEN);	/* dst */
@@ -1006,9 +1002,9 @@ static struct nic_operations pcnet32_operations = {
 };
 
 static struct pci_device_id pcnet32_nics[] = {
-	PCI_ROM(0x1022, 0x2000, "pcnet32", "AMD PCnet/PCI"),
-	PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD PCNet FAST III"),
-	PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD PCnet/HomePNA"),
+	PCI_ROM(0x1022, 0x2000, "pcnet32", "AMD PCnet/PCI", 0),
+	PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD PCNet FAST III", 0),
+	PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD PCnet/HomePNA", 0),
 };
 
 PCI_DRIVER ( pcnet32_driver, pcnet32_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/phantom/nx_bitops.h b/gpxe/src/drivers/net/phantom/nx_bitops.h
index 33c8fba..4068632 100644
--- a/gpxe/src/drivers/net/phantom/nx_bitops.h
+++ b/gpxe/src/drivers/net/phantom/nx_bitops.h
@@ -19,6 +19,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/drivers/net/phantom/nxhal_nic_interface.h b/gpxe/src/drivers/net/phantom/nxhal_nic_interface.h
index aa05c72..f487624 100644
--- a/gpxe/src/drivers/net/phantom/nxhal_nic_interface.h
+++ b/gpxe/src/drivers/net/phantom/nxhal_nic_interface.h
@@ -1,3 +1,5 @@
+FILE_LICENCE ( GPL2_ONLY );
+
 /*
  * Data types and structure for HAL - NIC interface.
  *
diff --git a/gpxe/src/drivers/net/phantom/phantom.c b/gpxe/src/drivers/net/phantom/phantom.c
index 1d1637d..4c3f22f 100644
--- a/gpxe/src/drivers/net/phantom/phantom.c
+++ b/gpxe/src/drivers/net/phantom/phantom.c
@@ -17,6 +17,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -1154,7 +1156,7 @@ static int phantom_open ( struct net_device *netdev ) {
 	 * firmware doesn't currently support this.
 	 */
 	if ( ( rc = phantom_add_macaddr ( phantom,
-				   netdev->ll_protocol->ll_broadcast ) ) != 0 )
+					  netdev->ll_broadcast ) ) != 0 )
 		goto err_add_macaddr_broadcast;
 	if ( ( rc = phantom_add_macaddr ( phantom,
 					  netdev->ll_addr ) ) != 0 )
@@ -1164,8 +1166,7 @@ static int phantom_open ( struct net_device *netdev ) {
 
 	phantom_del_macaddr ( phantom, netdev->ll_addr );
  err_add_macaddr_unicast:
-	phantom_del_macaddr ( phantom,
-			      netdev->ll_protocol->ll_broadcast );
+	phantom_del_macaddr ( phantom, netdev->ll_broadcast );
  err_add_macaddr_broadcast:
 	phantom_destroy_tx_ctx ( phantom );
  err_create_tx_ctx:
@@ -1189,8 +1190,7 @@ static void phantom_close ( struct net_device *netdev ) {
 
 	/* Shut down the port */
 	phantom_del_macaddr ( phantom, netdev->ll_addr );
-	phantom_del_macaddr ( phantom,
-			      netdev->ll_protocol->ll_broadcast );
+	phantom_del_macaddr ( phantom, netdev->ll_broadcast );
 	phantom_destroy_tx_ctx ( phantom );
 	phantom_destroy_rx_ctx ( phantom );
 	free_dma ( phantom->desc, sizeof ( *(phantom->desc) ) );
@@ -1897,10 +1897,10 @@ static int phantom_init_cmdpeg ( struct phantom_nic *phantom ) {
  * Read Phantom MAC address
  *
  * @v phanton_port	Phantom NIC
- * @v ll_addr		Buffer to fill with MAC address
+ * @v hw_addr		Buffer to fill with MAC address
  */
 static void phantom_get_macaddr ( struct phantom_nic *phantom,
-				  uint8_t *ll_addr ) {
+				  uint8_t *hw_addr ) {
 	union {
 		uint8_t mac_addr[2][ETH_ALEN];
 		uint32_t dwords[3];
@@ -1917,11 +1917,11 @@ static void phantom_get_macaddr ( struct phantom_nic *phantom,
 
 	/* Copy out the relevant MAC address */
 	for ( i = 0 ; i < ETH_ALEN ; i++ ) {
-		ll_addr[ ETH_ALEN - i - 1 ] =
+		hw_addr[ ETH_ALEN - i - 1 ] =
 			u.mac_addr[ phantom->port & 1 ][i];
 	}
 	DBGC ( phantom, "Phantom %p MAC address is %s\n",
-	       phantom, eth_ntoa ( ll_addr ) );
+	       phantom, eth_ntoa ( hw_addr ) );
 }
 
 /**
@@ -2045,7 +2045,7 @@ static int phantom_probe ( struct pci_device *pci,
 		goto err_init_rcvpeg;
 
 	/* Read MAC addresses */
-	phantom_get_macaddr ( phantom, netdev->ll_addr );
+	phantom_get_macaddr ( phantom, netdev->hw_addr );
 
 	/* Skip if boot disabled on NIC */
 	if ( ( rc = phantom_check_boot_enable ( phantom ) ) != 0 )
@@ -2100,7 +2100,7 @@ static void phantom_remove ( struct pci_device *pci ) {
 
 /** Phantom PCI IDs */
 static struct pci_device_id phantom_nics[] = {
-	PCI_ROM ( 0x4040, 0x0100, "nx", "NX" ),
+	PCI_ROM ( 0x4040, 0x0100, "nx", "NX", 0 ),
 };
 
 /** Phantom PCI driver */
diff --git a/gpxe/src/drivers/net/phantom/phantom.h b/gpxe/src/drivers/net/phantom/phantom.h
index 974eeca..1018a69 100644
--- a/gpxe/src/drivers/net/phantom/phantom.h
+++ b/gpxe/src/drivers/net/phantom/phantom.h
@@ -20,6 +20,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/drivers/net/phantom/phantom_hw.h b/gpxe/src/drivers/net/phantom/phantom_hw.h
index e2c3e53..950f36a 100644
--- a/gpxe/src/drivers/net/phantom/phantom_hw.h
+++ b/gpxe/src/drivers/net/phantom/phantom_hw.h
@@ -20,6 +20,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/drivers/net/pnic.c b/gpxe/src/drivers/net/pnic.c
index 3a4af96..cbc6790 100644
--- a/gpxe/src/drivers/net/pnic.c
+++ b/gpxe/src/drivers/net/pnic.c
@@ -12,6 +12,8 @@ Bochs Pseudo NIC driver for Etherboot
  * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdio.h>
 #include <gpxe/io.h>
@@ -248,7 +250,7 @@ static int pnic_probe ( struct pci_device *pci,
 
 	/* Get MAC address */
 	status = pnic_command ( pnic, PNIC_CMD_READ_MAC, NULL, 0,
-				netdev->ll_addr, ETH_ALEN, NULL );
+				netdev->hw_addr, ETH_ALEN, NULL );
 
 	/* Mark as link up; PNIC has no concept of link state */
 	netdev_link_up ( netdev );
@@ -268,7 +270,7 @@ static int pnic_probe ( struct pci_device *pci,
 
 static struct pci_device_id pnic_nics[] = {
 /* genrules.pl doesn't let us use macros for PCI IDs...*/
-PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor" ),
+PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor", 0 ),
 };
 
 struct pci_driver pnic_driver __pci_driver = {
diff --git a/gpxe/src/drivers/net/pnic_api.h b/gpxe/src/drivers/net/pnic_api.h
index 6d117fa..27e0236 100644
--- a/gpxe/src/drivers/net/pnic_api.h
+++ b/gpxe/src/drivers/net/pnic_api.h
@@ -12,6 +12,8 @@
  * time from PNIC_REG_DATA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /*
  * PCI parameters
  */
diff --git a/gpxe/src/drivers/net/prism2.c b/gpxe/src/drivers/net/prism2.c
index c54dba5..4c66592 100644
--- a/gpxe/src/drivers/net/prism2.c
+++ b/gpxe/src/drivers/net/prism2.c
@@ -13,6 +13,8 @@ $Id$
  * your option) any later version.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <etherboot.h>
 #include <nic.h>
 #include <gpxe/pci.h>
diff --git a/gpxe/src/drivers/net/prism2_pci.c b/gpxe/src/drivers/net/prism2_pci.c
index eda7bf5..b7c1e6b 100644
--- a/gpxe/src/drivers/net/prism2_pci.c
+++ b/gpxe/src/drivers/net/prism2_pci.c
@@ -14,6 +14,8 @@ $Id$
  * your option) any later version.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/pci.h>
 #include <nic.h>
 
@@ -37,9 +39,9 @@ static void prism2_pci_disable ( struct nic *nic ) {
 }
 
 static struct pci_device_id prism2_pci_nics[] = {
-PCI_ROM(0x1260, 0x3873, "prism2_pci",	"Harris Semiconductor Prism2.5 clone"),
-PCI_ROM(0x1260, 0x3873, "hwp01170",	"ActionTec HWP01170"),
-PCI_ROM(0x1260, 0x3873, "dwl520",	"DLink DWL-520"),
+PCI_ROM(0x1260, 0x3873, "prism2_pci",	"Harris Semiconductor Prism2.5 clone", 0),
+PCI_ROM(0x1260, 0x3873, "hwp01170",	"ActionTec HWP01170", 0),
+PCI_ROM(0x1260, 0x3873, "dwl520",	"DLink DWL-520", 0),
 };
 
 PCI_DRIVER ( prism2_pci_driver, prism2_pci_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/prism2_plx.c b/gpxe/src/drivers/net/prism2_plx.c
index a10eef2..9fb5be2 100644
--- a/gpxe/src/drivers/net/prism2_plx.c
+++ b/gpxe/src/drivers/net/prism2_plx.c
@@ -14,6 +14,8 @@ $Id$
  * your option) any later version.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/pci.h>
 #include <nic.h>
 
@@ -92,17 +94,17 @@ static void prism2_plx_disable ( struct nic *nic ) {
 }
 
 static struct pci_device_id prism2_plx_nics[] = {
-PCI_ROM(0x1385, 0x4100, "ma301",         "Netgear MA301"),
-PCI_ROM(0x10b7, 0x7770, "3c-airconnect", "3Com AirConnect"),
-PCI_ROM(0x111a, 0x1023, "ss1023",        "Siemens SpeedStream SS1023"),
-PCI_ROM(0x15e8, 0x0130, "correga",       "Correga"),
-PCI_ROM(0x1638, 0x1100, "smc2602w",      "SMC EZConnect SMC2602W"),	/* or Eumitcom PCI WL11000, Addtron AWA-100 */
-PCI_ROM(0x16ab, 0x1100, "gl24110p",      "Global Sun Tech GL24110P"),
-PCI_ROM(0x16ab, 0x1101, "16ab-1101",     "Unknown"),
-PCI_ROM(0x16ab, 0x1102, "wdt11",         "Linksys WDT11"),
-PCI_ROM(0x16ec, 0x3685, "usr2415",       "USR 2415"),
-PCI_ROM(0xec80, 0xec00, "f5d6000",       "Belkin F5D6000"),
-PCI_ROM(0x126c, 0x8030, "emobility",     "Nortel emobility"),
+PCI_ROM(0x1385, 0x4100, "ma301",         "Netgear MA301", 0),
+PCI_ROM(0x10b7, 0x7770, "3c-airconnect", "3Com AirConnect", 0),
+PCI_ROM(0x111a, 0x1023, "ss1023",        "Siemens SpeedStream SS1023", 0),
+PCI_ROM(0x15e8, 0x0130, "correga",       "Correga", 0),
+PCI_ROM(0x1638, 0x1100, "smc2602w",      "SMC EZConnect SMC2602W", 0),	/* or Eumitcom PCI WL11000, Addtron AWA-100 */
+PCI_ROM(0x16ab, 0x1100, "gl24110p",      "Global Sun Tech GL24110P", 0),
+PCI_ROM(0x16ab, 0x1101, "16ab-1101",     "Unknown", 0),
+PCI_ROM(0x16ab, 0x1102, "wdt11",         "Linksys WDT11", 0),
+PCI_ROM(0x16ec, 0x3685, "usr2415",       "USR 2415", 0),
+PCI_ROM(0xec80, 0xec00, "f5d6000",       "Belkin F5D6000", 0),
+PCI_ROM(0x126c, 0x8030, "emobility",     "Nortel emobility", 0),
 };
 
 PCI_DRIVER ( prism2_plx_driver, prism2_plx_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/r8169.c b/gpxe/src/drivers/net/r8169.c
index 4315f47..b468782 100644
--- a/gpxe/src/drivers/net/r8169.c
+++ b/gpxe/src/drivers/net/r8169.c
@@ -23,6 +23,8 @@
  * Copyright (c) a lot of people too. Please respect their work.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -39,6 +41,7 @@
 #include <gpxe/netdevice.h>
 #include <gpxe/pci.h>
 #include <gpxe/timer.h>
+#include <mii.h>
 
 #include "r8169.h"
 
@@ -2206,9 +2209,9 @@ rtl8169_probe ( struct pci_device *pdev, const struct pci_device_id *ent )
 
 	/* Get MAC address */
 	for ( i = 0; i < MAC_ADDR_LEN; i++ )
-		netdev->ll_addr[i] = RTL_R8 ( MAC0 + i );
+		netdev->hw_addr[i] = RTL_R8 ( MAC0 + i );
 
-	DBG ( "%s\n", eth_ntoa ( netdev->ll_addr ) );
+	DBG ( "%s\n", eth_ntoa ( netdev->hw_addr ) );
 
 	rtl8169_init_phy ( netdev, tp );
 
@@ -2254,16 +2257,16 @@ rtl8169_remove ( struct pci_device *pdev )
 }
 
 static struct pci_device_id rtl8169_nics[] = {
-	PCI_ROM(0x10ec, 0x8129, "rtl8169-0x8129", "rtl8169-0x8129"),
-	PCI_ROM(0x10ec, 0x8136, "rtl8169-0x8136", "rtl8169-0x8136"),
-	PCI_ROM(0x10ec, 0x8167, "rtl8169-0x8167", "rtl8169-0x8167"),
-	PCI_ROM(0x10ec, 0x8168, "rtl8169-0x8168", "rtl8169-0x8168"),
-	PCI_ROM(0x10ec, 0x8169, "rtl8169-0x8169", "rtl8169-0x8169"),
-	PCI_ROM(0x1186, 0x4300, "rtl8169-0x4300", "rtl8169-0x4300"),
-	PCI_ROM(0x1259, 0xc107, "rtl8169-0xc107", "rtl8169-0xc107"),
-	PCI_ROM(0x16ec, 0x0116, "rtl8169-0x0116", "rtl8169-0x0116"),
-	PCI_ROM(0x1737, 0x1032, "rtl8169-0x1032", "rtl8169-0x1032"),
-	PCI_ROM(0x0001, 0x8168, "rtl8169-0x8168", "rtl8169-0x8168"),
+	PCI_ROM(0x10ec, 0x8129, "rtl8169-0x8129", "rtl8169-0x8129", 0),
+	PCI_ROM(0x10ec, 0x8136, "rtl8169-0x8136", "rtl8169-0x8136", 0),
+	PCI_ROM(0x10ec, 0x8167, "rtl8169-0x8167", "rtl8169-0x8167", 0),
+	PCI_ROM(0x10ec, 0x8168, "rtl8169-0x8168", "rtl8169-0x8168", 0),
+	PCI_ROM(0x10ec, 0x8169, "rtl8169-0x8169", "rtl8169-0x8169", 0),
+	PCI_ROM(0x1186, 0x4300, "rtl8169-0x4300", "rtl8169-0x4300", 0),
+	PCI_ROM(0x1259, 0xc107, "rtl8169-0xc107", "rtl8169-0xc107", 0),
+	PCI_ROM(0x16ec, 0x0116, "rtl8169-0x0116", "rtl8169-0x0116", 0),
+	PCI_ROM(0x1737, 0x1032, "rtl8169-0x1032", "rtl8169-0x1032", 0),
+	PCI_ROM(0x0001, 0x8168, "rtl8169-0x8168", "rtl8169-0x8168", 0),
 };
 
 struct pci_driver rtl8169_driver __pci_driver = {
diff --git a/gpxe/src/drivers/net/r8169.h b/gpxe/src/drivers/net/r8169.h
index d353632..89679b1 100644
--- a/gpxe/src/drivers/net/r8169.h
+++ b/gpxe/src/drivers/net/r8169.h
@@ -24,6 +24,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef _R8169_H_
 #define _R8169_H_
 
@@ -52,90 +54,9 @@
 #define DUPLEX_HALF		0x00
 #define DUPLEX_FULL		0x01
 
-/* Generic MII registers. */
-
-#define MII_BMCR            0x00        /* Basic mode control register */
-#define MII_BMSR            0x01        /* Basic mode status register  */
-#define MII_PHYSID1         0x02        /* PHYS ID 1                   */
-#define MII_PHYSID2         0x03        /* PHYS ID 2                   */
-#define MII_ADVERTISE       0x04        /* Advertisement control reg   */
-#define MII_LPA             0x05        /* Link partner ability reg    */
-#define MII_EXPANSION       0x06        /* Expansion register          */
-#define MII_CTRL1000        0x09        /* 1000BASE-T control          */
-#define MII_STAT1000        0x0a        /* 1000BASE-T status           */
-#define MII_ESTATUS	    0x0f	/* Extended Status */
-#define MII_DCOUNTER        0x12        /* Disconnect counter          */
-#define MII_FCSCOUNTER      0x13        /* False carrier counter       */
-#define MII_NWAYTEST        0x14        /* N-way auto-neg test reg     */
-#define MII_RERRCOUNTER     0x15        /* Receive error counter       */
-#define MII_SREVISION       0x16        /* Silicon revision            */
-#define MII_RESV1           0x17        /* Reserved...                 */
-#define MII_LBRERROR        0x18        /* Lpback, rx, bypass error    */
-#define MII_PHYADDR         0x19        /* PHY address                 */
-#define MII_RESV2           0x1a        /* Reserved...                 */
-#define MII_TPISTATUS       0x1b        /* TPI status for 10mbps       */
-#define MII_NCONFIG         0x1c        /* Network interface config    */
-
-/* Basic mode control register. */
-#define BMCR_RESV               0x003f  /* Unused...                   */
-#define BMCR_SPEED1000		0x0040  /* MSB of Speed (1000)         */
-#define BMCR_CTST               0x0080  /* Collision test              */
-#define BMCR_FULLDPLX           0x0100  /* Full duplex                 */
-#define BMCR_ANRESTART          0x0200  /* Auto negotiation restart    */
-#define BMCR_ISOLATE            0x0400  /* Disconnect DP83840 from MII */
-#define BMCR_PDOWN              0x0800  /* Powerdown the DP83840       */
-#define BMCR_ANENABLE           0x1000  /* Enable auto negotiation     */
-#define BMCR_SPEED100           0x2000  /* Select 100Mbps              */
-#define BMCR_LOOPBACK           0x4000  /* TXD loopback bits           */
-#define BMCR_RESET              0x8000  /* Reset the DP83840           */
-
-/* Basic mode status register. */
-#define BMSR_ERCAP              0x0001  /* Ext-reg capability          */
-#define BMSR_JCD                0x0002  /* Jabber detected             */
-#define BMSR_LSTATUS            0x0004  /* Link status                 */
-#define BMSR_ANEGCAPABLE        0x0008  /* Able to do auto-negotiation */
-#define BMSR_RFAULT             0x0010  /* Remote fault detected       */
-#define BMSR_ANEGCOMPLETE       0x0020  /* Auto-negotiation complete   */
-#define BMSR_RESV               0x00c0  /* Unused...                   */
-#define BMSR_ESTATEN		0x0100	/* Extended Status in R15 */
-#define BMSR_100HALF2           0x0200  /* Can do 100BASE-T2 HDX */
-#define BMSR_100FULL2           0x0400  /* Can do 100BASE-T2 FDX */
-#define BMSR_10HALF             0x0800  /* Can do 10mbps, half-duplex  */
-#define BMSR_10FULL             0x1000  /* Can do 10mbps, full-duplex  */
-#define BMSR_100HALF            0x2000  /* Can do 100mbps, half-duplex */
-#define BMSR_100FULL            0x4000  /* Can do 100mbps, full-duplex */
-#define BMSR_100BASE4           0x8000  /* Can do 100mbps, 4k packets  */
-
 #define AUTONEG_DISABLE		0x00
 #define AUTONEG_ENABLE		0x01
 
-#define MII_ADVERTISE           0x04        /* Advertisement control reg   */
-#define ADVERTISE_SLCT          0x001f  /* Selector bits               */
-#define ADVERTISE_CSMA          0x0001  /* Only selector supported     */
-#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
-#define ADVERTISE_1000XFULL     0x0020  /* Try for 1000BASE-X full-duplex */
-#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
-#define ADVERTISE_1000XHALF     0x0040  /* Try for 1000BASE-X half-duplex */
-#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
-#define ADVERTISE_1000XPAUSE    0x0080  /* Try for 1000BASE-X pause    */
-#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
-#define ADVERTISE_1000XPSE_ASYM 0x0100  /* Try for 1000BASE-X asym pause */
-#define ADVERTISE_100BASE4      0x0200  /* Try for 100mbps 4k packets  */
-#define ADVERTISE_PAUSE_CAP     0x0400  /* Try for pause               */
-#define ADVERTISE_PAUSE_ASYM    0x0800  /* Try for asymetric pause     */
-#define ADVERTISE_RESV          0x1000  /* Unused...                   */
-#define ADVERTISE_RFAULT        0x2000  /* Say we can detect faults    */
-#define ADVERTISE_LPACK         0x4000  /* Ack link partners response  */
-#define ADVERTISE_NPAGE         0x8000  /* Next page bit               */
-#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
-			ADVERTISE_CSMA)
-#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
-		       ADVERTISE_100HALF | ADVERTISE_100FULL)
-
-/* 1000BASE-T Control register */
-#define ADVERTISE_1000FULL      0x0200  /* Advertise 1000BASE-T full duplex */
-#define ADVERTISE_1000HALF      0x0100  /* Advertise 1000BASE-T half duplex */
-
 /* MAC address length */
 #define MAC_ADDR_LEN	6
 
diff --git a/gpxe/src/drivers/net/rtl8139.c b/gpxe/src/drivers/net/rtl8139.c
index 2dff324..754ce60 100644
--- a/gpxe/src/drivers/net/rtl8139.c
+++ b/gpxe/src/drivers/net/rtl8139.c
@@ -12,6 +12,8 @@
 
 */
 
+FILE_LICENCE ( GPL_ANY );
+
 /*********************************************************************/
 /* Revision History                                                  */
 /*********************************************************************/
@@ -277,10 +279,10 @@ static void rtl_init_eeprom ( struct net_device *netdev ) {
 	/* Detect EEPROM type and initialise three-wire device */
 	ee9356 = ( inw ( rtl->ioaddr + RxConfig ) & Eeprom9356 );
 	if ( ee9356 ) {
-		DBG ( "EEPROM is an AT93C56\n" );
+		DBGC ( rtl, "rtl8139 %p EEPROM is an AT93C56\n", rtl );
 		init_at93c56 ( &rtl->eeprom, 16 );
 	} else {
-		DBG ( "EEPROM is an AT93C46\n" );
+		DBGC ( rtl, "rtl8139 %p EEPROM is an AT93C46\n", rtl );
 		init_at93c46 ( &rtl->eeprom, 16 );
 	}
 	rtl->eeprom.bus = &rtl->spibit.bus;
@@ -288,7 +290,8 @@ static void rtl_init_eeprom ( struct net_device *netdev ) {
 	/* Initialise space for non-volatile options, if available */
 	vpd = ( inw ( rtl->ioaddr + Config1 ) & VPDEnable );
 	if ( vpd ) {
-		DBG ( "EEPROM in use for VPD; cannot use for options\n" );
+		DBGC ( rtl, "rtl8139 %p EEPROM in use for VPD; cannot use "
+		       "for options\n", rtl );
 	} else {
 		nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, rtl_nvo_fragments,
 			   &netdev->refcnt );
@@ -331,7 +334,8 @@ static int rtl_open ( struct net_device *netdev ) {
 	if ( ! rtl->rx.ring )
 		return -ENOMEM;
 	outl ( virt_to_bus ( rtl->rx.ring ), rtl->ioaddr + RxBuf );
-	DBG ( "RX ring at %lx\n", virt_to_bus ( rtl->rx.ring ) );
+	DBGC ( rtl, "rtl8139 %p RX ring at %lx\n",
+	       rtl, virt_to_bus ( rtl->rx.ring ) );
 
 	/* Enable TX and RX */
 	outb ( ( CmdRxEnb | CmdTxEnb ), rtl->ioaddr + ChipCmd );
@@ -375,7 +379,7 @@ static int rtl_transmit ( struct net_device *netdev,
 
 	/* Check for space in TX ring */
 	if ( rtl->tx.iobuf[rtl->tx.next] != NULL ) {
-		DBG ( "TX overflow\n" );
+		DBGC ( rtl, "rtl8139 %p TX overflow\n", rtl );
 		return -ENOBUFS;
 	}
 
@@ -383,8 +387,8 @@ static int rtl_transmit ( struct net_device *netdev,
 	iob_pad ( iobuf, ETH_ZLEN );
 
 	/* Add to TX ring */
-	DBG ( "TX id %d at %lx+%zx\n", rtl->tx.next,
-	      virt_to_bus ( iobuf->data ), iob_len ( iobuf ) );
+	DBGC2 ( rtl, "rtl8139 %p TX id %d at %lx+%zx\n", rtl, rtl->tx.next,
+		virt_to_bus ( iobuf->data ), iob_len ( iobuf ) );
 	rtl->tx.iobuf[rtl->tx.next] = iobuf;
 	outl ( virt_to_bus ( iobuf->data ),
 	       rtl->ioaddr + TxAddr0 + 4 * rtl->tx.next );
@@ -420,7 +424,8 @@ static void rtl_poll ( struct net_device *netdev ) {
 	tsad = inw ( rtl->ioaddr + TxSummary );
 	for ( i = 0 ; i < TX_RING_SIZE ; i++ ) {
 		if ( ( rtl->tx.iobuf[i] != NULL ) && ( tsad & ( 1 << i ) ) ) {
-			DBG ( "TX id %d complete\n", i );
+			DBGC2 ( rtl, "rtl8139 %p TX id %d complete\n",
+				rtl, i );
 			netdev_tx_complete ( netdev, rtl->tx.iobuf[i] );
 			rtl->tx.iobuf[i] = NULL;
 		}
@@ -433,8 +438,8 @@ static void rtl_poll ( struct net_device *netdev ) {
 		rx_len = * ( ( uint16_t * )
 			     ( rtl->rx.ring + rtl->rx.offset + 2 ) );
 		if ( rx_status & RxOK ) {
-			DBG ( "RX packet at offset %x+%x\n", rtl->rx.offset,
-			      rx_len );
+			DBGC2 ( rtl, "rtl8139 %p RX packet at offset "
+				"%x+%x\n", rtl, rtl->rx.offset, rx_len );
 
 			rx_iob = alloc_iob ( rx_len );
 			if ( ! rx_iob ) {
@@ -456,8 +461,8 @@ static void rtl_poll ( struct net_device *netdev ) {
 
 			netdev_rx ( netdev, rx_iob );
 		} else {
-			DBG ( "RX bad packet (status %#04x len %d)\n",
-			      rx_status, rx_len );
+			DBGC ( rtl, "rtl8139 %p RX bad packet (status %#04x "
+			       "len %d)\n", rtl, rx_status, rx_len );
 			netdev_rx_err ( netdev, NULL, -EINVAL );
 		}
 		rtl->rx.offset = ( ( ( rtl->rx.offset + 4 + rx_len + 3 ) & ~3 )
@@ -474,7 +479,9 @@ static void rtl_poll ( struct net_device *netdev ) {
  */
 static void rtl_irq ( struct net_device *netdev, int enable ) {
 	struct rtl8139_nic *rtl = netdev->priv;
-	
+
+	DBGC ( rtl, "rtl8139 %p interrupts %s\n",
+	       rtl, ( enable ? "enabled" : "disabled" ) );
 	outw ( ( enable ? ( ROK | RER | TOK | TER ) : 0 ),
 	       rtl->ioaddr + IntrMask );
 }
@@ -518,7 +525,7 @@ static int rtl_probe ( struct pci_device *pci,
 	/* Reset the NIC, set up EEPROM access and read MAC address */
 	rtl_reset ( netdev );
 	rtl_init_eeprom ( netdev );
-	nvs_read ( &rtl->eeprom.nvs, EE_MAC, netdev->ll_addr, ETH_ALEN );
+	nvs_read ( &rtl->eeprom.nvs, EE_MAC, netdev->hw_addr, ETH_ALEN );
 
 	/* Mark as link up; we don't yet handle link state */
 	netdev_link_up ( netdev );
@@ -563,20 +570,20 @@ static void rtl_remove ( struct pci_device *pci ) {
 }
 
 static struct pci_device_id rtl8139_nics[] = {
-PCI_ROM(0x10ec, 0x8129, "rtl8129",       "Realtek 8129"),
-PCI_ROM(0x10ec, 0x8139, "rtl8139",       "Realtek 8139"),
-PCI_ROM(0x10ec, 0x8138, "rtl8139b",      "Realtek 8139B"),
-PCI_ROM(0x1186, 0x1300, "dfe538",        "DFE530TX+/DFE538TX"),
-PCI_ROM(0x1113, 0x1211, "smc1211-1",     "SMC EZ10/100"),
-PCI_ROM(0x1112, 0x1211, "smc1211",       "SMC EZ10/100"),
-PCI_ROM(0x1500, 0x1360, "delta8139",     "Delta Electronics 8139"),
-PCI_ROM(0x4033, 0x1360, "addtron8139",   "Addtron Technology 8139"),
-PCI_ROM(0x1186, 0x1340, "dfe690txd",     "D-Link DFE690TXD"),
-PCI_ROM(0x13d1, 0xab06, "fe2000vx",      "AboCom FE2000VX"),
-PCI_ROM(0x1259, 0xa117, "allied8139",    "Allied Telesyn 8139"),
-PCI_ROM(0x14ea, 0xab06, "fnw3603tx",     "Planex FNW-3603-TX"),
-PCI_ROM(0x14ea, 0xab07, "fnw3800tx",     "Planex FNW-3800-TX"),
-PCI_ROM(0xffff, 0x8139, "clone-rtl8139", "Cloned 8139"),
+PCI_ROM(0x10ec, 0x8129, "rtl8129",       "Realtek 8129", 0),
+PCI_ROM(0x10ec, 0x8139, "rtl8139",       "Realtek 8139", 0),
+PCI_ROM(0x10ec, 0x8138, "rtl8139b",      "Realtek 8139B", 0),
+PCI_ROM(0x1186, 0x1300, "dfe538",        "DFE530TX+/DFE538TX", 0),
+PCI_ROM(0x1113, 0x1211, "smc1211-1",     "SMC EZ10/100", 0),
+PCI_ROM(0x1112, 0x1211, "smc1211",       "SMC EZ10/100", 0),
+PCI_ROM(0x1500, 0x1360, "delta8139",     "Delta Electronics 8139", 0),
+PCI_ROM(0x4033, 0x1360, "addtron8139",   "Addtron Technology 8139", 0),
+PCI_ROM(0x1186, 0x1340, "dfe690txd",     "D-Link DFE690TXD", 0),
+PCI_ROM(0x13d1, 0xab06, "fe2000vx",      "AboCom FE2000VX", 0),
+PCI_ROM(0x1259, 0xa117, "allied8139",    "Allied Telesyn 8139", 0),
+PCI_ROM(0x14ea, 0xab06, "fnw3603tx",     "Planex FNW-3603-TX", 0),
+PCI_ROM(0x14ea, 0xab07, "fnw3800tx",     "Planex FNW-3800-TX", 0),
+PCI_ROM(0xffff, 0x8139, "clone-rtl8139", "Cloned 8139", 0),
 };
 
 struct pci_driver rtl8139_driver __pci_driver = {
diff --git a/gpxe/src/drivers/net/rtl818x/rtl8180.c b/gpxe/src/drivers/net/rtl818x/rtl8180.c
new file mode 100644
index 0000000..472ea20
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl818x/rtl8180.c
@@ -0,0 +1,17 @@
+/* Realtek 8180 card: rtl818x driver + rtl8180 RF modules */
+
+FILE_LICENCE(GPL2_OR_LATER);
+
+#include <gpxe/pci.h>
+
+REQUIRE_OBJECT(rtl818x);
+REQUIRE_OBJECT(rtl8180_grf5101);
+REQUIRE_OBJECT(rtl8180_max2820);
+REQUIRE_OBJECT(rtl8180_sa2400);
+
+static struct pci_device_id rtl8180_nics[] __unused = {
+	PCI_ROM(0x10ec, 0x8180, "rtl8180", "Realtek 8180", 0),
+	PCI_ROM(0x1799, 0x6001, "f5d6001", "Belkin F5D6001", 0),
+	PCI_ROM(0x1799, 0x6020, "f5d6020", "Belkin F5D6020", 0),
+	PCI_ROM(0x1186, 0x3300, "dwl510",  "D-Link DWL-510", 0),
+};
diff --git a/gpxe/src/drivers/net/rtl818x/rtl8180_grf5101.c b/gpxe/src/drivers/net/rtl818x/rtl8180_grf5101.c
new file mode 100644
index 0000000..036a4f8
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl818x/rtl8180_grf5101.c
@@ -0,0 +1,186 @@
+/*
+ * Radio tuning for GCT GRF5101 on RTL8180
+ *
+ * Copyright 2007 Andrea Merello <andreamrl at tiscali.it>
+ *
+ * Modified slightly for gPXE, June 2009 by Joshua Oreman.
+ *
+ * Code from the BSD driver and the rtl8181 project have been
+ * very useful to understand certain things
+ *
+ * I want to thanks the Authors of such projects and the Ndiswrapper
+ * project Authors.
+ *
+ * A special Big Thanks also is for all people who donated me cards,
+ * making possible the creation of the original rtl8180 driver
+ * from which this code is derived!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <unistd.h>
+#include <gpxe/pci.h>
+#include <gpxe/net80211.h>
+
+#include "rtl818x.h"
+
+FILE_LICENCE(GPL2_ONLY);
+
+#define GRF5101_ANTENNA 0xA3
+
+static const int grf5101_encode[] = {
+	0x0, 0x8, 0x4, 0xC,
+	0x2, 0xA, 0x6, 0xE,
+	0x1, 0x9, 0x5, 0xD,
+	0x3, 0xB, 0x7, 0xF
+};
+
+static void write_grf5101(struct net80211_device *dev, u8 addr, u32 data)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u32 phy_config;
+
+	phy_config =  grf5101_encode[(data >> 8) & 0xF];
+	phy_config |= grf5101_encode[(data >> 4) & 0xF] << 4;
+	phy_config |= grf5101_encode[data & 0xF] << 8;
+	phy_config |= grf5101_encode[(addr >> 1) & 0xF] << 12;
+	phy_config |= (addr & 1) << 16;
+	phy_config |= grf5101_encode[(data & 0xf000) >> 12] << 24;
+
+	/* MAC will bang bits to the chip */
+	phy_config |= 0x90000000;
+
+	/* This was originally a 32-bit write to a typecast
+	   RFPinsOutput, but gcc complained about aliasing rules. -JBO */
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, phy_config & 0xffff);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, phy_config >> 16);
+
+	mdelay(3);
+}
+
+static void grf5101_write_phy_antenna(struct net80211_device *dev, short chan)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u8 ant = GRF5101_ANTENNA;
+
+	if (priv->rfparam & RF_PARAM_ANTBDEFAULT)
+		ant |= BB_ANTENNA_B;
+
+	if (chan == 14)
+		ant |= BB_ANTATTEN_CHAN14;
+
+	rtl818x_write_phy(dev, 0x10, ant);
+}
+
+static void grf5101_rf_set_channel(struct net80211_device *dev,
+				   struct net80211_channel *channelp)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int channel = channelp->channel_nr;
+	u32 txpw = priv->txpower[channel - 1] & 0xFF;
+	u32 chan = channel - 1;
+
+	/* set TX power */
+	write_grf5101(dev, 0x15, 0x0);
+	write_grf5101(dev, 0x06, txpw);
+	write_grf5101(dev, 0x15, 0x10);
+	write_grf5101(dev, 0x15, 0x0);
+
+	/* set frequency */
+	write_grf5101(dev, 0x07, 0x0);
+	write_grf5101(dev, 0x0B, chan);
+	write_grf5101(dev, 0x07, 0x1000);
+
+	grf5101_write_phy_antenna(dev, channel);
+}
+
+static void grf5101_rf_stop(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u32 anaparam;
+
+	anaparam = priv->anaparam;
+	anaparam &= 0x000fffff;
+	anaparam |= 0x3f900000;
+	rtl818x_set_anaparam(priv, anaparam);
+
+	write_grf5101(dev, 0x07, 0x0);
+	write_grf5101(dev, 0x1f, 0x45);
+	write_grf5101(dev, 0x1f, 0x5);
+	write_grf5101(dev, 0x00, 0x8e4);
+}
+
+static void grf5101_rf_init(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+
+	rtl818x_set_anaparam(priv, priv->anaparam);
+
+	write_grf5101(dev, 0x1f, 0x0);
+	write_grf5101(dev, 0x1f, 0x0);
+	write_grf5101(dev, 0x1f, 0x40);
+	write_grf5101(dev, 0x1f, 0x60);
+	write_grf5101(dev, 0x1f, 0x61);
+	write_grf5101(dev, 0x1f, 0x61);
+	write_grf5101(dev, 0x00, 0xae4);
+	write_grf5101(dev, 0x1f, 0x1);
+	write_grf5101(dev, 0x1f, 0x41);
+	write_grf5101(dev, 0x1f, 0x61);
+
+	write_grf5101(dev, 0x01, 0x1a23);
+	write_grf5101(dev, 0x02, 0x4971);
+	write_grf5101(dev, 0x03, 0x41de);
+	write_grf5101(dev, 0x04, 0x2d80);
+	write_grf5101(dev, 0x05, 0x68ff);	/* 0x61ff original value */
+	write_grf5101(dev, 0x06, 0x0);
+	write_grf5101(dev, 0x07, 0x0);
+	write_grf5101(dev, 0x08, 0x7533);
+	write_grf5101(dev, 0x09, 0xc401);
+	write_grf5101(dev, 0x0a, 0x0);
+	write_grf5101(dev, 0x0c, 0x1c7);
+	write_grf5101(dev, 0x0d, 0x29d3);
+	write_grf5101(dev, 0x0e, 0x2e8);
+	write_grf5101(dev, 0x10, 0x192);
+	write_grf5101(dev, 0x11, 0x248);
+	write_grf5101(dev, 0x12, 0x0);
+	write_grf5101(dev, 0x13, 0x20c4);
+	write_grf5101(dev, 0x14, 0xf4fc);
+	write_grf5101(dev, 0x15, 0x0);
+	write_grf5101(dev, 0x16, 0x1500);
+
+	write_grf5101(dev, 0x07, 0x1000);
+
+	/* baseband configuration */
+	rtl818x_write_phy(dev, 0, 0xa8);
+	rtl818x_write_phy(dev, 3, 0x0);
+	rtl818x_write_phy(dev, 4, 0xc0);
+	rtl818x_write_phy(dev, 5, 0x90);
+	rtl818x_write_phy(dev, 6, 0x1e);
+	rtl818x_write_phy(dev, 7, 0x64);
+
+	grf5101_write_phy_antenna(dev, 1);
+
+	rtl818x_write_phy(dev, 0x11, 0x88);
+
+	if (rtl818x_ioread8(priv, &priv->map->CONFIG2) &
+	    RTL818X_CONFIG2_ANTENNA_DIV)
+		rtl818x_write_phy(dev, 0x12, 0xc0); /* enable ant diversity */
+	else
+		rtl818x_write_phy(dev, 0x12, 0x40); /* disable ant diversity */
+
+	rtl818x_write_phy(dev, 0x13, 0x90 | priv->csthreshold);
+
+	rtl818x_write_phy(dev, 0x19, 0x0);
+	rtl818x_write_phy(dev, 0x1a, 0xa0);
+	rtl818x_write_phy(dev, 0x1b, 0x44);
+}
+
+struct rtl818x_rf_ops grf5101_rf_ops __rtl818x_rf_driver = {
+	.name		= "GCT GRF5101",
+	.id             = 5,
+	.init		= grf5101_rf_init,
+	.stop		= grf5101_rf_stop,
+	.set_chan	= grf5101_rf_set_channel
+};
diff --git a/gpxe/src/drivers/net/rtl818x/rtl8180_max2820.c b/gpxe/src/drivers/net/rtl818x/rtl8180_max2820.c
new file mode 100644
index 0000000..7c9495f
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl818x/rtl8180_max2820.c
@@ -0,0 +1,158 @@
+/*
+ * Radio tuning for Maxim max2820 on RTL8180
+ *
+ * Copyright 2007 Andrea Merello <andreamrl at tiscali.it>
+ *
+ * Modified slightly for gPXE, June 2009 by Joshua Oreman.
+ *
+ * Code from the BSD driver and the rtl8181 project have been
+ * very useful to understand certain things
+ *
+ * I want to thanks the Authors of such projects and the Ndiswrapper
+ * project Authors.
+ *
+ * A special Big Thanks also is for all people who donated me cards,
+ * making possible the creation of the original rtl8180 driver
+ * from which this code is derived!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <unistd.h>
+#include <gpxe/pci.h>
+#include <gpxe/net80211.h>
+
+#include "rtl818x.h"
+
+FILE_LICENCE(GPL2_ONLY);
+
+#define MAXIM_ANTENNA 0xb3
+
+static const u32 max2820_chan[] = {
+	12, /* CH 1 */
+	17,
+	22,
+	27,
+	32,
+	37,
+	42,
+	47,
+	52,
+	57,
+	62,
+	67,
+	72,
+	84, /* CH 14 */
+};
+
+static void write_max2820(struct net80211_device *dev, u8 addr, u32 data)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u32 phy_config;
+
+	phy_config = 0x90 + (data & 0xf);
+	phy_config <<= 16;
+	phy_config += addr;
+	phy_config <<= 8;
+	phy_config += (data >> 4) & 0xff;
+
+	/* This was originally a 32-bit write to a typecast
+	   RFPinsOutput, but gcc complained about aliasing rules. -JBO */
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, phy_config & 0xffff);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, phy_config >> 16);
+
+	mdelay(1);
+}
+
+static void max2820_write_phy_antenna(struct net80211_device *dev, short chan)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u8 ant;
+
+	ant = MAXIM_ANTENNA;
+	if (priv->rfparam & RF_PARAM_ANTBDEFAULT)
+		ant |= BB_ANTENNA_B;
+	if (chan == 14)
+		ant |= BB_ANTATTEN_CHAN14;
+
+	rtl818x_write_phy(dev, 0x10, ant);
+}
+
+static void max2820_rf_set_channel(struct net80211_device *dev,
+				   struct net80211_channel *channelp)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int channel = channelp->channel_nr;
+	unsigned int chan_idx = channel - 1;
+	u32 txpw = priv->txpower[chan_idx] & 0xFF;
+	u32 chan = max2820_chan[chan_idx];
+
+	/* While philips SA2400 drive the PA bias from
+	 * sa2400, for MAXIM we do this directly from BB */
+	rtl818x_write_phy(dev, 3, txpw);
+
+	max2820_write_phy_antenna(dev, channel);
+	write_max2820(dev, 3, chan);
+}
+
+static void max2820_rf_stop(struct net80211_device *dev)
+{
+	rtl818x_write_phy(dev, 3, 0x8);
+	write_max2820(dev, 1, 0);
+}
+
+
+static void max2820_rf_init(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+
+	/* MAXIM from netbsd driver */
+	write_max2820(dev, 0, 0x007); /* test mode as indicated in datasheet */
+	write_max2820(dev, 1, 0x01e); /* enable register */
+	write_max2820(dev, 2, 0x001); /* synt register */
+
+	max2820_rf_set_channel(dev, NULL);
+
+	write_max2820(dev, 4, 0x313); /* rx register */
+
+	/* PA is driven directly by the BB, we keep the MAXIM bias
+	 * at the highest value in case that setting it to lower
+	 * values may introduce some further attenuation somewhere..
+	 */
+	write_max2820(dev, 5, 0x00f);
+
+	/* baseband configuration */
+	rtl818x_write_phy(dev, 0, 0x88); /* sys1       */
+	rtl818x_write_phy(dev, 3, 0x08); /* txagc      */
+	rtl818x_write_phy(dev, 4, 0xf8); /* lnadet     */
+	rtl818x_write_phy(dev, 5, 0x90); /* ifagcinit  */
+	rtl818x_write_phy(dev, 6, 0x1a); /* ifagclimit */
+	rtl818x_write_phy(dev, 7, 0x64); /* ifagcdet   */
+
+	max2820_write_phy_antenna(dev, 1);
+
+	rtl818x_write_phy(dev, 0x11, 0x88); /* trl */
+
+	if (rtl818x_ioread8(priv, &priv->map->CONFIG2) &
+	    RTL818X_CONFIG2_ANTENNA_DIV)
+		rtl818x_write_phy(dev, 0x12, 0xc7);
+	else
+		rtl818x_write_phy(dev, 0x12, 0x47);
+
+	rtl818x_write_phy(dev, 0x13, 0x9b);
+
+	rtl818x_write_phy(dev, 0x19, 0x0);  /* CHESTLIM */
+	rtl818x_write_phy(dev, 0x1a, 0x9f); /* CHSQLIM  */
+
+	max2820_rf_set_channel(dev, NULL);
+}
+
+struct rtl818x_rf_ops max2820_rf_ops __rtl818x_rf_driver = {
+	.name		= "Maxim max2820",
+	.id		= 4,
+	.init		= max2820_rf_init,
+	.stop		= max2820_rf_stop,
+	.set_chan	= max2820_rf_set_channel
+};
diff --git a/gpxe/src/drivers/net/rtl818x/rtl8180_sa2400.c b/gpxe/src/drivers/net/rtl818x/rtl8180_sa2400.c
new file mode 100644
index 0000000..bcfd397
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl818x/rtl8180_sa2400.c
@@ -0,0 +1,217 @@
+/*
+ * Radio tuning for Philips SA2400 on RTL8180
+ *
+ * Copyright 2007 Andrea Merello <andreamrl at tiscali.it>
+ *
+ * Modified slightly for gPXE, June 2009 by Joshua Oreman.
+ *
+ * Code from the BSD driver and the rtl8181 project have been
+ * very useful to understand certain things
+ *
+ * I want to thanks the Authors of such projects and the Ndiswrapper
+ * project Authors.
+ *
+ * A special Big Thanks also is for all people who donated me cards,
+ * making possible the creation of the original rtl8180 driver
+ * from which this code is derived!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <unistd.h>
+#include <gpxe/pci.h>
+#include <gpxe/net80211.h>
+
+#include "rtl818x.h"
+
+FILE_LICENCE(GPL2_ONLY);
+
+#define SA2400_ANTENNA 0x91
+#define SA2400_DIG_ANAPARAM_PWR1_ON 0x8
+#define SA2400_ANA_ANAPARAM_PWR1_ON 0x28
+#define SA2400_ANAPARAM_PWR0_ON 0x3
+
+/* RX sensitivity in dbm */
+#define SA2400_MAX_SENS 85
+
+#define SA2400_REG4_FIRDAC_SHIFT 7
+
+static const u32 sa2400_chan[] = {
+	0x00096c, /* ch1 */
+	0x080970,
+	0x100974,
+	0x180978,
+	0x000980,
+	0x080984,
+	0x100988,
+	0x18098c,
+	0x000994,
+	0x080998,
+	0x10099c,
+	0x1809a0,
+	0x0009a8,
+	0x0009b4, /* ch 14 */
+};
+
+static void write_sa2400(struct net80211_device *dev, u8 addr, u32 data)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u32 phy_config;
+
+	/* MAC will bang bits to the sa2400. sw 3-wire is NOT used */
+	phy_config = 0xb0000000;
+
+	phy_config |= ((u32)(addr & 0xf)) << 24;
+	phy_config |= data & 0xffffff;
+
+	/* This was originally a 32-bit write to a typecast
+	   RFPinsOutput, but gcc complained about aliasing rules. -JBO */
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, phy_config & 0xffff);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, phy_config >> 16);
+
+	mdelay(3);
+}
+
+static void sa2400_write_phy_antenna(struct net80211_device *dev, short chan)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u8 ant = SA2400_ANTENNA;
+
+	if (priv->rfparam & RF_PARAM_ANTBDEFAULT)
+		ant |= BB_ANTENNA_B;
+
+	if (chan == 14)
+		ant |= BB_ANTATTEN_CHAN14;
+
+	rtl818x_write_phy(dev, 0x10, ant);
+
+}
+
+static void sa2400_rf_set_channel(struct net80211_device *dev,
+				  struct net80211_channel *channelp)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int channel = channelp->channel_nr;
+	u32 txpw = priv->txpower[channel - 1] & 0xFF;
+	u32 chan = sa2400_chan[channel - 1];
+
+	write_sa2400(dev, 7, txpw);
+
+	sa2400_write_phy_antenna(dev, channel);
+
+	write_sa2400(dev, 0, chan);
+	write_sa2400(dev, 1, 0xbb50);
+	write_sa2400(dev, 2, 0x80);
+	write_sa2400(dev, 3, 0);
+}
+
+static void sa2400_rf_stop(struct net80211_device *dev)
+{
+	write_sa2400(dev, 4, 0);
+}
+
+static void sa2400_rf_init(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u32 anaparam, txconf;
+	u8 firdac;
+	int analogphy = priv->rfparam & RF_PARAM_ANALOGPHY;
+
+	anaparam = priv->anaparam;
+	anaparam &= ~(1 << ANAPARAM_TXDACOFF_SHIFT);
+	anaparam &= ~ANAPARAM_PWR1_MASK;
+	anaparam &= ~ANAPARAM_PWR0_MASK;
+
+	if (analogphy) {
+		anaparam |= SA2400_ANA_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT;
+		firdac = 0;
+	} else {
+		anaparam |= (SA2400_DIG_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT);
+		anaparam |= (SA2400_ANAPARAM_PWR0_ON << ANAPARAM_PWR0_SHIFT);
+		firdac = 1 << SA2400_REG4_FIRDAC_SHIFT;
+	}
+
+	rtl818x_set_anaparam(priv, anaparam);
+
+	write_sa2400(dev, 0, sa2400_chan[0]);
+	write_sa2400(dev, 1, 0xbb50);
+	write_sa2400(dev, 2, 0x80);
+	write_sa2400(dev, 3, 0);
+	write_sa2400(dev, 4, 0x19340 | firdac);
+	write_sa2400(dev, 5, 0x1dfb | (SA2400_MAX_SENS - 54) << 15);
+	write_sa2400(dev, 4, 0x19348 | firdac); /* calibrate VCO */
+
+	if (!analogphy)
+		write_sa2400(dev, 4, 0x1938c); /*???*/
+
+	write_sa2400(dev, 4, 0x19340 | firdac);
+
+	write_sa2400(dev, 0, sa2400_chan[0]);
+	write_sa2400(dev, 1, 0xbb50);
+	write_sa2400(dev, 2, 0x80);
+	write_sa2400(dev, 3, 0);
+	write_sa2400(dev, 4, 0x19344 | firdac); /* calibrate filter */
+
+	/* new from rtl8180 embedded driver (rtl8181 project) */
+	write_sa2400(dev, 6, 0x13ff | (1 << 23)); /* MANRX */
+	write_sa2400(dev, 8, 0); /* VCO */
+
+	if (analogphy) {
+		rtl818x_set_anaparam(priv, anaparam |
+				     (1 << ANAPARAM_TXDACOFF_SHIFT));
+
+		txconf = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+		rtl818x_iowrite32(priv, &priv->map->TX_CONF,
+			txconf | RTL818X_TX_CONF_LOOPBACK_CONT);
+
+		write_sa2400(dev, 4, 0x19341); /* calibrates DC */
+
+		/* a 5us delay is required here,
+		 * we rely on the 3ms delay introduced in write_sa2400 */
+
+		write_sa2400(dev, 4, 0x19345);
+
+		/* a 20us delay is required here,
+		 * we rely on the 3ms delay introduced in write_sa2400 */
+
+		rtl818x_iowrite32(priv, &priv->map->TX_CONF, txconf);
+
+		rtl818x_set_anaparam(priv, anaparam);
+	}
+	/* end new code */
+
+	write_sa2400(dev, 4, 0x19341 | firdac); /* RTX MODE */
+
+	/* baseband configuration */
+	rtl818x_write_phy(dev, 0, 0x98);
+	rtl818x_write_phy(dev, 3, 0x38);
+	rtl818x_write_phy(dev, 4, 0xe0);
+	rtl818x_write_phy(dev, 5, 0x90);
+	rtl818x_write_phy(dev, 6, 0x1a);
+	rtl818x_write_phy(dev, 7, 0x64);
+
+	sa2400_write_phy_antenna(dev, 1);
+
+	rtl818x_write_phy(dev, 0x11, 0x80);
+
+	if (rtl818x_ioread8(priv, &priv->map->CONFIG2) &
+	    RTL818X_CONFIG2_ANTENNA_DIV)
+		rtl818x_write_phy(dev, 0x12, 0xc7); /* enable ant diversity */
+	else
+		rtl818x_write_phy(dev, 0x12, 0x47); /* disable ant diversity */
+
+	rtl818x_write_phy(dev, 0x13, 0x90 | priv->csthreshold);
+
+	rtl818x_write_phy(dev, 0x19, 0x0);
+	rtl818x_write_phy(dev, 0x1a, 0xa0);
+}
+
+struct rtl818x_rf_ops sa2400_rf_ops __rtl818x_rf_driver = {
+	.name		= "Philips SA2400",
+	.id		= 3,
+	.init		= sa2400_rf_init,
+	.stop		= sa2400_rf_stop,
+	.set_chan	= sa2400_rf_set_channel
+};
diff --git a/gpxe/src/drivers/net/rtl818x/rtl8185.c b/gpxe/src/drivers/net/rtl818x/rtl8185.c
new file mode 100644
index 0000000..bfa5ab5
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl818x/rtl8185.c
@@ -0,0 +1,14 @@
+/* Realtek 8185 card: rtl818x driver + rtl8185_rtl8225 RF module */
+
+FILE_LICENCE(GPL2_OR_LATER);
+
+#include <gpxe/pci.h>
+
+REQUIRE_OBJECT(rtl818x);
+REQUIRE_OBJECT(rtl8185_rtl8225);
+
+static struct pci_device_id rtl8185_nics[] __unused = {
+	PCI_ROM(0x10ec, 0x8185, "rtl8185", "Realtek 8185", 0),
+	PCI_ROM(0x1799, 0x700f, "f5d7000", "Belkin F5D7000", 0),
+	PCI_ROM(0x1799, 0x701f, "f5d7010", "Belkin F5D7010", 0),
+};
diff --git a/gpxe/src/drivers/net/rtl818x/rtl8185_rtl8225.c b/gpxe/src/drivers/net/rtl818x/rtl8185_rtl8225.c
new file mode 100644
index 0000000..b3fc186
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl818x/rtl8185_rtl8225.c
@@ -0,0 +1,804 @@
+/*
+ * Radio tuning for RTL8225 on RTL8185
+ *
+ * Copyright 2007 Michael Wu <flamingice at sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl at tiscali.it>
+ *
+ * Modified slightly for gPXE, June 2009 by Joshua Oreman
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2005 Andrea Merello <andreamrl at tiscali.it>, et al.
+ *
+ * Thanks to Realtek for their support!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <unistd.h>
+#include <gpxe/pci.h>
+#include <gpxe/net80211.h>
+
+#include "rtl818x.h"
+
+FILE_LICENCE(GPL2_ONLY);
+
+#define RTL8225_ANAPARAM_ON	0xa0000b59
+#define RTL8225_ANAPARAM2_ON	0x860dec11
+#define RTL8225_ANAPARAM_OFF	0xa00beb59
+#define RTL8225_ANAPARAM2_OFF	0x840dec11
+
+#define min(a,b) (((a)<(b))?(a):(b))
+#define ARRAY_SIZE(a) (int)(sizeof(a)/sizeof((a)[0]))
+
+static inline void rtl8225_write_phy_ofdm(struct net80211_device *dev,
+					  u8 addr, u8 data)
+{
+	rtl818x_write_phy(dev, addr, data);
+}
+
+static inline void rtl8225_write_phy_cck(struct net80211_device *dev,
+					 u8 addr, u8 data)
+{
+	rtl818x_write_phy(dev, addr, data | 0x10000);
+}
+
+static void rtl8225_write(struct net80211_device *dev, u8 addr, u16 data)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u16 reg80, reg84, reg82;
+	u32 bangdata;
+	int i;
+
+	bangdata = (data << 4) | (addr & 0xf);
+
+	reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3;
+	reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7);
+
+	reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400);
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(10);
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(2);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(10);
+
+	for (i = 15; i >= 0; i--) {
+		u16 reg = reg80 | !!(bangdata & (1 << i));
+
+		if (i & 1)
+			rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
+
+		if (!(i & 1))
+			rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+	}
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(10);
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+	rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+}
+
+static u16 rtl8225_read(struct net80211_device *dev, u8 addr)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u16 reg80, reg82, reg84, out;
+	int i;
+
+	reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
+	reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
+	reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400;
+
+	reg80 &= ~0xF;
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F);
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(4);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(5);
+
+	for (i = 4; i >= 0; i--) {
+		u16 reg = reg80 | ((addr >> i) & 1);
+
+		if (!(i & 1)) {
+			rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+			rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+			udelay(1);
+		}
+
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+				  reg | (1 << 1));
+		rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+		udelay(2);
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+				  reg | (1 << 1));
+		rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+		udelay(2);
+
+		if (i & 1) {
+			rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+			rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+			udelay(1);
+		}
+	}
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E);
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+			  reg80 | (1 << 3) | (1 << 1));
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(2);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+			  reg80 | (1 << 3));
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(2);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+			  reg80 | (1 << 3));
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(2);
+
+	out = 0;
+	for (i = 11; i >= 0; i--) {
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+				  reg80 | (1 << 3));
+		rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+		udelay(1);
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+				  reg80 | (1 << 3) | (1 << 1));
+		rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+		udelay(2);
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+				  reg80 | (1 << 3) | (1 << 1));
+		rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+		udelay(2);
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+				  reg80 | (1 << 3) | (1 << 1));
+		rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+		udelay(2);
+
+		if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1))
+			out |= 1 << i;
+
+		rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+				  reg80 | (1 << 3));
+		rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+		udelay(2);
+	}
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+			  reg80 | (1 << 3) | (1 << 2));
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(2);
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0);
+
+	return out;
+}
+
+static const u16 rtl8225bcd_rxgain[] = {
+	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
+	0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
+	0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
+	0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
+	0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
+	0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
+	0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
+	0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
+	0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
+	0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
+	0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
+	0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
+};
+
+static const u8 rtl8225_agc[] = {
+	0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+	0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96,
+	0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e,
+	0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86,
+	0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e,
+	0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36,
+	0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e,
+	0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26,
+	0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e,
+	0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16,
+	0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
+	0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
+	0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+static const u8 rtl8225_gain[] = {
+	0x23, 0x88, 0x7c, 0xa5, /* -82dbm */
+	0x23, 0x88, 0x7c, 0xb5, /* -82dbm */
+	0x23, 0x88, 0x7c, 0xc5, /* -82dbm */
+	0x33, 0x80, 0x79, 0xc5, /* -78dbm */
+	0x43, 0x78, 0x76, 0xc5, /* -74dbm */
+	0x53, 0x60, 0x73, 0xc5, /* -70dbm */
+	0x63, 0x58, 0x70, 0xc5, /* -66dbm */
+};
+
+static const u8 rtl8225_threshold[] = {
+	0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd
+};
+
+static const u8 rtl8225_tx_gain_cck_ofdm[] = {
+	0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
+};
+
+static const u8 rtl8225_tx_power_cck[] = {
+	0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
+	0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
+	0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
+	0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
+	0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
+	0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
+};
+
+static const u8 rtl8225_tx_power_cck_ch14[] = {
+	0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
+	0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
+	0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
+	0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
+	0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
+	0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 rtl8225_tx_power_ofdm[] = {
+	0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
+};
+
+static const u32 rtl8225_chan[] = {
+	0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c,
+	0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72
+};
+
+static void rtl8225_rf_set_tx_power(struct net80211_device *dev, int channel)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u8 cck_power, ofdm_power;
+	const u8 *tmp;
+	u32 reg;
+	int i;
+
+	cck_power = priv->txpower[channel - 1] & 0xFF;
+	ofdm_power = priv->txpower[channel - 1] >> 8;
+
+	cck_power = min(cck_power, (u8)35);
+	ofdm_power = min(ofdm_power, (u8)35);
+
+	rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
+			 rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1);
+
+	if (channel == 14)
+		tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8];
+	else
+		tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8];
+
+	for (i = 0; i < 8; i++)
+		rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+	mdelay(1); /* FIXME: optional? */
+
+	/* anaparam2 on */
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+	reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+	rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+	rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
+			 rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1);
+
+	tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6];
+
+	rtl8225_write_phy_ofdm(dev, 5, *tmp);
+	rtl8225_write_phy_ofdm(dev, 7, *tmp);
+
+	mdelay(1);
+}
+
+static void rtl8225_rf_init(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int i;
+
+	rtl818x_set_anaparam(priv, RTL8225_ANAPARAM_ON);
+
+	/* host_pci_init */
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
+	rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	mdelay(200);	/* FIXME: ehh?? */
+	rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6));
+
+	rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008);
+
+	/* TODO: check if we need really to change BRSR to do RF config */
+	rtl818x_ioread16(priv, &priv->map->BRSR);
+	rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
+	rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+	rtl8225_write(dev, 0x0, 0x067);
+	rtl8225_write(dev, 0x1, 0xFE0);
+	rtl8225_write(dev, 0x2, 0x44D);
+	rtl8225_write(dev, 0x3, 0x441);
+	rtl8225_write(dev, 0x4, 0x8BE);
+	rtl8225_write(dev, 0x5, 0xBF0);		/* TODO: minipci */
+	rtl8225_write(dev, 0x6, 0xAE6);
+	rtl8225_write(dev, 0x7, rtl8225_chan[0]);
+	rtl8225_write(dev, 0x8, 0x01F);
+	rtl8225_write(dev, 0x9, 0x334);
+	rtl8225_write(dev, 0xA, 0xFD4);
+	rtl8225_write(dev, 0xB, 0x391);
+	rtl8225_write(dev, 0xC, 0x050);
+	rtl8225_write(dev, 0xD, 0x6DB);
+	rtl8225_write(dev, 0xE, 0x029);
+	rtl8225_write(dev, 0xF, 0x914); mdelay(1);
+
+	rtl8225_write(dev, 0x2, 0xC4D); mdelay(100);
+
+	rtl8225_write(dev, 0x0, 0x127);
+
+	for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) {
+		rtl8225_write(dev, 0x1, i + 1);
+		rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]);
+	}
+
+	rtl8225_write(dev, 0x0, 0x027);
+	rtl8225_write(dev, 0x0, 0x22F);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+
+	for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
+		rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
+		mdelay(1);
+		rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
+		mdelay(1);
+	}
+
+	mdelay(1);
+
+	rtl8225_write_phy_ofdm(dev, 0x00, 0x01); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x01, 0x02); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x02, 0x62); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x03, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x04, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x05, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x06, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x07, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x08, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x10, 0x84); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x11, 0x03); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x12, 0x20); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x13, 0x20); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x14, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x15, 0x40); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x16, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x17, 0x40); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x18, 0xef); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x19, 0x19); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x21, 0x27); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x22, 0x16); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x24, 0x46); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x25, 0x20); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x26, 0x90); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x27, 0x88); mdelay(1);
+
+	rtl8225_write_phy_cck(dev, 0x00, 0x98); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x03, 0x20); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x04, 0x7e); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x05, 0x12); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x06, 0xfc); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x07, 0x78); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x08, 0x2e); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x10, 0x93); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x11, 0x88); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x12, 0x47); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x13, 0xd0);
+	rtl8225_write_phy_cck(dev, 0x19, 0x00);
+	rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
+	rtl8225_write_phy_cck(dev, 0x1b, 0x08);
+	rtl8225_write_phy_cck(dev, 0x40, 0x86);
+	rtl8225_write_phy_cck(dev, 0x41, 0x8d); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x42, 0x15); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x43, 0x18); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x44, 0x1f); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x45, 0x1e); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x46, 0x1a); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x47, 0x15); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x48, 0x10); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x49, 0x0a); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x4a, 0x05); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x4b, 0x02); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x4c, 0x05); mdelay(1);
+
+	rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); mdelay(1);
+
+	rtl8225_rf_set_tx_power(dev, 1);
+
+	/* RX antenna default to A */
+	rtl8225_write_phy_cck(dev, 0x10, 0x9b); mdelay(1);	/* B: 0xDB */
+	rtl8225_write_phy_ofdm(dev, 0x26, 0x90); mdelay(1);	/* B: 0x10 */
+
+	rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);	/* B: 0x00 */
+	mdelay(1);
+	rtl818x_iowrite32(priv, (u32 *)((u8 *)priv->map + 0x94), 0x15c00002);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+
+	rtl8225_write(dev, 0x0c, 0x50);
+	/* set OFDM initial gain */
+	rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]);
+	rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]);
+	rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]);
+	rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]);
+	/* set CCK threshold */
+	rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]);
+}
+
+static const u8 rtl8225z2_tx_power_cck_ch14[] = {
+	0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 rtl8225z2_tx_power_cck_B[] = {
+	0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04
+};
+
+static const u8 rtl8225z2_tx_power_cck_A[] = {
+	0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04
+};
+
+static const u8 rtl8225z2_tx_power_cck[] = {
+	0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
+};
+
+static void rtl8225z2_rf_set_tx_power(struct net80211_device *dev, int channel)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u8 cck_power, ofdm_power;
+	const u8 *tmp;
+	int i;
+
+	cck_power = priv->txpower[channel - 1] & 0xFF;
+	ofdm_power = priv->txpower[channel - 1] >> 8;
+
+	if (channel == 14)
+		tmp = rtl8225z2_tx_power_cck_ch14;
+	else if (cck_power == 12)
+		tmp = rtl8225z2_tx_power_cck_B;
+	else if (cck_power == 13)
+		tmp = rtl8225z2_tx_power_cck_A;
+	else
+		tmp = rtl8225z2_tx_power_cck;
+
+	for (i = 0; i < 8; i++)
+		rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+	cck_power = min(cck_power, (u8)35);
+	if (cck_power == 13 || cck_power == 14)
+		cck_power = 12;
+	if (cck_power >= 15)
+		cck_power -= 2;
+
+	rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power);
+	rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK);
+	mdelay(1);
+
+	ofdm_power = min(ofdm_power, (u8)35);
+	rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power);
+
+	rtl8225_write_phy_ofdm(dev, 2, 0x62);
+	rtl8225_write_phy_ofdm(dev, 5, 0x00);
+	rtl8225_write_phy_ofdm(dev, 6, 0x40);
+	rtl8225_write_phy_ofdm(dev, 7, 0x00);
+	rtl8225_write_phy_ofdm(dev, 8, 0x40);
+
+	mdelay(1);
+}
+
+static const u16 rtl8225z2_rxgain[] = {
+	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009,
+	0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141,
+	0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183,
+	0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244,
+	0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288,
+	0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345,
+	0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389,
+	0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393,
+	0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d,
+	0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9,
+	0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
+	0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
+};
+
+static void rtl8225z2_rf_init(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int i;
+
+	rtl818x_set_anaparam(priv, RTL8225_ANAPARAM_ON);
+
+	/* host_pci_init */
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
+	rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	mdelay(200);	/* FIXME: ehh?? */
+	rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6));
+
+	rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008);
+
+	/* TODO: check if we need really to change BRSR to do RF config */
+	rtl818x_ioread16(priv, &priv->map->BRSR);
+	rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
+	rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+
+	rtl8225_write(dev, 0x0, 0x0B7); mdelay(1);
+	rtl8225_write(dev, 0x1, 0xEE0); mdelay(1);
+	rtl8225_write(dev, 0x2, 0x44D); mdelay(1);
+	rtl8225_write(dev, 0x3, 0x441); mdelay(1);
+	rtl8225_write(dev, 0x4, 0x8C3); mdelay(1);
+	rtl8225_write(dev, 0x5, 0xC72); mdelay(1);
+	rtl8225_write(dev, 0x6, 0x0E6); mdelay(1);
+	rtl8225_write(dev, 0x7, 0x82A); mdelay(1);
+	rtl8225_write(dev, 0x8, 0x03F); mdelay(1);
+	rtl8225_write(dev, 0x9, 0x335); mdelay(1);
+	rtl8225_write(dev, 0xa, 0x9D4); mdelay(1);
+	rtl8225_write(dev, 0xb, 0x7BB); mdelay(1);
+	rtl8225_write(dev, 0xc, 0x850); mdelay(1);
+	rtl8225_write(dev, 0xd, 0xCDF); mdelay(1);
+	rtl8225_write(dev, 0xe, 0x02B); mdelay(1);
+	rtl8225_write(dev, 0xf, 0x114); mdelay(100);
+
+	if (!(rtl8225_read(dev, 6) & (1 << 7))) {
+		rtl8225_write(dev, 0x02, 0x0C4D);
+		mdelay(200);
+		rtl8225_write(dev, 0x02, 0x044D);
+		mdelay(100);
+		/* TODO: readd calibration failure message when the calibration
+		   check works */
+	}
+
+	rtl8225_write(dev, 0x0, 0x1B7);
+	rtl8225_write(dev, 0x3, 0x002);
+	rtl8225_write(dev, 0x5, 0x004);
+
+	for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
+		rtl8225_write(dev, 0x1, i + 1);
+		rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]);
+	}
+
+	rtl8225_write(dev, 0x0, 0x0B7); mdelay(100);
+	rtl8225_write(dev, 0x2, 0xC4D);
+
+	mdelay(200);
+	rtl8225_write(dev, 0x2, 0x44D);
+	mdelay(100);
+
+	rtl8225_write(dev, 0x00, 0x2BF);
+	rtl8225_write(dev, 0xFF, 0xFFFF);
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+
+	for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
+		rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
+		mdelay(1);
+		rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
+		mdelay(1);
+	}
+
+	mdelay(1);
+
+	rtl8225_write_phy_ofdm(dev, 0x00, 0x01); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x01, 0x02); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x02, 0x62); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x03, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x04, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x05, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x06, 0x40); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x07, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x08, 0x40); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x18, 0xef); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0d, 0x43);
+	rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x10, 0x84); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x11, 0x06); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x12, 0x20); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x13, 0x20); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x14, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x15, 0x40); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x16, 0x00); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x17, 0x40); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x18, 0xef); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x19, 0x19); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x21, 0x27); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x22, 0x16); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x23, 0x80); mdelay(1); /* FIXME: not needed? */
+	rtl8225_write_phy_ofdm(dev, 0x24, 0x46); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x25, 0x20); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x26, 0x90); mdelay(1);
+	rtl8225_write_phy_ofdm(dev, 0x27, 0x88); mdelay(1);
+
+	rtl8225_write_phy_cck(dev, 0x00, 0x98); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x03, 0x20); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x04, 0x7e); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x05, 0x12); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x06, 0xfc); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x07, 0x78); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x08, 0x2e); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x10, 0x93); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x11, 0x88); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x12, 0x47); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x13, 0xd0);
+	rtl8225_write_phy_cck(dev, 0x19, 0x00);
+	rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
+	rtl8225_write_phy_cck(dev, 0x1b, 0x08);
+	rtl8225_write_phy_cck(dev, 0x40, 0x86);
+	rtl8225_write_phy_cck(dev, 0x41, 0x8a); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x42, 0x15); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x43, 0x18); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x44, 0x36); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x45, 0x35); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x46, 0x2e); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x47, 0x25); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x48, 0x1c); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x49, 0x12); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x4a, 0x09); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x4b, 0x04); mdelay(1);
+	rtl8225_write_phy_cck(dev, 0x4c, 0x05); mdelay(1);
+
+	rtl818x_iowrite8(priv, (u8 *)priv->map + 0x5B, 0x0D); mdelay(1);
+
+	rtl8225z2_rf_set_tx_power(dev, 1);
+
+	/* RX antenna default to A */
+	rtl8225_write_phy_cck(dev, 0x10, 0x9b); mdelay(1);	/* B: 0xDB */
+	rtl8225_write_phy_ofdm(dev, 0x26, 0x90); mdelay(1);	/* B: 0x10 */
+
+	rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);	/* B: 0x00 */
+	mdelay(1);
+	rtl818x_iowrite32(priv, (u32 *)((u8 *)priv->map + 0x94), 0x15c00002);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+}
+
+static void rtl8225x_rf_init(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u16 reg8, reg9;
+
+	rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
+	rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	mdelay(100);
+
+	rtl8225_write(dev, 0, 0x1B7);
+
+	reg8 = rtl8225_read(dev, 8);
+	reg9 = rtl8225_read(dev, 9);
+
+	rtl8225_write(dev, 0, 0x0B7);
+
+	if (reg8 != 0x588 || reg9 != 0x700) {
+		priv->rf_flag = 0;
+		rtl8225_rf_init(dev);
+	} else {
+		priv->rf_flag = 1;
+		rtl8225z2_rf_init(dev);
+	}
+}
+
+static void rtl8225_rf_stop(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u8 reg;
+
+	rtl8225_write(dev, 0x4, 0x1f); mdelay(1);
+
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+	reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+	rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF);
+	rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+}
+
+static void rtl8225_rf_set_channel(struct net80211_device *dev,
+				   struct net80211_channel *channelp)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int chan = channelp->channel_nr;
+
+	if (priv->rf_flag)
+		rtl8225z2_rf_set_tx_power(dev, chan);
+	else
+		rtl8225_rf_set_tx_power(dev, chan);
+
+	rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
+	mdelay(10);
+}
+
+static void rtl8225_rf_conf_erp(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+
+	if (dev->phy_flags & NET80211_PHY_USE_SHORT_SLOT) {
+		rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9);
+		rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
+		rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14);
+		rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
+		rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73);
+	} else {
+		rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14);
+		rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44);
+		rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24);
+		rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
+		rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5);
+	}
+}
+
+struct rtl818x_rf_ops rtl8225_ops __rtl818x_rf_driver = {
+	.name		= "rtl8225",
+	.id		= 9,
+	.init		= rtl8225x_rf_init,
+	.stop		= rtl8225_rf_stop,
+	.set_chan	= rtl8225_rf_set_channel,
+	.conf_erp	= rtl8225_rf_conf_erp,
+};
diff --git a/gpxe/src/drivers/net/rtl818x/rtl818x.c b/gpxe/src/drivers/net/rtl818x/rtl818x.c
new file mode 100644
index 0000000..7c7ca48
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl818x/rtl818x.c
@@ -0,0 +1,855 @@
+
+/*
+ * Linux device driver for RTL8180 / RTL8185
+ *
+ * Copyright 2007 Michael Wu <flamingice at sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl at tiscali.it>
+ *
+ * Modified for gPXE, June 2009, by Joshua Oreman <oremanj at rwcr.net>
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andreamrl at tiscali.it>, et al.
+ *
+ * Thanks to Realtek for their support!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+FILE_LICENCE(GPL2_ONLY);
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/pci.h>
+#include <gpxe/net80211.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/threewire.h>
+
+#include "rtl818x.h"
+
+/* rtl818x_rates[hw rate number] = rate in 100kbps units */
+static const u16 rtl818x_rates[] = {
+	10, 20, 55, 110, /* 802.11b */
+	60, 90, 120, 180, 240, 360, 480, 540, /* 802.11g */
+	0, 0, 0, 0,		/* index safely using a value masked with 0xF */
+};
+#define RTL818X_NR_B_RATES  4
+#define RTL818X_NR_RATES    12
+
+/* used by RF drivers */
+void rtl818x_write_phy(struct net80211_device *dev, u8 addr, u32 data)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int i = 10;
+	u32 buf;
+
+	buf = (data << 8) | addr;
+
+	rtl818x_iowrite32(priv, (u32 *)&priv->map->PHY[0], buf | 0x80);
+	while (i--) {
+		rtl818x_iowrite32(priv, (u32 *)&priv->map->PHY[0], buf);
+		if (rtl818x_ioread8(priv, &priv->map->PHY[2]) == (data & 0xFF))
+			return;
+	}
+}
+
+static void rtl818x_handle_rx(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	unsigned int count = RTL818X_RX_RING_SIZE;
+
+	while (count--) {
+		struct rtl818x_rx_desc *entry = &priv->rx_ring[priv->rx_idx];
+		struct io_buffer *iob = priv->rx_buf[priv->rx_idx];
+		u32 flags = le32_to_cpu(entry->flags);
+
+		if (flags & RTL818X_RX_DESC_FLAG_OWN)
+			return;
+
+		if (flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL |
+			     RTL818X_RX_DESC_FLAG_FOF |
+			     RTL818X_RX_DESC_FLAG_RX_ERR)) {
+			/* This is crappy hardware. The Linux driver
+			   doesn't even log these. */
+			goto done;
+		} else if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) {
+			/* This is actually a corrupt packet. */
+			DBG2("rtl818x RX:%d CRC fail: flags %08x\n",
+			     priv->rx_idx, flags);
+			net80211_rx_err(dev, NULL, EIO);
+		} else {
+			u32 flags2 = le32_to_cpu(entry->flags2);
+			struct io_buffer *new_iob = alloc_iob(MAX_RX_SIZE);
+			if (!new_iob) {
+				net80211_rx_err(dev, NULL, ENOMEM);
+				goto done;
+			}
+
+			DBGP("rtl818x RX:%d success: flags %08x %08x\n",
+			     priv->rx_idx, flags, flags2);
+
+			iob_put(iob, flags & 0xFFF);
+
+			net80211_rx(dev, iob, (flags2 >> 8) & 0x7f,
+				    rtl818x_rates[(flags >> 20) & 0xf]);
+
+			iob = new_iob;
+			priv->rx_buf[priv->rx_idx] = iob;
+		}
+
+	done:
+		entry->rx_buf = cpu_to_le32(virt_to_bus(iob->data));
+		entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | MAX_RX_SIZE);
+
+		if (priv->rx_idx == RTL818X_RX_RING_SIZE - 1)
+			entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR);
+
+		priv->rx_idx = (priv->rx_idx + 1) % RTL818X_RX_RING_SIZE;
+	}
+}
+
+static void rtl818x_handle_tx(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	unsigned int count = RTL818X_TX_RING_SIZE;
+
+	while (count--) {
+		struct rtl818x_tx_desc *entry = &priv->tx_ring[priv->tx_cons];
+		struct io_buffer *iob = priv->tx_buf[priv->tx_cons];
+		u32 flags = le32_to_cpu(entry->flags);
+		int rc;
+
+		if ((flags & RTL818X_TX_DESC_FLAG_OWN) || !iob)
+			return;
+
+		rc = 0;
+		if (!(flags & RTL818X_TX_DESC_FLAG_TX_OK)) {
+			/* our packet was not ACKed properly */
+			rc = EIO;
+		}
+
+		net80211_tx_complete(dev, iob, flags & 0xFF, rc);
+
+		priv->tx_buf[priv->tx_cons] = NULL;
+		priv->tx_cons = (priv->tx_cons + 1) % RTL818X_TX_RING_SIZE;
+	}
+}
+
+static void rtl818x_poll(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u16 reg = rtl818x_ioread16(priv, &priv->map->INT_STATUS);
+
+	if (reg == 0xFFFF)
+		return;
+
+	rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg);
+
+	if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR))
+		rtl818x_handle_tx(dev);
+
+	if (reg & (RTL818X_INT_RX_OK | RTL818X_INT_RX_ERR))
+		rtl818x_handle_rx(dev);
+}
+
+#define DIV_ROUND_UP(n,d) (((n)+(d)-1)/(d))
+
+static int rtl818x_tx(struct net80211_device *dev, struct io_buffer *iob)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	struct rtl818x_tx_desc *entry;
+	u32 tx_flags;
+	u16 plcp_len = 0;
+	int len = iob_len(iob);
+
+	tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS |
+		RTL818X_TX_DESC_FLAG_LS | (priv->hw_rate << 24) | len;
+
+	if (priv->r8185) {
+		tx_flags |= RTL818X_TX_DESC_FLAG_DMA |
+			    RTL818X_TX_DESC_FLAG_NO_ENC;
+	} else {
+		unsigned int remainder;
+
+		plcp_len = DIV_ROUND_UP(16 * (len + 4),
+					(dev->rates[dev->rate] * 2) / 10);
+		remainder = (16 * (len + 4)) %
+			    ((dev->rates[dev->rate] * 2) / 10);
+
+		if (remainder > 0 && remainder <= 6)
+			plcp_len |= 1 << 15;
+	}
+
+	entry = &priv->tx_ring[priv->tx_prod];
+
+	if (dev->phy_flags & NET80211_PHY_USE_PROTECTION) {
+		tx_flags |= RTL818X_TX_DESC_FLAG_CTS;
+		tx_flags |= priv->hw_rtscts_rate << 19;
+		entry->rts_duration = net80211_cts_duration(dev, len);
+	} else {
+		entry->rts_duration = 0;
+	}
+
+	if (entry->flags & RTL818X_TX_DESC_FLAG_OWN) {
+		/* card hasn't processed the old packet yet! */
+		return -EBUSY;
+	}
+
+	priv->tx_buf[priv->tx_prod] = iob;
+	priv->tx_prod = (priv->tx_prod + 1) % RTL818X_TX_RING_SIZE;
+
+	entry->plcp_len = cpu_to_le16(plcp_len);
+	entry->tx_buf = cpu_to_le32(virt_to_bus(iob->data));
+	entry->frame_len = cpu_to_le32(len);
+	entry->flags2 = /* alternate retry rate in 100kbps << 4 */ 0;
+	entry->retry_limit = RTL818X_MAX_RETRIES;
+	entry->flags = cpu_to_le32(tx_flags);
+
+	rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << 5));
+
+	return 0;
+}
+
+void rtl818x_set_anaparam(struct rtl818x_priv *priv, u32 anaparam)
+{
+	u8 reg;
+
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+	reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+		 reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+	rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+		 reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+}
+
+static int rtl818x_init_hw(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u16 reg;
+
+	rtl818x_iowrite8(priv, &priv->map->CMD, 0);
+	rtl818x_ioread8(priv, &priv->map->CMD);
+	mdelay(10);
+
+	/* reset */
+	rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+	rtl818x_ioread8(priv, &priv->map->CMD);
+
+	reg = rtl818x_ioread8(priv, &priv->map->CMD);
+	reg &= (1 << 1);
+	reg |= RTL818X_CMD_RESET;
+	rtl818x_iowrite8(priv, &priv->map->CMD, RTL818X_CMD_RESET);
+	rtl818x_ioread8(priv, &priv->map->CMD);
+	mdelay(200);
+
+	/* check success of reset */
+	if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) {
+		DBG("rtl818x %s: reset timeout!\n", dev->netdev->name);
+		return -ETIMEDOUT;
+	}
+
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD);
+	rtl818x_ioread8(priv, &priv->map->CMD);
+	mdelay(200);
+
+	if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) {
+		/* For cardbus */
+		reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+		reg |= 1 << 1;
+		rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+		reg = rtl818x_ioread16(priv, &priv->map->FEMR);
+		reg |= (1 << 15) | (1 << 14) | (1 << 4);
+		rtl818x_iowrite16(priv, &priv->map->FEMR, reg);
+	}
+
+	rtl818x_iowrite8(priv, &priv->map->MSR, 0);
+
+	if (!priv->r8185)
+		rtl818x_set_anaparam(priv, priv->anaparam);
+
+	rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma);
+	rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring_dma);
+
+	/* TODO: necessary? specs indicate not */
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+	reg = rtl818x_ioread8(priv, &priv->map->CONFIG2);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3));
+	if (priv->r8185) {
+		reg = rtl818x_ioread8(priv, &priv->map->CONFIG2);
+		rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4));
+	}
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+	/* TODO: set CONFIG5 for calibrating AGC on rtl8180 + philips radio? */
+
+	/* TODO: turn off hw wep on rtl8180 */
+
+	rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0);
+
+	if (priv->r8185) {
+		rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
+		rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
+		rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0);
+
+		rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
+
+		/* TODO: set ClkRun enable? necessary? */
+		reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE);
+		rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6));
+		rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+		reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+		rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2));
+		rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+	} else {
+		rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1);
+		rtl818x_iowrite8(priv, &priv->map->SECURITY, 0);
+
+		rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6);
+		rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C);
+	}
+
+	priv->rf->init(dev);
+	if (priv->r8185)
+		rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
+	return 0;
+}
+
+static int rtl818x_init_rx_ring(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	struct rtl818x_rx_desc *entry;
+	int i;
+
+	priv->rx_ring = malloc_dma(sizeof(*priv->rx_ring) * RTL818X_RX_RING_SIZE,
+				   RTL818X_RING_ALIGN);
+	priv->rx_ring_dma = virt_to_bus(priv->rx_ring);
+	if (!priv->rx_ring) {
+		DBG("rtl818x %s: cannot allocate RX ring\n", dev->netdev->name);
+		return -ENOMEM;
+	}
+
+	memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * RTL818X_RX_RING_SIZE);
+	priv->rx_idx = 0;
+
+	for (i = 0; i < RTL818X_RX_RING_SIZE; i++) {
+		struct io_buffer *iob = alloc_iob(MAX_RX_SIZE);
+		entry = &priv->rx_ring[i];
+		if (!iob)
+			return -ENOMEM;
+
+		priv->rx_buf[i] = iob;
+		entry->rx_buf = cpu_to_le32(virt_to_bus(iob->data));
+		entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN |
+					   MAX_RX_SIZE);
+	}
+	entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR);
+	return 0;
+}
+
+static void rtl818x_free_rx_ring(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int i;
+
+	for (i = 0; i < RTL818X_RX_RING_SIZE; i++) {
+		free_iob(priv->rx_buf[i]);
+		priv->rx_buf[i] = NULL;
+	}
+
+	free_dma(priv->rx_ring, sizeof(*priv->rx_ring) * RTL818X_RX_RING_SIZE);
+	priv->rx_ring = NULL;
+}
+
+static int rtl818x_init_tx_ring(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int i;
+
+	priv->tx_ring = malloc_dma(sizeof(*priv->tx_ring) * RTL818X_TX_RING_SIZE,
+				   RTL818X_RING_ALIGN);
+	priv->tx_ring_dma = virt_to_bus(priv->tx_ring);
+	if (!priv->tx_ring) {
+		DBG("rtl818x %s: cannot allocate TX ring\n", dev->netdev->name);
+		return -ENOMEM;
+	}
+
+	memset(priv->tx_ring, 0, sizeof(*priv->tx_ring) * RTL818X_TX_RING_SIZE);
+	priv->tx_prod = priv->tx_cons = 0;
+
+	for (i = 0; i < RTL818X_TX_RING_SIZE; i++)
+		priv->tx_ring[i].next_tx_desc = cpu_to_le32(priv->tx_ring_dma +
+				((i + 1) % RTL818X_TX_RING_SIZE) * sizeof(*priv->tx_ring));
+
+	return 0;
+}
+
+static void rtl818x_free_tx_ring(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int i;
+
+	for (i = 0; i < RTL818X_TX_RING_SIZE; i++) {
+		if (priv->tx_buf[i])
+			net80211_tx_complete(dev, priv->tx_buf[i], 0, ECANCELED);
+		priv->tx_buf[i] = NULL;
+	}
+
+	free_dma(priv->tx_ring, sizeof(*priv->tx_ring) * RTL818X_TX_RING_SIZE);
+	priv->tx_ring = NULL;
+}
+
+static void rtl818x_irq(struct net80211_device *dev, int enable)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	rtl818x_iowrite16(priv, &priv->map->INT_MASK, enable? 0xFFFF : 0);
+}
+
+/* Sets the MAC address of the card. */
+static void rtl818x_set_hwaddr(struct net80211_device *dev, u8 *hwaddr)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+	rtl818x_iowrite32(priv, (u32 *)&priv->map->MAC[0],
+			  le32_to_cpu(*(u32 *)hwaddr));
+	rtl818x_iowrite16(priv, (u16 *)&priv->map->MAC[4],
+			  le16_to_cpu(*(u16 *)(hwaddr + 4)));
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+}
+
+static int rtl818x_start(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int ret;
+	u32 reg;
+
+	ret = rtl818x_init_rx_ring(dev);
+	if (ret)
+		return ret;
+
+	ret = rtl818x_init_tx_ring(dev);
+	if (ret)
+		goto err_free_rings;
+
+	ret = rtl818x_init_hw(dev);
+	if (ret)
+		goto err_free_rings;
+
+	rtl818x_set_hwaddr(dev, dev->netdev->ll_addr);
+
+	rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma);
+	rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring_dma);
+
+	rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+
+	rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
+	rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0);
+
+	reg = RTL818X_RX_CONF_ONLYERLPKT |
+	      RTL818X_RX_CONF_RX_AUTORESETPHY |
+	      RTL818X_RX_CONF_MGMT |
+	      RTL818X_RX_CONF_DATA |
+	      (7 << 8 /* MAX RX DMA */) |
+	      RTL818X_RX_CONF_BROADCAST |
+	      RTL818X_RX_CONF_NICMAC;
+
+	if (priv->r8185)
+		reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2;
+	else {
+		reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1)
+			? RTL818X_RX_CONF_CSDM1 : 0;
+		reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2)
+			? RTL818X_RX_CONF_CSDM2 : 0;
+	}
+
+	priv->rx_conf = reg;
+	rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
+
+	if (priv->r8185) {
+		reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
+		reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
+		reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+		rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
+
+		reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
+		reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
+		reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+		reg |=  RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
+		rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
+
+		/* disable early TX */
+		rtl818x_iowrite8(priv, (u8 *)priv->map + 0xec, 0x3f);
+	}
+
+	reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+	reg |= (6 << 21 /* MAX TX DMA */) |
+	       RTL818X_TX_CONF_NO_ICV;
+
+	if (priv->r8185)
+		reg &= ~RTL818X_TX_CONF_PROBE_DTS;
+	else
+		reg &= ~RTL818X_TX_CONF_HW_SEQNUM;
+
+	/* different meaning, same value on both rtl8185 and rtl8180 */
+	reg &= ~RTL818X_TX_CONF_SAT_HWPLCP;
+
+	rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);
+
+	reg = rtl818x_ioread8(priv, &priv->map->CMD);
+	reg |= RTL818X_CMD_RX_ENABLE;
+	reg |= RTL818X_CMD_TX_ENABLE;
+	rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+
+	DBG("%s rtl818x: started\n", dev->netdev->name);
+
+	return 0;
+
+ err_free_rings:
+	rtl818x_free_rx_ring(dev);
+	if (priv->tx_ring)
+		rtl818x_free_tx_ring(dev);
+
+	DBG("%s rtl818x: failed to start\n", dev->netdev->name);
+
+	return ret;
+}
+
+static void rtl818x_stop(struct net80211_device *dev)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	u8 reg;
+
+	rtl818x_irq(dev, 0);
+
+	reg = rtl818x_ioread8(priv, &priv->map->CMD);
+	reg &= ~RTL818X_CMD_TX_ENABLE;
+	reg &= ~RTL818X_CMD_RX_ENABLE;
+	rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+
+	priv->rf->stop(dev);
+
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+	reg = rtl818x_ioread8(priv, &priv->map->CONFIG4);
+	rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF);
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+	rtl818x_free_rx_ring(dev);
+	rtl818x_free_tx_ring(dev);
+}
+
+static int rtl818x_config(struct net80211_device *dev, int changed)
+{
+	struct rtl818x_priv *priv = dev->priv;
+	int i;
+
+	if (changed & NET80211_CFG_CHANNEL)
+		priv->rf->set_chan(dev, &dev->channels[dev->channel]);
+
+	if (changed & NET80211_CFG_ASSOC) {
+		for (i = 0; i < ETH_ALEN; i++)
+			rtl818x_iowrite8(priv, &priv->map->BSSID[i], dev->bssid[i]);
+		rtl818x_iowrite8(priv, &priv->map->MSR,
+				 dev->state & NET80211_ASSOCIATED?
+					RTL818X_MSR_INFRA : RTL818X_MSR_NO_LINK);
+	}
+
+	if (changed & NET80211_CFG_PHY_PARAMS)
+		priv->rf->conf_erp(dev);
+
+	if (changed & NET80211_CFG_RATE) {
+		/* figure out the hardware rate number for the new
+		   logical rate */
+		int hw_rate;
+		for (hw_rate = 0; hw_rate < RTL818X_NR_RATES &&
+			     rtl818x_rates[hw_rate] != dev->rates[dev->rate];
+		     hw_rate++)
+			;
+		if (hw_rate >= RTL818X_NR_RATES)
+			return -EINVAL;
+
+		priv->hw_rate = hw_rate;
+
+		/* and the RTS/CTS rate */
+		for (hw_rate = 0; hw_rate < RTL818X_NR_RATES &&
+			     rtl818x_rates[hw_rate] !=
+				dev->rates[dev->rtscts_rate];
+		     hw_rate++)
+			;
+		if (hw_rate >= RTL818X_NR_RATES)
+			hw_rate = priv->hw_rate;
+
+		priv->hw_rtscts_rate = hw_rate;
+	}
+
+	return 0;
+}
+
+static const u8 rtl818x_eeprom_bits[] = {
+	[SPI_BIT_SCLK] = RTL818X_EEPROM_CMD_CK,
+	[SPI_BIT_MISO] = RTL818X_EEPROM_CMD_READ,
+	[SPI_BIT_MOSI] = RTL818X_EEPROM_CMD_WRITE,
+	[SPI_BIT_SS(0)] = RTL818X_EEPROM_CMD_CS,
+};
+
+static int rtl818x_spi_read_bit(struct bit_basher *basher, unsigned int bit_id)
+{
+	struct rtl818x_priv *priv = container_of(basher, struct rtl818x_priv,
+						 spibit.basher);
+
+	u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	return reg & rtl818x_eeprom_bits[bit_id];
+}
+
+static void rtl818x_spi_write_bit(struct bit_basher *basher,
+				  unsigned int bit_id, unsigned long data)
+{
+	struct rtl818x_priv *priv = container_of(basher, struct rtl818x_priv,
+						 spibit.basher);
+
+	u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	u8 mask = rtl818x_eeprom_bits[bit_id];
+	reg = (reg & ~mask) | (data & mask);
+
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg);
+
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(10);
+}
+
+static struct bit_basher_operations rtl818x_basher_ops = {
+	.read = rtl818x_spi_read_bit,
+	.write = rtl818x_spi_write_bit,
+};
+
+#if DBGLVL_MAX
+static const char *rtl818x_rf_names[] = {
+	NULL,			/* no 0 */
+	"Intersil", "RFMD",	/* unsupported 1-2 */
+	"SA2400", "max2820", "GRF5101",	/* supported 3-5 */
+	NULL, NULL, NULL,	/* no 6-8 */
+	"RTL8225",		/* supported 9 */
+	"RTL8255",		/* unsupported 10 */
+};
+#define RTL818X_NR_RF_NAMES 11
+#endif
+
+struct net80211_device_operations rtl818x_operations = {
+	.open = rtl818x_start,
+	.close = rtl818x_stop,
+	.transmit = rtl818x_tx,
+	.poll = rtl818x_poll,
+	.irq = rtl818x_irq,
+	.config = rtl818x_config,
+};
+
+static int rtl818x_probe(struct pci_device *pdev,
+			 const struct pci_device_id *id __unused)
+{
+	struct net80211_device *dev;
+	struct rtl818x_priv *priv;
+	struct rtl818x_rf_ops *rf;
+	int err, i;
+	const char *chip_name;
+	u32 reg;
+	u16 eeprom_val;
+	struct net80211_hw_info *hwinfo;
+
+	hwinfo = zalloc(sizeof(*hwinfo));
+	if (!hwinfo) {
+		DBG("rtl818x: hwinfo alloc failed\n");
+		return -ENOMEM;
+	}
+
+	adjust_pci_device(pdev);
+
+	dev = net80211_alloc(sizeof(*priv));
+	if (!dev) {
+		DBG("rtl818x: net80211 alloc failed\n");
+		return -ENOMEM;
+	}
+
+	priv = dev->priv;
+	priv->pdev = pdev;
+	dev->netdev->dev = &pdev->dev;
+
+	priv->map = (struct rtl818x_csr *)pdev->ioaddr;
+	if (!priv->map) {
+		DBG("rtl818x: cannot find device memory\n");
+		err = -ENXIO;
+		goto err_free_dev;
+	}
+
+	reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+	reg &= RTL818X_TX_CONF_HWVER_MASK;
+	switch (reg) {
+	case RTL818X_TX_CONF_R8180_ABCD:
+		chip_name = "0";
+		break;
+	case RTL818X_TX_CONF_R8180_F:
+		chip_name = "0vF";
+		break;
+	case RTL818X_TX_CONF_R8185_ABC:
+		chip_name = "5";
+		break;
+	case RTL818X_TX_CONF_R8185_D:
+		chip_name = "5vD";
+		break;
+	default:
+		DBG("rtl818x: Unknown chip! (0x%x)\n", reg >> 25);
+		err = -ENOSYS;
+		goto err_free_dev;
+	}
+
+	priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC;
+
+	hwinfo->bands = NET80211_BAND_BIT_2GHZ;
+	hwinfo->flags = NET80211_HW_RX_HAS_FCS;
+	hwinfo->signal_type = NET80211_SIGNAL_ARBITRARY;
+	hwinfo->signal_max = 65;
+	hwinfo->channel_change_time = 1000;
+
+	memcpy(hwinfo->rates[NET80211_BAND_2GHZ], rtl818x_rates,
+	       sizeof(*rtl818x_rates) * RTL818X_NR_RATES);
+
+	if (priv->r8185) {
+		hwinfo->modes = NET80211_MODE_B | NET80211_MODE_G;
+		hwinfo->nr_rates[NET80211_BAND_2GHZ] = RTL818X_NR_RATES;
+	} else {
+		hwinfo->modes = NET80211_MODE_B;
+		hwinfo->nr_rates[NET80211_BAND_2GHZ] = RTL818X_NR_B_RATES;
+	}
+
+	priv->spibit.basher.op = &rtl818x_basher_ops;
+	priv->spibit.bus.mode = SPI_MODE_THREEWIRE;
+	init_spi_bit_basher(&priv->spibit);
+
+	DBG2("rtl818x RX_CONF: %08x\n", rtl818x_ioread32(priv, &priv->map->RX_CONF));
+
+	if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6))
+		init_at93c66(&priv->eeprom, 16);
+	else
+		init_at93c46(&priv->eeprom, 16);
+	priv->eeprom.bus = &priv->spibit.bus;
+
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM);
+	rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+	udelay(10);
+
+	nvs_read(&priv->eeprom.nvs, 0x06, &eeprom_val, 2);
+	DBG2("rtl818x eeprom val = %04x\n", eeprom_val);
+	eeprom_val &= 0xFF;
+
+	priv->rf = NULL;
+	for_each_table_entry(rf, RTL818X_RF_DRIVERS) {
+		if (rf->id == eeprom_val) {
+			priv->rf = rf;
+			break;
+		}
+	}
+
+	if (!priv->rf) {
+#if DBGLVL_MAX
+		if (eeprom_val < RTL818X_NR_RF_NAMES &&
+		    rtl818x_rf_names[eeprom_val] != NULL)
+			DBG("rtl818x: %s RF frontend not supported!\n",
+			    rtl818x_rf_names[eeprom_val]);
+		else
+			DBG("rtl818x: RF frontend #%d not recognized!\n",
+			    eeprom_val);
+#endif
+
+		err = -ENOSYS;
+		goto err_free_dev;
+	}
+
+	nvs_read(&priv->eeprom.nvs, 0x17, &eeprom_val, 2);
+	priv->csthreshold = eeprom_val >> 8;
+	if (!priv->r8185) {
+		nvs_read(&priv->eeprom.nvs, 0xD, &priv->anaparam, 4);
+		nvs_read(&priv->eeprom.nvs, 0x19, &priv->rfparam, 2);
+		priv->anaparam = le32_to_cpu(priv->anaparam);
+		priv->rfparam = le16_to_cpu(priv->rfparam);
+	}
+
+	/* read the MAC address */
+	nvs_read(&priv->eeprom.nvs, 0x7, hwinfo->hwaddr, 6);
+
+	/* CCK TX power */
+	for (i = 0; i < 14; i += 2) {
+		u16 txpwr;
+		nvs_read(&priv->eeprom.nvs, 0x10 + (i >> 1), &txpwr, 2);
+		priv->txpower[i] = txpwr & 0xFF;
+		priv->txpower[i + 1] = txpwr >> 8;
+	}
+
+	/* OFDM TX power */
+	if (priv->r8185) {
+		for (i = 0; i < 14; i += 2) {
+			u16 txpwr;
+			nvs_read(&priv->eeprom.nvs, 0x20 + (i >> 1), &txpwr, 2);
+			priv->txpower[i] |= (txpwr & 0xFF) << 8;
+			priv->txpower[i + 1] |= txpwr & 0xFF00;
+		}
+	}
+
+	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+	err = net80211_register(dev, &rtl818x_operations, hwinfo);
+	if (err) {
+		DBG("rtl818x: cannot register device\n");
+		goto err_free_dev;
+	}
+
+	free(hwinfo);
+
+	DBG("rtl818x: Realtek RTL818%s (RF chip %s) with address %s\n",
+	    chip_name, priv->rf->name, netdev_addr(dev->netdev));
+
+	return 0;
+
+ err_free_dev:
+	pci_set_drvdata(pdev, NULL);
+	net80211_free(dev);
+	free(hwinfo);
+	return err;
+}
+
+static void rtl818x_remove(struct pci_device *pdev)
+{
+	struct net80211_device *dev = pci_get_drvdata(pdev);
+
+	if (!dev)
+		return;
+
+	net80211_unregister(dev);
+	net80211_free(dev);
+}
+
+/* Hide PCI_ROM definitions in here from parserom.pl; the definitions
+   that should be used are in rtl8180.c and rtl8185.c. */
+#define RTL_ROM PCI_ROM
+
+static struct pci_device_id rtl818x_nics[] = {
+	RTL_ROM(0x10ec, 0x8185, "rtl8185", "Realtek 8185", 0),
+	RTL_ROM(0x1799, 0x700f, "f5d7000", "Belkin F5D7000", 0),
+	RTL_ROM(0x1799, 0x701f, "f5d7010", "Belkin F5D7010", 0),
+
+	RTL_ROM(0x10ec, 0x8180, "rtl8180", "Realtek 8180", 0),
+	RTL_ROM(0x1799, 0x6001, "f5d6001", "Belkin F5D6001", 0),
+	RTL_ROM(0x1799, 0x6020, "f5d6020", "Belkin F5D6020", 0),
+	RTL_ROM(0x1186, 0x3300, "dwl510",  "D-Link DWL-510", 0),
+};
+
+struct pci_driver rtl818x_driver __pci_driver = {
+	.ids            = rtl818x_nics,
+	.id_count       = sizeof(rtl818x_nics) / sizeof(rtl818x_nics[0]),
+	.probe		= rtl818x_probe,
+	.remove		= rtl818x_remove,
+};
diff --git a/gpxe/src/drivers/net/rtl818x/rtl818x.h b/gpxe/src/drivers/net/rtl818x/rtl818x.h
new file mode 100644
index 0000000..15821fa
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl818x/rtl818x.h
@@ -0,0 +1,359 @@
+/*
+ * Definitions for RTL818x hardware
+ *
+ * Copyright 2007 Michael Wu <flamingice at sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl at tiscali.it>
+ *
+ * Modified for gPXE, June 2009, by Joshua Oreman <oremanj at rwcr.net>
+ *
+ * Based on the r8187 driver, which is:
+ * Copyright 2005 Andrea Merello <andreamrl at tiscali.it>, et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef RTL818X_H
+#define RTL818X_H
+
+#include <gpxe/spi_bit.h>
+#include <gpxe/tables.h>
+
+FILE_LICENCE(GPL2_ONLY);
+
+struct rtl818x_csr {
+	u8	MAC[6];
+	u8	reserved_0[2];
+	u32	MAR[2];
+	u8	RX_FIFO_COUNT;
+	u8	reserved_1;
+	u8	TX_FIFO_COUNT;
+	u8	BQREQ;
+	u8	reserved_2[4];
+	u32	TSFT[2];
+	u32	TLPDA;
+	u32	TNPDA;
+	u32	THPDA;
+	u16	BRSR;
+	u8	BSSID[6];
+	u8	RESP_RATE;
+	u8	EIFS;
+	u8	reserved_3[1];
+	u8	CMD;
+#define RTL818X_CMD_TX_ENABLE		(1 << 2)
+#define RTL818X_CMD_RX_ENABLE		(1 << 3)
+#define RTL818X_CMD_RESET		(1 << 4)
+	u8	reserved_4[4];
+	u16	INT_MASK;
+	u16	INT_STATUS;
+#define RTL818X_INT_RX_OK		(1 <<  0)
+#define RTL818X_INT_RX_ERR		(1 <<  1)
+#define RTL818X_INT_TXL_OK		(1 <<  2)
+#define RTL818X_INT_TXL_ERR		(1 <<  3)
+#define RTL818X_INT_RX_DU		(1 <<  4)
+#define RTL818X_INT_RX_FO		(1 <<  5)
+#define RTL818X_INT_TXN_OK		(1 <<  6)
+#define RTL818X_INT_TXN_ERR		(1 <<  7)
+#define RTL818X_INT_TXH_OK		(1 <<  8)
+#define RTL818X_INT_TXH_ERR		(1 <<  9)
+#define RTL818X_INT_TXB_OK		(1 << 10)
+#define RTL818X_INT_TXB_ERR		(1 << 11)
+#define RTL818X_INT_ATIM		(1 << 12)
+#define RTL818X_INT_BEACON		(1 << 13)
+#define RTL818X_INT_TIME_OUT		(1 << 14)
+#define RTL818X_INT_TX_FO		(1 << 15)
+	u32	TX_CONF;
+#define RTL818X_TX_CONF_LOOPBACK_MAC	(1 << 17)
+#define RTL818X_TX_CONF_LOOPBACK_CONT	(3 << 17)
+#define RTL818X_TX_CONF_NO_ICV		(1 << 19)
+#define RTL818X_TX_CONF_DISCW		(1 << 20)
+#define RTL818X_TX_CONF_SAT_HWPLCP	(1 << 24)
+#define RTL818X_TX_CONF_R8180_ABCD	(2 << 25)
+#define RTL818X_TX_CONF_R8180_F		(3 << 25)
+#define RTL818X_TX_CONF_R8185_ABC	(4 << 25)
+#define RTL818X_TX_CONF_R8185_D		(5 << 25)
+#define RTL818X_TX_CONF_R8187vD		(5 << 25)
+#define RTL818X_TX_CONF_R8187vD_B	(6 << 25)
+#define RTL818X_TX_CONF_HWVER_MASK	(7 << 25)
+#define RTL818X_TX_CONF_DISREQQSIZE	(1 << 28)
+#define RTL818X_TX_CONF_PROBE_DTS	(1 << 29)
+#define RTL818X_TX_CONF_HW_SEQNUM	(1 << 30)
+#define RTL818X_TX_CONF_CW_MIN		(1 << 31)
+	u32	RX_CONF;
+#define RTL818X_RX_CONF_MONITOR		(1 <<  0)
+#define RTL818X_RX_CONF_NICMAC		(1 <<  1)
+#define RTL818X_RX_CONF_MULTICAST	(1 <<  2)
+#define RTL818X_RX_CONF_BROADCAST	(1 <<  3)
+#define RTL818X_RX_CONF_FCS		(1 <<  5)
+#define RTL818X_RX_CONF_DATA		(1 << 18)
+#define RTL818X_RX_CONF_CTRL		(1 << 19)
+#define RTL818X_RX_CONF_MGMT		(1 << 20)
+#define RTL818X_RX_CONF_ADDR3		(1 << 21)
+#define RTL818X_RX_CONF_PM		(1 << 22)
+#define RTL818X_RX_CONF_BSSID		(1 << 23)
+#define RTL818X_RX_CONF_RX_AUTORESETPHY	(1 << 28)
+#define RTL818X_RX_CONF_CSDM1		(1 << 29)
+#define RTL818X_RX_CONF_CSDM2		(1 << 30)
+#define RTL818X_RX_CONF_ONLYERLPKT	(1 << 31)
+	u32	INT_TIMEOUT;
+	u32	TBDA;
+	u8	EEPROM_CMD;
+#define RTL818X_EEPROM_CMD_READ		(1 << 0)
+#define RTL818X_EEPROM_CMD_WRITE	(1 << 1)
+#define RTL818X_EEPROM_CMD_CK		(1 << 2)
+#define RTL818X_EEPROM_CMD_CS		(1 << 3)
+#define RTL818X_EEPROM_CMD_NORMAL	(0 << 6)
+#define RTL818X_EEPROM_CMD_LOAD		(1 << 6)
+#define RTL818X_EEPROM_CMD_PROGRAM	(2 << 6)
+#define RTL818X_EEPROM_CMD_CONFIG	(3 << 6)
+	u8	CONFIG0;
+	u8	CONFIG1;
+	u8	CONFIG2;
+#define RTL818X_CONFIG2_ANTENNA_DIV	(1 << 6)
+	u32	ANAPARAM;
+	u8	MSR;
+#define RTL818X_MSR_NO_LINK		(0 << 2)
+#define RTL818X_MSR_ADHOC		(1 << 2)
+#define RTL818X_MSR_INFRA		(2 << 2)
+#define RTL818X_MSR_MASTER		(3 << 2)
+#define RTL818X_MSR_ENEDCA		(4 << 2)
+	u8	CONFIG3;
+#define RTL818X_CONFIG3_ANAPARAM_WRITE	(1 << 6)
+#define RTL818X_CONFIG3_GNT_SELECT	(1 << 7)
+	u8	CONFIG4;
+#define RTL818X_CONFIG4_POWEROFF	(1 << 6)
+#define RTL818X_CONFIG4_VCOOFF		(1 << 7)
+	u8	TESTR;
+	u8	reserved_9[2];
+	u8	PGSELECT;
+	u8	SECURITY;
+	u32	ANAPARAM2;
+	u8	reserved_10[12];
+	u16	BEACON_INTERVAL;
+	u16	ATIM_WND;
+	u16	BEACON_INTERVAL_TIME;
+	u16	ATIMTR_INTERVAL;
+	u8	PHY_DELAY;
+	u8	CARRIER_SENSE_COUNTER;
+	u8	reserved_11[2];
+	u8	PHY[4];
+	u16	RFPinsOutput;
+	u16	RFPinsEnable;
+	u16	RFPinsSelect;
+	u16	RFPinsInput;
+	u32	RF_PARA;
+	u32	RF_TIMING;
+	u8	GP_ENABLE;
+	u8	GPIO;
+	u8	reserved_12[2];
+	u32	HSSI_PARA;
+	u8	reserved_13[4];
+	u8	TX_AGC_CTL;
+#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT		(1 << 0)
+#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT	(1 << 1)
+#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT			(1 << 2)
+	u8	TX_GAIN_CCK;
+	u8	TX_GAIN_OFDM;
+	u8	TX_ANTENNA;
+	u8	reserved_14[16];
+	u8	WPA_CONF;
+	u8	reserved_15[3];
+	u8	SIFS;
+	u8	DIFS;
+	u8	SLOT;
+	u8	reserved_16[5];
+	u8	CW_CONF;
+#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT	(1 << 0)
+#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT	(1 << 1)
+	u8	CW_VAL;
+	u8	RATE_FALLBACK;
+#define RTL818X_RATE_FALLBACK_ENABLE	(1 << 7)
+	u8	ACM_CONTROL;
+	u8	reserved_17[24];
+	u8	CONFIG5;
+	u8	TX_DMA_POLLING;
+	u8	reserved_18[2];
+	u16	CWR;
+	u8	RETRY_CTR;
+	u8	reserved_19[3];
+	u16	INT_MIG;
+/* RTL818X_R8187B_*: magic numbers from ioregisters */
+#define RTL818X_R8187B_B	0
+#define RTL818X_R8187B_D	1
+#define RTL818X_R8187B_E	2
+	u32	RDSAR;
+	u16	TID_AC_MAP;
+	u8	reserved_20[4];
+	u8	ANAPARAM3;
+	u8	reserved_21[5];
+	u16	FEMR;
+	u8	reserved_22[4];
+	u16	TALLY_CNT;
+	u8	TALLY_SEL;
+} __attribute__((packed));
+
+#define MAX_RX_SIZE IEEE80211_MAX_FRAME_LEN
+
+#define RF_PARAM_ANALOGPHY	(1 << 0)
+#define RF_PARAM_ANTBDEFAULT	(1 << 1)
+#define RF_PARAM_CARRIERSENSE1	(1 << 2)
+#define RF_PARAM_CARRIERSENSE2	(1 << 3)
+
+#define BB_ANTATTEN_CHAN14	0x0C
+#define BB_ANTENNA_B 		0x40
+
+#define BB_HOST_BANG 		(1 << 30)
+#define BB_HOST_BANG_EN 	(1 << 2)
+#define BB_HOST_BANG_CLK 	(1 << 1)
+#define BB_HOST_BANG_DATA	1
+
+#define ANAPARAM_TXDACOFF_SHIFT	27
+#define ANAPARAM_PWR0_SHIFT	28
+#define ANAPARAM_PWR0_MASK 	(0x07 << ANAPARAM_PWR0_SHIFT)
+#define ANAPARAM_PWR1_SHIFT	20
+#define ANAPARAM_PWR1_MASK	(0x7F << ANAPARAM_PWR1_SHIFT)
+
+#define RTL818X_RX_RING_SIZE	8 /* doesn't have to be a power of 2 */
+#define RTL818X_TX_RING_SIZE	8 /* nor this [but 2^n is very slightly faster] */
+#define RTL818X_RING_ALIGN	256
+
+#define RTL818X_MAX_RETRIES     4
+
+enum rtl818x_tx_desc_flags {
+	RTL818X_TX_DESC_FLAG_NO_ENC	= (1 << 15),
+	RTL818X_TX_DESC_FLAG_TX_OK	= (1 << 15),
+	RTL818X_TX_DESC_FLAG_SPLCP	= (1 << 16),
+	RTL818X_TX_DESC_FLAG_RX_UNDER	= (1 << 16),
+	RTL818X_TX_DESC_FLAG_MOREFRAG	= (1 << 17),
+	RTL818X_TX_DESC_FLAG_CTS	= (1 << 18),
+	RTL818X_TX_DESC_FLAG_RTS	= (1 << 23),
+	RTL818X_TX_DESC_FLAG_LS		= (1 << 28),
+	RTL818X_TX_DESC_FLAG_FS		= (1 << 29),
+	RTL818X_TX_DESC_FLAG_DMA	= (1 << 30),
+	RTL818X_TX_DESC_FLAG_OWN	= (1 << 31)
+};
+
+struct rtl818x_tx_desc {
+	u32 flags;
+	u16 rts_duration;
+	u16 plcp_len;
+	u32 tx_buf;
+	u32 frame_len;
+	u32 next_tx_desc;
+	u8 cw;
+	u8 retry_limit;
+	u8 agc;
+	u8 flags2;
+	u32 reserved[2];
+} __attribute__ ((packed));
+
+enum rtl818x_rx_desc_flags {
+	RTL818X_RX_DESC_FLAG_ICV_ERR	= (1 << 12),
+	RTL818X_RX_DESC_FLAG_CRC32_ERR	= (1 << 13),
+	RTL818X_RX_DESC_FLAG_PM		= (1 << 14),
+	RTL818X_RX_DESC_FLAG_RX_ERR	= (1 << 15),
+	RTL818X_RX_DESC_FLAG_BCAST	= (1 << 16),
+	RTL818X_RX_DESC_FLAG_PAM	= (1 << 17),
+	RTL818X_RX_DESC_FLAG_MCAST	= (1 << 18),
+	RTL818X_RX_DESC_FLAG_QOS	= (1 << 19), /* RTL8187(B) only */
+	RTL818X_RX_DESC_FLAG_TRSW	= (1 << 24), /* RTL8187(B) only */
+	RTL818X_RX_DESC_FLAG_SPLCP	= (1 << 25),
+	RTL818X_RX_DESC_FLAG_FOF	= (1 << 26),
+	RTL818X_RX_DESC_FLAG_DMA_FAIL	= (1 << 27),
+	RTL818X_RX_DESC_FLAG_LS		= (1 << 28),
+	RTL818X_RX_DESC_FLAG_FS		= (1 << 29),
+	RTL818X_RX_DESC_FLAG_EOR	= (1 << 30),
+	RTL818X_RX_DESC_FLAG_OWN	= (1 << 31)
+};
+
+struct rtl818x_rx_desc {
+	u32 flags;
+	u32 flags2;
+	union {
+		u32 rx_buf;
+		u64 tsft;
+	};
+} __attribute__ ((packed));
+
+struct rtl818x_priv {
+	struct rtl818x_csr *map;
+	const struct rtl818x_rf_ops *rf;
+	int rf_flag; /* whatever RF driver wishes to use it for */
+	int hw_rate;
+	int hw_rtscts_rate;
+
+	struct spi_bit_basher spibit;
+	struct spi_device eeprom;
+
+	struct rtl818x_rx_desc *rx_ring;
+	u32 rx_ring_dma;
+	unsigned int rx_idx;	/* next desc to be filled by card */
+	struct io_buffer *rx_buf[RTL818X_RX_RING_SIZE];
+
+	struct rtl818x_tx_desc *tx_ring;
+	u32 tx_ring_dma;
+	unsigned int tx_cons;	/* next desc to be filled by card */
+	unsigned int tx_prod;	/* next desc to be filled by driver */
+	struct io_buffer *tx_buf[RTL818X_TX_RING_SIZE];
+
+	struct pci_device *pdev;
+	u32 rx_conf;
+
+	u16 txpower[14];
+
+	int r8185;
+	u32 anaparam;
+	u16 rfparam;
+	u8 csthreshold;
+};
+
+void rtl818x_write_phy(struct net80211_device *dev, u8 addr, u32 data);
+void rtl818x_set_anaparam(struct rtl818x_priv *priv, u32 anaparam);
+
+static inline u8 rtl818x_ioread8(struct rtl818x_priv *priv __unused, u8 *addr)
+{
+	return inb(addr);
+}
+
+static inline u16 rtl818x_ioread16(struct rtl818x_priv *priv __unused, u16 *addr)
+{
+	return inw(addr);
+}
+
+static inline u32 rtl818x_ioread32(struct rtl818x_priv *priv __unused, u32 *addr)
+{
+	return inl(addr);
+}
+
+static inline void rtl818x_iowrite8(struct rtl818x_priv *priv __unused,
+				    u8 *addr, u8 val)
+{
+	outb(val, addr);
+}
+
+static inline void rtl818x_iowrite16(struct rtl818x_priv *priv __unused,
+				     u16 *addr, u16 val)
+{
+	outw(val, addr);
+}
+
+static inline void rtl818x_iowrite32(struct rtl818x_priv *priv __unused,
+				     u32 *addr, u32 val)
+{
+	outl(val, addr);
+}
+
+#define RTL818X_RF_DRIVERS __table(struct rtl818x_rf_ops, "rtl818x_rf_drivers")
+#define __rtl818x_rf_driver __table_entry(RTL818X_RF_DRIVERS, 01)
+
+struct rtl818x_rf_ops {
+	char *name;
+	u8 id;			/* as identified in EEPROM */
+	void (*init)(struct net80211_device *dev);
+	void (*stop)(struct net80211_device *dev);
+	void (*set_chan)(struct net80211_device *dev, struct net80211_channel *chan);
+	void (*conf_erp)(struct net80211_device *dev); /* set based on dev->erp_flags */
+};
+
+#endif /* RTL818X_H */
diff --git a/gpxe/src/drivers/net/sis190.c b/gpxe/src/drivers/net/sis190.c
new file mode 100644
index 0000000..5d0145a
--- /dev/null
+++ b/gpxe/src/drivers/net/sis190.c
@@ -0,0 +1,1179 @@
+/*
+   sis190.c: Silicon Integrated Systems SiS190 ethernet driver
+
+   Copyright (c) 2003 K.M. Liu <kmliu at sis.com>
+   Copyright (c) 2003, 2004 Jeff Garzik <jgarzik at pobox.com>
+   Copyright (c) 2003, 2004, 2005 Francois Romieu <romieu at fr.zoreil.com>
+
+   Modified for gPXE 2009 by Thomas Miletich <thomas.miletich at gmail.com>
+
+   Based on r8169.c, tg3.c, 8139cp.c, skge.c, epic100.c and SiS 190/191
+   genuine driver.
+
+   This software may be used and distributed according to the terms of
+   the GNU General Public License (GPL), incorporated herein by reference.
+   Drivers based on or derived from this code fall under the GPL and must
+   retain the authorship, copyright and license notice.  This file is not
+   a complete program and may only be used when the entire operating
+   system is licensed under the GPL.
+
+   See the file COPYING in this distribution for more information.
+
+ */
+
+FILE_LICENCE ( GPL_ANY );
+
+#include "sis190.h"
+
+static struct pci_device_id sis190_pci_tbl[] = {
+	PCI_ROM (0x1039, 0x0190, "sis190", "sis190", 0),
+	PCI_ROM (0x1039, 0x0191, "sis191", "sis191", 0),
+};
+
+/******************************************************************************
+ *************** HACK to keep ISA bridge in the PCI device list ***************
+ ******************************************************************************/
+
+/* Some sis190 variants store the MAC address in the BIOS CMOS. To read it, we
+ * have to use a PCI to ISA bridge. To access the bridge we need a few things
+ * from it's struct pci_device. We fake the successful probe of a driver to
+ * keep the bridge's struct pci_device in the list of pci_devices.
+ * See details in sis190_get_mac_addr_from_apc().
+ */
+
+static struct pci_device_id sis190_isa_bridge_tbl[] = {
+	PCI_ID (0x1039, 0x0965, "", "", 0),
+	PCI_ID (0x1039, 0x0966, "", "", 0),
+	PCI_ID (0x1039, 0x0968, "", "", 0),
+};
+
+static int sis190_isa_bridge_probe(struct pci_device *pdev __unused,
+				   const struct pci_device_id *ent __unused)
+{
+	return 0;
+}
+
+static void sis190_isa_bridge_remove(struct pci_device *pdev __unused)
+{
+	return;
+}
+
+struct pci_driver sis190_isa_bridge_driver __pci_driver = {
+	.ids		= sis190_isa_bridge_tbl,
+	.id_count	= (sizeof(sis190_isa_bridge_tbl) /
+	                   sizeof(sis190_isa_bridge_tbl[0])),
+	.probe		= sis190_isa_bridge_probe,
+	.remove		= sis190_isa_bridge_remove,
+};
+
+/******************************************************************************
+ *********************************** </HACK> **********************************
+ ******************************************************************************/
+
+static const u32 sis190_intr_mask =
+	RxQEmpty | RxQInt | TxQ1Int | TxQ0Int | RxHalt | TxHalt | LinkChange;
+
+/*
+ * Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ * The chips use a 64 element hash table based on the Ethernet CRC.
+ */
+static const int multicast_filter_limit = 32;
+
+static void __mdio_cmd(void *ioaddr, u32 ctl)
+{
+	unsigned int i;
+
+	SIS_W32(GMIIControl, ctl);
+
+	mdelay(1);
+
+	for (i = 0; i < 100; i++) {
+		if (!(SIS_R32(GMIIControl) & EhnMIInotDone))
+			break;
+		mdelay(1);
+	}
+
+	if (i > 99)
+		DBG("sis190: PHY command timed out !\n");
+}
+
+static void mdio_write(void *ioaddr, int phy_id, int reg, int val)
+{
+	__mdio_cmd(ioaddr, EhnMIIreq | EhnMIIwrite |
+		(((u32) reg) << EhnMIIregShift) | (phy_id << EhnMIIpmdShift) |
+		(((u32) val) << EhnMIIdataShift));
+}
+
+static int mdio_read(void *ioaddr, int phy_id, int reg)
+{
+	__mdio_cmd(ioaddr, EhnMIIreq | EhnMIIread |
+		(((u32) reg) << EhnMIIregShift) | (phy_id << EhnMIIpmdShift));
+
+	return (u16) (SIS_R32(GMIIControl) >> EhnMIIdataShift);
+}
+
+static void __mdio_write(struct net_device *dev, int phy_id, int reg, int val)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+
+	mdio_write(tp->mmio_addr, phy_id, reg, val);
+}
+
+static int __mdio_read(struct net_device *dev, int phy_id, int reg)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+
+	return mdio_read(tp->mmio_addr, phy_id, reg);
+}
+
+static u16 mdio_read_latched(void *ioaddr, int phy_id, int reg)
+{
+	mdio_read(ioaddr, phy_id, reg);
+	return mdio_read(ioaddr, phy_id, reg);
+}
+
+static u16 sis190_read_eeprom(void *ioaddr, u32 reg)
+{
+	u16 data = 0xffff;
+	unsigned int i;
+
+	if (!(SIS_R32(ROMControl) & 0x0002))
+		return 0;
+
+	SIS_W32(ROMInterface, EEREQ | EEROP | (reg << 10));
+
+	for (i = 0; i < 200; i++) {
+		if (!(SIS_R32(ROMInterface) & EEREQ)) {
+			data = (SIS_R32(ROMInterface) & 0xffff0000) >> 16;
+			break;
+		}
+		mdelay(1);
+	}
+
+	return data;
+}
+
+static void sis190_irq_mask_and_ack(void *ioaddr)
+{
+	SIS_W32(IntrMask, 0x00);
+	SIS_W32(IntrStatus, 0xffffffff);
+	SIS_PCI_COMMIT();
+}
+
+static void sis190_asic_down(void *ioaddr)
+{
+	/* Stop the chip's Tx and Rx DMA processes. */
+
+	SIS_W32(TxControl, 0x1a00);
+	SIS_W32(RxControl, 0x1a00);
+
+	sis190_irq_mask_and_ack(ioaddr);
+}
+
+static inline void sis190_mark_as_last_descriptor(struct RxDesc *desc)
+{
+	desc->size |= cpu_to_le32(RingEnd);
+}
+
+static inline void sis190_give_to_asic(struct RxDesc *desc)
+{
+	u32 eor = le32_to_cpu(desc->size) & RingEnd;
+
+	desc->PSize = 0x0;
+	desc->size = cpu_to_le32((RX_BUF_SIZE & RX_BUF_MASK) | eor);
+	wmb();
+	desc->status = cpu_to_le32(OWNbit | INTbit);
+}
+
+static inline void sis190_map_to_asic(struct RxDesc *desc, u32 mapping)
+{
+	desc->addr = cpu_to_le32(mapping);
+	sis190_give_to_asic(desc);
+}
+
+static inline void sis190_make_unusable_by_asic(struct RxDesc *desc)
+{
+	desc->PSize = 0x0;
+	desc->addr = cpu_to_le32(0xdeadbeef);
+	desc->size &= cpu_to_le32(RingEnd);
+	wmb();
+	desc->status = 0x0;
+}
+
+static struct io_buffer *sis190_alloc_rx_iob(struct RxDesc *desc)
+{
+	struct io_buffer *iob;
+
+	iob = alloc_iob(RX_BUF_SIZE);
+	if (iob) {
+		u32 mapping;
+
+		mapping = virt_to_bus(iob->data);
+		sis190_map_to_asic(desc, mapping);
+	} else {
+		DBG("sis190: alloc_iob failed\n");
+		sis190_make_unusable_by_asic(desc);
+	}
+
+	return iob;
+}
+
+static u32 sis190_rx_fill(struct sis190_private *tp, u32 start, u32 end)
+{
+	u32 cur;
+
+	for (cur = start; cur < end; cur++) {
+		unsigned int i = cur % NUM_RX_DESC;
+
+		if (tp->Rx_iobuf[i])
+			continue;
+
+		tp->Rx_iobuf[i] = sis190_alloc_rx_iob(tp->RxDescRing + i);
+
+		if (!tp->Rx_iobuf[i])
+			break;
+	}
+	return cur - start;
+}
+
+static inline int sis190_rx_pkt_err(u32 status)
+{
+#define ErrMask	(OVRUN | SHORT | LIMIT | MIIER | NIBON | COLON | ABORT)
+
+	if ((status & CRCOK) && !(status & ErrMask))
+		return 0;
+
+	return -1;
+}
+
+static int sis190_process_rx(struct sis190_private *tp)
+{
+	u32 rx_left, cur_rx = tp->cur_rx;
+	u32 delta, count;
+
+	rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx;
+
+	for (; rx_left > 0; rx_left--, cur_rx++) {
+		unsigned int entry = cur_rx % NUM_RX_DESC;
+		struct RxDesc *desc = tp->RxDescRing + entry;
+		u32 status;
+
+		if (le32_to_cpu(desc->status) & OWNbit)
+			break;
+
+		status = le32_to_cpu(desc->PSize);
+
+		if (sis190_rx_pkt_err(status) < 0) {
+			sis190_give_to_asic(desc);
+		} else {
+			struct io_buffer *iob = tp->Rx_iobuf[entry];
+			unsigned int pkt_size = (status & RxSizeMask) - 4;
+
+			if (pkt_size > RX_BUF_SIZE) {
+				DBG("sis190: (frag) status = %08x.\n", status);
+				sis190_give_to_asic(desc);
+				continue;
+			}
+
+			sis190_make_unusable_by_asic(desc);
+
+			iob_put(iob, pkt_size);
+
+			DBG2("sis190: received packet. len: %d\n", pkt_size);
+			netdev_rx(tp->dev, iob);
+			DBGIO_HD(iob->data, 60);
+			tp->Rx_iobuf[entry] = NULL;
+		}
+	}
+	count = cur_rx - tp->cur_rx;
+	tp->cur_rx = cur_rx;
+
+	delta = sis190_rx_fill(tp, tp->dirty_rx, tp->cur_rx);
+	if (!delta && count)
+		DBG("sis190: no Rx buffer allocated.\n");
+	tp->dirty_rx += delta;
+
+	if (((tp->dirty_rx + NUM_RX_DESC) == tp->cur_rx))
+		DBG("sis190: Rx buffers exhausted.\n");
+
+	return count;
+}
+
+static inline int sis190_tx_pkt_err(u32 status)
+{
+#define TxErrMask (WND | TABRT | FIFO | LINK)
+
+	if (!(status & TxErrMask))
+		return 0;
+
+	return -1;
+}
+
+static void sis190_process_tx(struct sis190_private *tp)
+{
+	u32 pending, dirty_tx = tp->dirty_tx;
+
+	pending = tp->cur_tx - dirty_tx;
+
+	for (; pending; pending--, dirty_tx++) {
+		unsigned int entry = dirty_tx % NUM_TX_DESC;
+		struct TxDesc *txd = tp->TxDescRing + entry;
+		u32 status = le32_to_cpu(txd->status);
+		struct io_buffer *iob;
+
+		if (status & OWNbit)
+			break;
+
+		iob = tp->Tx_iobuf[entry];
+
+		if (!iob)
+			break;
+
+		if (sis190_tx_pkt_err(status) == 0) {
+			DBG2("sis190: Transmitted packet: %#08x\n", status);
+			netdev_tx_complete(tp->dev, iob);
+		} else {
+			DBG("sis190: Transmit error: %#08x\n", status);
+			netdev_tx_complete_err(tp->dev, iob, -EINVAL);
+		}
+
+		tp->Tx_iobuf[entry] = NULL;
+	}
+
+	if (tp->dirty_tx != dirty_tx)
+		tp->dirty_tx = dirty_tx;
+}
+
+/*
+ * The interrupt handler does all of the Rx thread work and cleans up after
+ * the Tx thread.
+ */
+static void sis190_poll(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void  *ioaddr = tp->mmio_addr;
+	u32 status;
+
+	status = SIS_R32(IntrStatus);
+
+	if ((status == 0xffffffff) || !status)
+		return;
+
+	SIS_W32(IntrStatus, status);
+
+	/* sis190_phy_task() needs to be called in event of a LinkChange and
+	 * after auto-negotiation is finished. Finishing auto-neg won't generate
+	 * any indication, hence we call it every time if the link is bad. */
+	if ((status & LinkChange) || !netdev_link_ok(dev))
+		sis190_phy_task(tp);
+
+	if (status & RxQInt)
+		sis190_process_rx(tp);
+
+	if (status & TxQ0Int)
+		sis190_process_tx(tp);
+}
+
+static inline void sis190_init_ring_indexes(struct sis190_private *tp)
+{
+	tp->dirty_tx = tp->dirty_rx = tp->cur_tx = tp->cur_rx = 0;
+}
+
+static int sis190_init_ring(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+
+	sis190_init_ring_indexes(tp);
+
+	memset(tp->Tx_iobuf, 0, NUM_TX_DESC * sizeof(struct io_buffer *));
+	memset(tp->Rx_iobuf, 0, NUM_RX_DESC * sizeof(struct io_buffer *));
+
+	if (sis190_rx_fill(tp, 0, NUM_RX_DESC) != NUM_RX_DESC)
+		goto err;
+
+	sis190_mark_as_last_descriptor(tp->RxDescRing + NUM_RX_DESC - 1);
+
+	return 0;
+
+err:
+	sis190_free(dev);
+	return -ENOMEM;
+}
+
+static void sis190_set_rx_mode(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void *ioaddr = tp->mmio_addr;
+	u32 mc_filter[2];	/* Multicast hash filter */
+	u16 rx_mode;
+
+	rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
+	mc_filter[1] = mc_filter[0] = 0xffffffff;
+
+	SIS_W16(RxMacControl, rx_mode | 0x2);
+	SIS_W32(RxHashTable, mc_filter[0]);
+	SIS_W32(RxHashTable + 4, mc_filter[1]);
+
+}
+
+static void sis190_soft_reset(void  *ioaddr)
+{
+	SIS_W32(IntrControl, 0x8000);
+	SIS_PCI_COMMIT();
+	SIS_W32(IntrControl, 0x0);
+	sis190_asic_down(ioaddr);
+}
+
+static void sis190_hw_start(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void *ioaddr = tp->mmio_addr;
+
+	sis190_soft_reset(ioaddr);
+
+	SIS_W32(TxDescStartAddr, tp->tx_dma);
+	SIS_W32(RxDescStartAddr, tp->rx_dma);
+
+	SIS_W32(IntrStatus, 0xffffffff);
+	SIS_W32(IntrMask, 0x0);
+	SIS_W32(GMIIControl, 0x0);
+	SIS_W32(TxMacControl, 0x60);
+	SIS_W16(RxMacControl, 0x02);
+	SIS_W32(RxHashTable, 0x0);
+	SIS_W32(0x6c, 0x0);
+	SIS_W32(RxWolCtrl, 0x0);
+	SIS_W32(RxWolData, 0x0);
+
+	SIS_PCI_COMMIT();
+
+	sis190_set_rx_mode(dev);
+
+	SIS_W32(TxControl, 0x1a00 | CmdTxEnb);
+	SIS_W32(RxControl, 0x1a1d);
+}
+
+static void sis190_phy_task(struct sis190_private *tp)
+{
+	struct net_device *dev = tp->dev;
+	void *ioaddr = tp->mmio_addr;
+	int phy_id = tp->mii_if.phy_id;
+	int cnt = 0;
+	u16 val;
+
+	val = mdio_read(ioaddr, phy_id, MII_BMCR);
+
+	/* 100ms timeout is completely arbitrary. I have no datasheet to
+	 * check whether that's a sensible value or not.
+	 */
+	while ((val & BMCR_RESET) && (cnt < 100)) {
+		val = mdio_read(ioaddr, phy_id, MII_BMCR);
+		mdelay(1);
+		cnt++;
+	}
+
+	if (cnt > 99) {
+		DBG("sis190: BMCR_RESET timeout\n");
+		return;
+	}
+
+	if (!(mdio_read_latched(ioaddr, phy_id, MII_BMSR) &
+		     BMSR_ANEGCOMPLETE)) {
+		DBG("sis190: auto-negotiating...\n");
+		netdev_link_down(dev);
+	} else {
+		/* Rejoice ! */
+		struct {
+			int val;
+			u32 ctl;
+			const char *msg;
+		} reg31[] = {
+			{ LPA_1000FULL, 0x07000c00 | 0x00001000,
+				"1000 Mbps Full Duplex" },
+			{ LPA_1000HALF, 0x07000c00,
+				"1000 Mbps Half Duplex" },
+			{ LPA_100FULL, 0x04000800 | 0x00001000,
+				"100 Mbps Full Duplex" },
+			{ LPA_100HALF, 0x04000800,
+				"100 Mbps Half Duplex" },
+			{ LPA_10FULL, 0x04000400 | 0x00001000,
+				"10 Mbps Full Duplex" },
+			{ LPA_10HALF, 0x04000400,
+				"10 Mbps Half Duplex" },
+			{ 0, 0x04000400, "unknown" }
+		}, *p = NULL;
+		u16 adv, autoexp, gigadv, gigrec;
+
+		val = mdio_read(ioaddr, phy_id, 0x1f);
+
+		val = mdio_read(ioaddr, phy_id, MII_LPA);
+		adv = mdio_read(ioaddr, phy_id, MII_ADVERTISE);
+
+		autoexp = mdio_read(ioaddr, phy_id, MII_EXPANSION);
+
+		if (val & LPA_NPAGE && autoexp & EXPANSION_NWAY) {
+			/* check for gigabit speed */
+			gigadv = mdio_read(ioaddr, phy_id, MII_CTRL1000);
+			gigrec = mdio_read(ioaddr, phy_id, MII_STAT1000);
+			val = (gigadv & (gigrec >> 2));
+			if (val & ADVERTISE_1000FULL)
+				p = reg31;
+			else if (val & ADVERTISE_1000HALF)
+				p = reg31 + 1;
+		}
+
+		if (!p) {
+			val &= adv;
+
+			for (p = reg31; p->val; p++) {
+				if ((val & p->val) == p->val)
+					break;
+			}
+		}
+
+		p->ctl |= SIS_R32(StationControl) & ~0x0f001c00;
+
+		if ((tp->features & F_HAS_RGMII) &&
+		    (tp->features & F_PHY_BCM5461)) {
+			// Set Tx Delay in RGMII mode.
+			mdio_write(ioaddr, phy_id, 0x18, 0xf1c7);
+			udelay(200);
+			mdio_write(ioaddr, phy_id, 0x1c, 0x8c00);
+			p->ctl |= 0x03000000;
+		}
+
+		SIS_W32(StationControl, p->ctl);
+
+		if (tp->features & F_HAS_RGMII) {
+			SIS_W32(RGDelay, 0x0441);
+			SIS_W32(RGDelay, 0x0440);
+		}
+
+		DBG("sis190: link on %s mode.\n", p->msg);
+		netdev_link_up(dev);
+	}
+}
+
+static int sis190_open(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	int rc;
+
+	/* Allocate TX ring */
+	tp->TxDescRing = malloc_dma(TX_RING_BYTES, RING_ALIGNMENT);
+	if (!tp->TxDescRing) {
+		DBG("sis190: TX ring allocation failed\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	tp->tx_dma = cpu_to_le32(virt_to_bus(tp->TxDescRing));
+
+	/* Allocate RX ring */
+	tp->RxDescRing = malloc_dma(RX_RING_BYTES, RING_ALIGNMENT);
+	if (!tp->RxDescRing) {
+		DBG("sis190: RX ring allocation failed\n");
+		rc = -ENOMEM;
+		goto error;
+	}
+	tp->rx_dma = cpu_to_le32(virt_to_bus(tp->RxDescRing));
+
+	rc = sis190_init_ring(dev);
+	if (rc < 0)
+		goto error;
+
+	/* init rx filter, also program MAC address to card */
+	sis190_init_rxfilter(dev);
+
+	sis190_hw_start(dev);
+out:
+	return rc;
+
+error:
+	sis190_free(dev);
+	goto out;
+}
+
+static void sis190_down(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void  *ioaddr = tp->mmio_addr;
+
+	do {
+		sis190_asic_down(ioaddr);
+	} while (SIS_R32(IntrMask));
+}
+
+static void sis190_free(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	int i;
+
+	free_dma(tp->TxDescRing, TX_RING_BYTES);
+	free_dma(tp->RxDescRing, RX_RING_BYTES);
+
+	tp->TxDescRing = NULL;
+	tp->RxDescRing = NULL;
+
+	tp->tx_dma = 0;
+	tp->rx_dma = 0;
+
+	tp->cur_tx = tp->dirty_tx = 0;
+	tp->cur_rx = tp->dirty_rx = 0;
+
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		free_iob(tp->Rx_iobuf[i]);
+		tp->Rx_iobuf[i] = NULL;
+	}
+
+	/* tx io_buffers aren't owned by the driver, so don't free them */
+	for(i = 0; i < NUM_TX_DESC; i++)
+		tp->Tx_iobuf[i] = NULL;
+}
+
+static void sis190_close(struct net_device *dev)
+{
+	sis190_down(dev);
+	sis190_free(dev);
+}
+
+static int sis190_transmit(struct net_device *dev, struct io_buffer *iob)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void  *ioaddr = tp->mmio_addr;
+	u32 len, entry;
+	struct TxDesc *desc;
+
+	len = iob_len(iob);
+	if (len < ETH_ZLEN) {
+		iob_pad(iob, ETH_ZLEN);
+		len = ETH_ZLEN;
+	}
+
+	entry = tp->cur_tx % NUM_TX_DESC;
+	desc = tp->TxDescRing + entry;
+
+	if (le32_to_cpu(desc->status) & OWNbit) {
+		DBG("sis190: Tx Ring full\n");
+		return -EINVAL;
+	}
+
+	tp->Tx_iobuf[entry] = iob;
+
+	desc->PSize = cpu_to_le32(len);
+	desc->addr = cpu_to_le32(virt_to_bus(iob->data));
+
+	desc->size = cpu_to_le32(len);
+	if (entry == (NUM_TX_DESC - 1))
+		desc->size |= cpu_to_le32(RingEnd);
+
+	wmb();
+
+	desc->status = cpu_to_le32(OWNbit | INTbit | DEFbit | CRCbit | PADbit);
+
+	tp->cur_tx++;
+
+	SIS_W32(TxControl, 0x1a00 | CmdReset | CmdTxEnb);
+
+	return 0;
+}
+
+static void sis190_free_phy(struct list_head *first_phy)
+{
+	struct sis190_phy *cur, *next;
+
+	list_for_each_entry_safe(cur, next, first_phy, list) {
+		free(cur);
+	}
+}
+
+/**
+ *	sis190_default_phy - Select default PHY for sis190 mac.
+ *	@dev: the net device to probe for
+ *
+ *	Select first detected PHY with link as default.
+ *	If no one is link on, select PHY whose types is HOME as default.
+ *	If HOME doesn't exist, select LAN.
+ */
+static u16 sis190_default_phy(struct sis190_private *tp)
+{
+	struct sis190_phy *phy, *phy_home, *phy_default, *phy_lan;
+	struct mii_if_info *mii_if = &tp->mii_if;
+	void  *ioaddr = tp->mmio_addr;
+	u16 status;
+
+	phy_home = phy_default = phy_lan = NULL;
+
+	list_for_each_entry(phy, &tp->first_phy, list) {
+		status = mdio_read_latched(ioaddr, phy->phy_id, MII_BMSR);
+
+		// Link ON & Not select default PHY & not ghost PHY.
+		if ((status & BMSR_LSTATUS) &&
+		    !phy_default &&
+		    (phy->type != UNKNOWN)) {
+			phy_default = phy;
+		} else {
+			status = mdio_read(ioaddr, phy->phy_id, MII_BMCR);
+			mdio_write(ioaddr, phy->phy_id, MII_BMCR,
+				   status | BMCR_ANENABLE | BMCR_ISOLATE);
+			if (phy->type == HOME)
+				phy_home = phy;
+			else if (phy->type == LAN)
+				phy_lan = phy;
+		}
+	}
+
+	if (!phy_default) {
+		if (phy_home)
+			phy_default = phy_home;
+		else if (phy_lan)
+			phy_default = phy_lan;
+		else
+			phy_default = list_entry(&tp->first_phy,
+						 struct sis190_phy, list);
+	}
+
+	if (mii_if->phy_id != phy_default->phy_id) {
+		mii_if->phy_id = phy_default->phy_id;
+		DBG("sis190: Using transceiver at address %d as default.\n",
+		     mii_if->phy_id);
+	}
+
+	status = mdio_read(ioaddr, mii_if->phy_id, MII_BMCR);
+	status &= (~BMCR_ISOLATE);
+
+	mdio_write(ioaddr, mii_if->phy_id, MII_BMCR, status);
+	status = mdio_read_latched(ioaddr, mii_if->phy_id, MII_BMSR);
+
+	return status;
+}
+
+static void sis190_init_phy(struct sis190_private *tp,
+			    struct sis190_phy *phy, unsigned int phy_id,
+			    u16 mii_status)
+{
+	void *ioaddr = tp->mmio_addr;
+	struct mii_chip_info *p;
+
+	INIT_LIST_HEAD(&phy->list);
+	phy->status = mii_status;
+	phy->phy_id = phy_id;
+
+	phy->id[0] = mdio_read(ioaddr, phy_id, MII_PHYSID1);
+	phy->id[1] = mdio_read(ioaddr, phy_id, MII_PHYSID2);
+
+	for (p = mii_chip_table; p->type; p++) {
+		if ((p->id[0] == phy->id[0]) &&
+		    (p->id[1] == (phy->id[1] & 0xfff0))) {
+			break;
+		}
+	}
+
+	if (p->id[1]) {
+		phy->type = (p->type == MIX) ?
+			((mii_status & (BMSR_100FULL | BMSR_100HALF)) ?
+				LAN : HOME) : p->type;
+		tp->features |= p->feature;
+
+		DBG("sis190: %s transceiver at address %d.\n", p->name, phy_id);
+	} else {
+		phy->type = UNKNOWN;
+
+		DBG("sis190: unknown PHY 0x%x:0x%x transceiver at address %d\n",
+		    phy->id[0], (phy->id[1] & 0xfff0), phy_id);
+	}
+}
+
+static void sis190_mii_probe_88e1111_fixup(struct sis190_private *tp)
+{
+	if (tp->features & F_PHY_88E1111) {
+		void *ioaddr = tp->mmio_addr;
+		int phy_id = tp->mii_if.phy_id;
+		u16 reg[2][2] = {
+			{ 0x808b, 0x0ce1 },
+			{ 0x808f, 0x0c60 }
+		}, *p;
+
+		p = (tp->features & F_HAS_RGMII) ? reg[0] : reg[1];
+
+		mdio_write(ioaddr, phy_id, 0x1b, p[0]);
+		udelay(200);
+		mdio_write(ioaddr, phy_id, 0x14, p[1]);
+		udelay(200);
+	}
+}
+
+/**
+ *	sis190_mii_probe - Probe MII PHY for sis190
+ *	@dev: the net device to probe for
+ *
+ *	Search for total of 32 possible mii phy addresses.
+ *	Identify and set current phy if found one,
+ *	return error if it failed to found.
+ */
+static int sis190_mii_probe(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	struct mii_if_info *mii_if = &tp->mii_if;
+	void *ioaddr = tp->mmio_addr;
+	int phy_id;
+	int rc = 0;
+
+	INIT_LIST_HEAD(&tp->first_phy);
+
+	for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
+		struct sis190_phy *phy;
+		u16 status;
+
+		status = mdio_read_latched(ioaddr, phy_id, MII_BMSR);
+
+		// Try next mii if the current one is not accessible.
+		if (status == 0xffff || status == 0x0000)
+			continue;
+
+		phy = zalloc(sizeof(*phy));
+		if (!phy) {
+			sis190_free_phy(&tp->first_phy);
+			rc = -ENOMEM;
+			goto out;
+		}
+
+		DBG("sis190: found PHY\n");
+
+		sis190_init_phy(tp, phy, phy_id, status);
+
+		list_add(&tp->first_phy, &phy->list);
+	}
+
+	if (list_empty(&tp->first_phy)) {
+		DBG("sis190: No MII transceivers found!\n");
+		rc = -EIO;
+		goto out;
+	}
+
+	/* Select default PHY for mac */
+	sis190_default_phy(tp);
+
+	sis190_mii_probe_88e1111_fixup(tp);
+
+	mii_if->dev = dev;
+	mii_if->mdio_read = __mdio_read;
+	mii_if->mdio_write = __mdio_write;
+	mii_if->phy_id_mask = PHY_ID_ANY;
+	mii_if->reg_num_mask = MII_REG_ANY;
+out:
+	return rc;
+}
+
+static void sis190_mii_remove(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+
+	sis190_free_phy(&tp->first_phy);
+}
+
+static int sis190_init_board(struct pci_device *pdev, struct net_device **netdev)
+{
+	struct sis190_private *tp;
+	struct net_device *dev;
+	void *ioaddr;
+	int rc;
+
+	dev = alloc_etherdev(sizeof(*tp));
+	if (!dev) {
+		DBG("sis190: unable to alloc new etherdev\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	dev->dev = &pdev->dev;
+
+	tp = netdev_priv(dev);
+	memset(tp, 0, sizeof(*tp));
+
+	tp->dev = dev;
+
+	adjust_pci_device(pdev);
+
+	ioaddr = ioremap(pdev->membase, SIS190_REGS_SIZE);
+	if (!ioaddr) {
+		DBG("sis190: cannot remap MMIO, aborting\n");
+		rc = -EIO;
+		goto err;
+	}
+
+	tp->pci_device = pdev;
+	tp->mmio_addr = ioaddr;
+
+	sis190_irq_mask_and_ack(ioaddr);
+
+	sis190_soft_reset(ioaddr);
+
+	*netdev = dev;
+
+	return 0;
+
+err:
+	return rc;
+}
+
+static void sis190_set_rgmii(struct sis190_private *tp, u8 reg)
+{
+	tp->features |= (reg & 0x80) ? F_HAS_RGMII : 0;
+}
+
+static int sis190_get_mac_addr_from_eeprom(struct pci_device *pdev __unused,
+						     struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void *ioaddr = tp->mmio_addr;
+	u16 sig;
+	int i;
+
+	DBG("sis190: Read MAC address from EEPROM\n");
+
+	/* Check to see if there is a sane EEPROM */
+	sig = (u16) sis190_read_eeprom(ioaddr, EEPROMSignature);
+
+	if ((sig == 0xffff) || (sig == 0x0000)) {
+		DBG("sis190: Error EEPROM read.\n");
+		return -EIO;
+	}
+
+	/* Get MAC address from EEPROM */
+	for (i = 0; i < ETH_ALEN / 2; i++) {
+		u16 w = sis190_read_eeprom(ioaddr, EEPROMMACAddr + i);
+
+		((u16 *)dev->hw_addr)[i] = cpu_to_le16(w);
+	}
+
+	sis190_set_rgmii(tp, sis190_read_eeprom(ioaddr, EEPROMInfo));
+
+	return 0;
+}
+
+/**
+ *	sis190_get_mac_addr_from_apc - Get MAC address for SiS96x model
+ *	@pdev: PCI device
+ *	@dev:  network device to get address for
+ *
+ *	SiS96x model, use APC CMOS RAM to store MAC address.
+ *	APC CMOS RAM is accessed through ISA bridge.
+ *	MAC address is read into @net_dev->dev_addr.
+ */
+static int sis190_get_mac_addr_from_apc(struct pci_device *pdev,
+					struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	struct pci_device *isa_bridge = NULL;
+	struct device *d;
+	u8 reg, tmp8;
+	unsigned int i;
+
+	DBG("sis190: Read MAC address from APC.\n");
+
+	list_for_each_entry(d, &(pdev->dev.siblings), siblings) {
+		unsigned int i;
+		isa_bridge = container_of(d, struct pci_device, dev);
+		for(i = 0; i < sis190_isa_bridge_driver.id_count; i++) {
+			if(isa_bridge->vendor ==
+			     sis190_isa_bridge_driver.ids[i].vendor
+			     && isa_bridge->device ==
+			     sis190_isa_bridge_driver.ids[i].device) {
+				DBG("sis190: ISA bridge found\n");
+				break;
+			} else {
+				isa_bridge = NULL;
+			}
+		}
+		if(isa_bridge)
+			break;
+	}
+
+	if (!isa_bridge) {
+		DBG("sis190: Can not find ISA bridge.\n");
+		return -EIO;
+	}
+
+	/* Enable port 78h & 79h to access APC Registers. */
+	pci_read_config_byte(isa_bridge, 0x48, &tmp8);
+	reg = (tmp8 & ~0x02);
+	pci_write_config_byte(isa_bridge, 0x48, reg);
+	udelay(50);
+	pci_read_config_byte(isa_bridge, 0x48, &reg);
+
+        for (i = 0; i < ETH_ALEN; i++) {
+                outb(0x9 + i, 0x78);
+                dev->hw_addr[i] = inb(0x79);
+        }
+
+	outb(0x12, 0x78);
+	reg = inb(0x79);
+
+	sis190_set_rgmii(tp, reg);
+
+	/* Restore the value to ISA Bridge */
+	pci_write_config_byte(isa_bridge, 0x48, tmp8);
+
+	return 0;
+}
+
+/**
+ *      sis190_init_rxfilter - Initialize the Rx filter
+ *      @dev: network device to initialize
+ *
+ *      Set receive filter address to our MAC address
+ *      and enable packet filtering.
+ */
+static inline void sis190_init_rxfilter(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void *ioaddr = tp->mmio_addr;
+	u16 ctl;
+	int i;
+
+	ctl = SIS_R16(RxMacControl);
+	/*
+	 * Disable packet filtering before setting filter.
+	 * Note: SiS's driver writes 32 bits but RxMacControl is 16 bits
+	 * only and followed by RxMacAddr (6 bytes). Strange. -- FR
+	 */
+	SIS_W16(RxMacControl, ctl & ~0x0f00);
+
+	for (i = 0; i < ETH_ALEN; i++)
+		SIS_W8(RxMacAddr + i, dev->ll_addr[i]);
+
+	SIS_W16(RxMacControl, ctl);
+	SIS_PCI_COMMIT();
+}
+
+static int sis190_get_mac_addr(struct pci_device *pdev,
+					 struct net_device *dev)
+{
+	int rc;
+
+	rc = sis190_get_mac_addr_from_eeprom(pdev, dev);
+	if (rc < 0) {
+		u8 reg;
+
+		pci_read_config_byte(pdev, 0x73, &reg);
+
+		if (reg & 0x00000001)
+			rc = sis190_get_mac_addr_from_apc(pdev, dev);
+	}
+	return rc;
+}
+
+static void sis190_set_speed_auto(struct net_device *dev)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void *ioaddr = tp->mmio_addr;
+	int phy_id = tp->mii_if.phy_id;
+	int val;
+
+	DBG("sis190: Enabling Auto-negotiation.\n");
+
+	val = mdio_read(ioaddr, phy_id, MII_ADVERTISE);
+
+	// Enable 10/100 Full/Half Mode, leave MII_ADVERTISE bit4:0
+	// unchanged.
+	mdio_write(ioaddr, phy_id, MII_ADVERTISE, (val & ADVERTISE_SLCT) |
+		   ADVERTISE_100FULL | ADVERTISE_10FULL |
+		   ADVERTISE_100HALF | ADVERTISE_10HALF);
+
+	// Enable 1000 Full Mode.
+	mdio_write(ioaddr, phy_id, MII_CTRL1000, ADVERTISE_1000FULL);
+
+	// Enable auto-negotiation and restart auto-negotiation.
+	mdio_write(ioaddr, phy_id, MII_BMCR,
+		   BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET);
+}
+
+static void sis190_irq(struct net_device *dev, int enable)
+{
+	struct sis190_private *tp = netdev_priv(dev);
+	void *ioaddr = tp->mmio_addr;
+
+	SIS_W32(IntrStatus, 0xffffffff);
+
+	if (enable == 0)
+		SIS_W32(IntrMask, 0x00);
+	else
+		SIS_W32(IntrMask, sis190_intr_mask);
+
+	SIS_PCI_COMMIT();
+}
+
+static struct net_device_operations sis190_netdev_ops = {
+	.open = sis190_open,
+	.close = sis190_close,
+	.poll = sis190_poll,
+	.transmit = sis190_transmit,
+	.irq = sis190_irq,
+};
+
+static int sis190_probe(struct pci_device *pdev,
+			   const struct pci_device_id *ent __unused)
+{
+	struct sis190_private *tp;
+	struct net_device *dev;
+	void *ioaddr;
+	int rc;
+
+	rc = sis190_init_board(pdev, &dev);
+	if (rc < 0)
+		goto out;
+
+	pci_set_drvdata(pdev, dev);
+
+	tp = netdev_priv(dev);
+	ioaddr = tp->mmio_addr;
+
+	rc = sis190_get_mac_addr(pdev, dev);
+	if (rc < 0)
+		goto err;
+
+	rc = sis190_mii_probe(dev);
+	if (rc < 0)
+		goto err;
+
+	rc = register_netdev(dev);
+	if (rc < 0)
+		goto err;
+
+	sis190_set_speed_auto(dev);
+	sis190_phy_task(tp);
+
+	netdev_init(dev, &sis190_netdev_ops);
+	netdev_link_down(dev);
+out:
+	return rc;
+
+err:
+	sis190_mii_remove(dev);
+	iounmap(tp->mmio_addr);
+	goto out;
+}
+
+static void sis190_remove(struct pci_device *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct sis190_private *tp = dev->priv;
+	void *ioaddr = tp->mmio_addr;
+
+	sis190_mii_remove(dev);
+
+	/* shutdown chip, disable interrupts, etc */
+	sis190_soft_reset(ioaddr);
+
+	iounmap(tp->mmio_addr);
+
+	unregister_netdev(dev);
+	netdev_nullify(dev);
+	netdev_put(dev);
+}
+
+struct pci_driver sis190_pci_driver __pci_driver = {
+	.ids		= sis190_pci_tbl,
+	.id_count	= (sizeof(sis190_pci_tbl) / sizeof(sis190_pci_tbl[0])),
+	.probe		= sis190_probe,
+	.remove		= sis190_remove,
+};
diff --git a/gpxe/src/drivers/net/sis190.h b/gpxe/src/drivers/net/sis190.h
new file mode 100644
index 0000000..c6dac5b
--- /dev/null
+++ b/gpxe/src/drivers/net/sis190.h
@@ -0,0 +1,311 @@
+#ifndef __SIS190_H__
+#define __SIS190_H__
+
+FILE_LICENCE ( GPL_ANY );
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <mii.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/io.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/pci.h>
+#include <gpxe/timer.h>
+
+#define PCI_VENDOR_ID_SI	0x1039
+
+#define PHY_MAX_ADDR		32
+#define PHY_ID_ANY		0x1f
+#define MII_REG_ANY		0x1f
+
+#define DRV_VERSION		"1.3"
+#define DRV_NAME		"sis190"
+#define SIS190_DRIVER_NAME	DRV_NAME " Gigabit Ethernet driver " DRV_VERSION
+#define PFX DRV_NAME ": "
+
+#define sis190_rx_quota(count, quota)	count
+
+#define NUM_TX_DESC		8	/* [8..1024] */
+#define NUM_RX_DESC		8	/* [8..8192] */
+#define TX_RING_BYTES		(NUM_TX_DESC * sizeof(struct TxDesc))
+#define RX_RING_BYTES		(NUM_RX_DESC * sizeof(struct RxDesc))
+#define RX_BUF_SIZE		1536
+#define RX_BUF_MASK		0xfff8
+
+#define RING_ALIGNMENT		256
+
+#define SIS190_REGS_SIZE	0x80
+
+/* Enhanced PHY access register bit definitions */
+#define EhnMIIread		0x0000
+#define EhnMIIwrite		0x0020
+#define EhnMIIdataShift		16
+#define EhnMIIpmdShift		6	/* 7016 only */
+#define EhnMIIregShift		11
+#define EhnMIIreq		0x0010
+#define EhnMIInotDone		0x0010
+
+/* Write/read MMIO register */
+#define SIS_W8(reg, val)	writeb ((val), ioaddr + (reg))
+#define SIS_W16(reg, val)	writew ((val), ioaddr + (reg))
+#define SIS_W32(reg, val)	writel ((val), ioaddr + (reg))
+#define SIS_R8(reg)		readb (ioaddr + (reg))
+#define SIS_R16(reg)		readw (ioaddr + (reg))
+#define SIS_R32(reg)		readl (ioaddr + (reg))
+
+#define SIS_PCI_COMMIT()	SIS_R32(IntrControl)
+
+enum sis190_registers {
+	TxControl		= 0x00,
+	TxDescStartAddr		= 0x04,
+	rsv0			= 0x08,	// reserved
+	TxSts			= 0x0c,	// unused (Control/Status)
+	RxControl		= 0x10,
+	RxDescStartAddr		= 0x14,
+	rsv1			= 0x18,	// reserved
+	RxSts			= 0x1c,	// unused
+	IntrStatus		= 0x20,
+	IntrMask		= 0x24,
+	IntrControl		= 0x28,
+	IntrTimer		= 0x2c,	// unused (Interupt Timer)
+	PMControl		= 0x30,	// unused (Power Mgmt Control/Status)
+	rsv2			= 0x34,	// reserved
+	ROMControl		= 0x38,
+	ROMInterface		= 0x3c,
+	StationControl		= 0x40,
+	GMIIControl		= 0x44,
+	GIoCR			= 0x48, // unused (GMAC IO Compensation)
+	GIoCtrl			= 0x4c, // unused (GMAC IO Control)
+	TxMacControl		= 0x50,
+	TxLimit			= 0x54, // unused (Tx MAC Timer/TryLimit)
+	RGDelay			= 0x58, // unused (RGMII Tx Internal Delay)
+	rsv3			= 0x5c, // reserved
+	RxMacControl		= 0x60,
+	RxMacAddr		= 0x62,
+	RxHashTable		= 0x68,
+	// Undocumented		= 0x6c,
+	RxWolCtrl		= 0x70,
+	RxWolData		= 0x74, // unused (Rx WOL Data Access)
+	RxMPSControl		= 0x78,	// unused (Rx MPS Control)
+	rsv4			= 0x7c, // reserved
+};
+
+enum sis190_register_content {
+	/* IntrStatus */
+	SoftInt			= 0x40000000,	// unused
+	Timeup			= 0x20000000,	// unused
+	PauseFrame		= 0x00080000,	// unused
+	MagicPacket		= 0x00040000,	// unused
+	WakeupFrame		= 0x00020000,	// unused
+	LinkChange		= 0x00010000,
+	RxQEmpty		= 0x00000080,
+	RxQInt			= 0x00000040,
+	TxQ1Empty		= 0x00000020,	// unused
+	TxQ1Int			= 0x00000010,
+	TxQ0Empty		= 0x00000008,	// unused
+	TxQ0Int			= 0x00000004,
+	RxHalt			= 0x00000002,
+	TxHalt			= 0x00000001,
+
+	/* {Rx/Tx}CmdBits */
+	CmdReset		= 0x10,
+	CmdRxEnb		= 0x08,		// unused
+	CmdTxEnb		= 0x01,
+	RxBufEmpty		= 0x01,		// unused
+
+	/* Cfg9346Bits */
+	Cfg9346_Lock		= 0x00,		// unused
+	Cfg9346_Unlock		= 0xc0,		// unused
+
+	/* RxMacControl */
+	AcceptErr		= 0x20,		// unused
+	AcceptRunt		= 0x10,		// unused
+	AcceptBroadcast		= 0x0800,
+	AcceptMulticast		= 0x0400,
+	AcceptMyPhys		= 0x0200,
+	AcceptAllPhys		= 0x0100,
+
+	/* RxConfigBits */
+	RxCfgFIFOShift		= 13,
+	RxCfgDMAShift		= 8,		// 0x1a in RxControl ?
+
+	/* TxConfigBits */
+	TxInterFrameGapShift	= 24,
+	TxDMAShift		= 8, /* DMA burst value (0-7) is shift this many bits */
+
+	LinkStatus		= 0x02,		// unused
+	FullDup			= 0x01,		// unused
+
+	/* TBICSRBit */
+	TBILinkOK		= 0x02000000,	// unused
+};
+
+struct TxDesc {
+	volatile u32 PSize;
+	volatile u32 status;
+	volatile u32 addr;
+	volatile u32 size;
+};
+
+struct RxDesc {
+	volatile u32 PSize;
+	volatile u32 status;
+	volatile u32 addr;
+	volatile u32 size;
+};
+
+enum _DescStatusBit {
+	/* _Desc.status */
+	OWNbit		= 0x80000000, // RXOWN/TXOWN
+	INTbit		= 0x40000000, // RXINT/TXINT
+	CRCbit		= 0x00020000, // CRCOFF/CRCEN
+	PADbit		= 0x00010000, // PREADD/PADEN
+	/* _Desc.size */
+	RingEnd		= 0x80000000,
+	/* TxDesc.status */
+	LSEN		= 0x08000000, // TSO ? -- FR
+	IPCS		= 0x04000000,
+	TCPCS		= 0x02000000,
+	UDPCS		= 0x01000000,
+	BSTEN		= 0x00800000,
+	EXTEN		= 0x00400000,
+	DEFEN		= 0x00200000,
+	BKFEN		= 0x00100000,
+	CRSEN		= 0x00080000,
+	COLEN		= 0x00040000,
+	THOL3		= 0x30000000,
+	THOL2		= 0x20000000,
+	THOL1		= 0x10000000,
+	THOL0		= 0x00000000,
+
+	WND		= 0x00080000,
+	TABRT		= 0x00040000,
+	FIFO		= 0x00020000,
+	LINK		= 0x00010000,
+	ColCountMask	= 0x0000ffff,
+	/* RxDesc.status */
+	IPON		= 0x20000000,
+	TCPON		= 0x10000000,
+	UDPON		= 0x08000000,
+	Wakup		= 0x00400000,
+	Magic		= 0x00200000,
+	Pause		= 0x00100000,
+	DEFbit		= 0x00200000,
+	BCAST		= 0x000c0000,
+	MCAST		= 0x00080000,
+	UCAST		= 0x00040000,
+	/* RxDesc.PSize */
+	TAGON		= 0x80000000,
+	RxDescCountMask	= 0x7f000000, // multi-desc pkt when > 1 ? -- FR
+	ABORT		= 0x00800000,
+	SHORT		= 0x00400000,
+	LIMIT		= 0x00200000,
+	MIIER		= 0x00100000,
+	OVRUN		= 0x00080000,
+	NIBON		= 0x00040000,
+	COLON		= 0x00020000,
+	CRCOK		= 0x00010000,
+	RxSizeMask	= 0x0000ffff
+	/*
+	* The asic could apparently do vlan, TSO, jumbo (sis191 only) and
+	* provide two (unused with Linux) Tx queues. No publically
+	* available documentation alas.
+	*/
+};
+
+enum sis190_eeprom_access_register_bits {
+	EECS	= 0x00000001,	// unused
+	EECLK	= 0x00000002,	// unused
+	EEDO	= 0x00000008,	// unused
+	EEDI	= 0x00000004,	// unused
+	EEREQ	= 0x00000080,
+	EEROP	= 0x00000200,
+	EEWOP	= 0x00000100	// unused
+};
+
+/* EEPROM Addresses */
+enum sis190_eeprom_address {
+	EEPROMSignature	= 0x00,
+	EEPROMCLK	= 0x01,	// unused
+	EEPROMInfo	= 0x02,
+	EEPROMMACAddr	= 0x03
+};
+
+enum sis190_feature {
+	F_HAS_RGMII	= 1,
+	F_PHY_88E1111	= 2,
+	F_PHY_BCM5461	= 4
+};
+
+struct sis190_private {
+	void *mmio_addr;
+	struct pci_device *pci_device;
+	struct net_device *dev;
+	u32 cur_rx;
+	u32 cur_tx;
+	u32 dirty_rx;
+	u32 dirty_tx;
+	u32 rx_dma;
+	u32 tx_dma;
+	struct RxDesc *RxDescRing;
+	struct TxDesc *TxDescRing;
+	struct io_buffer *Rx_iobuf[NUM_RX_DESC];
+	struct io_buffer *Tx_iobuf[NUM_TX_DESC];
+	struct mii_if_info mii_if;
+	struct list_head first_phy;
+	u32 features;
+};
+
+struct sis190_phy {
+	struct list_head list;
+	int phy_id;
+	u16 id[2];
+	u16 status;
+	u8  type;
+};
+
+enum sis190_phy_type {
+	UNKNOWN	= 0x00,
+	HOME	= 0x01,
+	LAN	= 0x02,
+	MIX	= 0x03
+};
+
+static struct mii_chip_info {
+	const char *name;
+	u16 id[2];
+	unsigned int type;
+	u32 feature;
+} mii_chip_table[] = {
+	{ "Atheros PHY",          { 0x004d, 0xd010 }, LAN, 0 },
+	{ "Atheros PHY AR8012",   { 0x004d, 0xd020 }, LAN, 0 },
+	{ "Broadcom PHY BCM5461", { 0x0020, 0x60c0 }, LAN, F_PHY_BCM5461 },
+	{ "Broadcom PHY AC131",   { 0x0143, 0xbc70 }, LAN, 0 },
+	{ "Agere PHY ET1101B",    { 0x0282, 0xf010 }, LAN, 0 },
+	{ "Marvell PHY 88E1111",  { 0x0141, 0x0cc0 }, LAN, F_PHY_88E1111 },
+	{ "Realtek PHY RTL8201",  { 0x0000, 0x8200 }, LAN, 0 },
+	{ NULL, { 0x00, 0x00 }, 0, 0 }
+};
+
+static const struct {
+	const char *name;
+} sis_chip_info[] = {
+	{ "SiS 190 PCI Fast Ethernet adapter" },
+	{ "SiS 191 PCI Gigabit Ethernet adapter" },
+};
+
+static void sis190_phy_task(struct sis190_private *tp);
+static void sis190_free(struct net_device *dev);
+static inline void sis190_init_rxfilter(struct net_device *dev);
+
+#endif
diff --git a/gpxe/src/drivers/net/sis900.c b/gpxe/src/drivers/net/sis900.c
index 008b9cf..da14a09 100644
--- a/gpxe/src/drivers/net/sis900.c
+++ b/gpxe/src/drivers/net/sis900.c
@@ -24,6 +24,8 @@
    preliminary Rev. 1.0 Jan. 18, 1998
    http://www.sis.com.tw/support/databook.htm */
 
+FILE_LICENCE ( GPL_ANY );
+
 /* Revision History */
 
 /*
@@ -1181,8 +1183,12 @@ static int
 sis900_poll(struct nic *nic, int retrieve)
 {
     u32 rx_status = rxd[cur_rx].cmdsts;
+    u32 intr_status;
     int retstat = 0;
 
+     /* acknowledge interrupts by reading interrupt status register */
+    intr_status = inl(ioaddr + isr);
+
     if (sis900_debug > 2)
         printf("sis900_poll: cur_rx:%d, status:%X\n", cur_rx, 
 	       (unsigned int) rx_status);
@@ -1262,8 +1268,10 @@ sis900_irq(struct nic *nic __unused, irq_action_t action __unused)
 {
   switch ( action ) {
   case DISABLE :
+    outl(0, ioaddr + imr);
     break;
   case ENABLE :
+    outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);
     break;
   case FORCE :
     break;
@@ -1278,8 +1286,8 @@ static struct nic_operations sis900_operations = {
 };
 
 static struct pci_device_id sis900_nics[] = {
-PCI_ROM(0x1039, 0x0900, "sis900",  "SIS900"),
-PCI_ROM(0x1039, 0x7016, "sis7016", "SIS7016"),
+PCI_ROM(0x1039, 0x0900, "sis900",  "SIS900", 0),
+PCI_ROM(0x1039, 0x7016, "sis7016", "SIS7016", 0),
 };
 
 PCI_DRIVER ( sis900_driver, sis900_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/sis900.h b/gpxe/src/drivers/net/sis900.h
index e88e111..7a5c6b5 100644
--- a/gpxe/src/drivers/net/sis900.h
+++ b/gpxe/src/drivers/net/sis900.h
@@ -11,6 +11,8 @@
  *   http://www.sis.com.tw/support/databook.htm
  */
 
+FILE_LICENCE ( GPL_ANY );
+
 /* MAC operationl registers of SiS 7016 and SiS 900 ethernet controller */
 /* The I/O extent, SiS 900 needs 256 bytes of io address */
 #define SIS900_TOTAL_SIZE 0x100
diff --git a/gpxe/src/drivers/net/skge.c b/gpxe/src/drivers/net/skge.c
new file mode 100755
index 0000000..afe8273
--- /dev/null
+++ b/gpxe/src/drivers/net/skge.c
@@ -0,0 +1,2472 @@
+/*
+ * gPXE driver for Marvell Yukon chipset and SysKonnect Gigabit
+ * Ethernet adapters. Derived from Linux skge driver (v1.13), which was
+ * based on earlier sk98lin, e100 and FreeBSD if_sk drivers.
+ *
+ * This driver intentionally does not support all the features of the
+ * original driver such as link fail-over and link management because
+ * those should be done at higher levels.
+ *
+ * Copyright (C) 2004, 2005 Stephen Hemminger <shemminger at osdl.org>
+ *
+ * Modified for gPXE, July 2008 by Michael Decker <mrd999 at gmail.com>
+ * Tested and Modified in December 2009 by
+ *    Thomas Miletich <thomas.miletich at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_ONLY );
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/pci.h>
+
+#include "skge.h"
+
+static struct pci_device_id skge_id_table[] = {
+	PCI_ROM(0x10b7, 0x1700,     "3C940",     "3COM 3C940", 0),
+	PCI_ROM(0x10b7, 0x80eb,     "3C940B",    "3COM 3C940", 0),
+	PCI_ROM(0x1148, 0x4300,     "GE",        "Syskonnect GE", 0),
+	PCI_ROM(0x1148, 0x4320,     "YU",        "Syskonnect YU", 0),
+	PCI_ROM(0x1186, 0x4C00,     "DGE510T",   "DLink DGE-510T", 0),
+	PCI_ROM(0x1186, 0x4b01,     "DGE530T",   "DLink DGE-530T", 0),
+	PCI_ROM(0x11ab, 0x4320,     "id4320",    "Marvell id4320", 0),
+	PCI_ROM(0x11ab, 0x5005,     "id5005",    "Marvell id5005", 0), /* Belkin */
+	PCI_ROM(0x1371, 0x434e,     "Gigacard",  "CNET Gigacard", 0),
+	PCI_ROM(0x1737, 0x1064,     "EG1064",    "Linksys EG1064", 0),
+	PCI_ROM(0x1737, 0xffff,     "id_any",    "Linksys [any]", 0)
+};
+
+static int skge_up(struct net_device *dev);
+static void skge_down(struct net_device *dev);
+static void skge_tx_clean(struct net_device *dev);
+static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
+static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
+static void yukon_init(struct skge_hw *hw, int port);
+static void genesis_mac_init(struct skge_hw *hw, int port);
+static void genesis_link_up(struct skge_port *skge);
+
+static void skge_phyirq(struct skge_hw *hw);
+static void skge_poll(struct net_device *dev);
+static int skge_xmit_frame(struct net_device *dev, struct io_buffer *iob);
+static void skge_net_irq ( struct net_device *dev, int enable );
+
+static void skge_rx_refill(struct net_device *dev);
+
+static struct net_device_operations skge_operations = {
+	.open     = skge_up,
+	.close    = skge_down,
+	.transmit = skge_xmit_frame,
+	.poll     = skge_poll,
+	.irq      = skge_net_irq
+};
+
+/* Avoid conditionals by using array */
+static const int txqaddr[] = { Q_XA1, Q_XA2 };
+static const int rxqaddr[] = { Q_R1, Q_R2 };
+static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F };
+static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F };
+static const u32 napimask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F };
+static const u32 portmask[] = { IS_PORT_1, IS_PORT_2 };
+
+/* Determine supported/advertised modes based on hardware.
+ * Note: ethtool ADVERTISED_xxx == SUPPORTED_xxx
+ */
+static u32 skge_supported_modes(const struct skge_hw *hw)
+{
+	u32 supported;
+
+	if (hw->copper) {
+		supported = SUPPORTED_10baseT_Half
+			| SUPPORTED_10baseT_Full
+			| SUPPORTED_100baseT_Half
+			| SUPPORTED_100baseT_Full
+			| SUPPORTED_1000baseT_Half
+			| SUPPORTED_1000baseT_Full
+			| SUPPORTED_Autoneg| SUPPORTED_TP;
+
+		if (hw->chip_id == CHIP_ID_GENESIS)
+			supported &= ~(SUPPORTED_10baseT_Half
+					     | SUPPORTED_10baseT_Full
+					     | SUPPORTED_100baseT_Half
+					     | SUPPORTED_100baseT_Full);
+
+		else if (hw->chip_id == CHIP_ID_YUKON)
+			supported &= ~SUPPORTED_1000baseT_Half;
+	} else
+		supported = SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half
+			| SUPPORTED_FIBRE | SUPPORTED_Autoneg;
+
+	return supported;
+}
+
+/* Chip internal frequency for clock calculations */
+static inline u32 hwkhz(const struct skge_hw *hw)
+{
+	return (hw->chip_id == CHIP_ID_GENESIS) ? 53125 : 78125;
+}
+
+/* Microseconds to chip HZ */
+static inline u32 skge_usecs2clk(const struct skge_hw *hw, u32 usec)
+{
+	return hwkhz(hw) * usec / 1000;
+}
+
+enum led_mode { LED_MODE_OFF, LED_MODE_ON, LED_MODE_TST };
+static void skge_led(struct skge_port *skge, enum led_mode mode)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+
+	if (hw->chip_id == CHIP_ID_GENESIS) {
+		switch (mode) {
+		case LED_MODE_OFF:
+			if (hw->phy_type == SK_PHY_BCOM)
+				xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_OFF);
+			else {
+				skge_write32(hw, SK_REG(port, TX_LED_VAL), 0);
+				skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_T_OFF);
+			}
+			skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
+			skge_write32(hw, SK_REG(port, RX_LED_VAL), 0);
+			skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_T_OFF);
+			break;
+
+		case LED_MODE_ON:
+			skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_ON);
+			skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_LINKSYNC_ON);
+
+			skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
+			skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_START);
+
+			break;
+
+		case LED_MODE_TST:
+			skge_write8(hw, SK_REG(port, RX_LED_TST), LED_T_ON);
+			skge_write32(hw, SK_REG(port, RX_LED_VAL), 100);
+			skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
+
+			if (hw->phy_type == SK_PHY_BCOM)
+				xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_ON);
+			else {
+				skge_write8(hw, SK_REG(port, TX_LED_TST), LED_T_ON);
+				skge_write32(hw, SK_REG(port, TX_LED_VAL), 100);
+				skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_START);
+			}
+
+		}
+	} else {
+		switch (mode) {
+		case LED_MODE_OFF:
+			gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0);
+			gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+				     PHY_M_LED_MO_DUP(MO_LED_OFF)  |
+				     PHY_M_LED_MO_10(MO_LED_OFF)   |
+				     PHY_M_LED_MO_100(MO_LED_OFF)  |
+				     PHY_M_LED_MO_1000(MO_LED_OFF) |
+				     PHY_M_LED_MO_RX(MO_LED_OFF));
+			break;
+		case LED_MODE_ON:
+			gm_phy_write(hw, port, PHY_MARV_LED_CTRL,
+				     PHY_M_LED_PULS_DUR(PULS_170MS) |
+				     PHY_M_LED_BLINK_RT(BLINK_84MS) |
+				     PHY_M_LEDC_TX_CTRL |
+				     PHY_M_LEDC_DP_CTRL);
+
+			gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+				     PHY_M_LED_MO_RX(MO_LED_OFF) |
+				     (skge->speed == SPEED_100 ?
+				      PHY_M_LED_MO_100(MO_LED_ON) : 0));
+			break;
+		case LED_MODE_TST:
+			gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0);
+			gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+				     PHY_M_LED_MO_DUP(MO_LED_ON)  |
+				     PHY_M_LED_MO_10(MO_LED_ON)   |
+				     PHY_M_LED_MO_100(MO_LED_ON)  |
+				     PHY_M_LED_MO_1000(MO_LED_ON) |
+				     PHY_M_LED_MO_RX(MO_LED_ON));
+		}
+	}
+}
+
+/*
+ * I've left in these EEPROM and VPD functions, as someone may desire to
+ * integrate them in the future. -mdeck
+ *
+ * static int skge_get_eeprom_len(struct net_device *dev)
+ * {
+ * 	struct skge_port *skge = netdev_priv(dev);
+ * 	u32 reg2;
+ *
+ * 	pci_read_config_dword(skge->hw->pdev, PCI_DEV_REG2, &reg2);
+ * 	return 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8);
+ * }
+ *
+ * static u32 skge_vpd_read(struct pci_dev *pdev, int cap, u16 offset)
+ * {
+ * 	u32 val;
+ *
+ * 	pci_write_config_word(pdev, cap + PCI_VPD_ADDR, offset);
+ *
+ * 	do {
+ * 		pci_read_config_word(pdev, cap + PCI_VPD_ADDR, &offset);
+ * 	} while (!(offset & PCI_VPD_ADDR_F));
+ *
+ * 	pci_read_config_dword(pdev, cap + PCI_VPD_DATA, &val);
+ * 	return val;
+ * }
+ *
+ * static void skge_vpd_write(struct pci_dev *pdev, int cap, u16 offset, u32 val)
+ * {
+ * 	pci_write_config_dword(pdev, cap + PCI_VPD_DATA, val);
+ * 	pci_write_config_word(pdev, cap + PCI_VPD_ADDR,
+ * 			      offset | PCI_VPD_ADDR_F);
+ *
+ * 	do {
+ * 		pci_read_config_word(pdev, cap + PCI_VPD_ADDR, &offset);
+ * 	} while (offset & PCI_VPD_ADDR_F);
+ * }
+ *
+ * static int skge_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+ * 			   u8 *data)
+ * {
+ * 	struct skge_port *skge = netdev_priv(dev);
+ * 	struct pci_dev *pdev = skge->hw->pdev;
+ * 	int cap = pci_find_capability(pdev, PCI_CAP_ID_VPD);
+ * 	int length = eeprom->len;
+ * 	u16 offset = eeprom->offset;
+ *
+ * 	if (!cap)
+ * 		return -EINVAL;
+ *
+ * 	eeprom->magic = SKGE_EEPROM_MAGIC;
+ *
+ * 	while (length > 0) {
+ * 		u32 val = skge_vpd_read(pdev, cap, offset);
+ * 		int n = min_t(int, length, sizeof(val));
+ *
+ * 		memcpy(data, &val, n);
+ * 		length -= n;
+ * 		data += n;
+ * 		offset += n;
+ * 	}
+ * 	return 0;
+ * }
+ *
+ * static int skge_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+ * 			   u8 *data)
+ * {
+ * 	struct skge_port *skge = netdev_priv(dev);
+ * 	struct pci_dev *pdev = skge->hw->pdev;
+ * 	int cap = pci_find_capability(pdev, PCI_CAP_ID_VPD);
+ * 	int length = eeprom->len;
+ * 	u16 offset = eeprom->offset;
+ *
+ * 	if (!cap)
+ * 		return -EINVAL;
+ *
+ * 	if (eeprom->magic != SKGE_EEPROM_MAGIC)
+ * 		return -EINVAL;
+ *
+ * 	while (length > 0) {
+ * 		u32 val;
+ * 		int n = min_t(int, length, sizeof(val));
+ *
+ * 		if (n < sizeof(val))
+ * 			val = skge_vpd_read(pdev, cap, offset);
+ * 		memcpy(&val, data, n);
+ *
+ * 		skge_vpd_write(pdev, cap, offset, val);
+ *
+ * 		length -= n;
+ * 		data += n;
+ * 		offset += n;
+ * 	}
+ * 	return 0;
+ * }
+ */
+
+/*
+ * Allocate ring elements and chain them together
+ * One-to-one association of board descriptors with ring elements
+ */
+static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u32 base,
+                           size_t num)
+{
+	struct skge_tx_desc *d;
+	struct skge_element *e;
+	unsigned int i;
+
+	ring->start = zalloc(num*sizeof(*e));
+	if (!ring->start)
+		return -ENOMEM;
+
+	for (i = 0, e = ring->start, d = vaddr; i < num; i++, e++, d++) {
+		e->desc = d;
+		if (i == num - 1) {
+			e->next = ring->start;
+			d->next_offset = base;
+		} else {
+			e->next = e + 1;
+			d->next_offset = base + (i+1) * sizeof(*d);
+		}
+	}
+	ring->to_use = ring->to_clean = ring->start;
+
+	return 0;
+}
+
+/* Allocate and setup a new buffer for receiving */
+static void skge_rx_setup(struct skge_port *skge __unused,
+			  struct skge_element *e,
+			  struct io_buffer *iob, unsigned int bufsize)
+{
+	struct skge_rx_desc *rd = e->desc;
+	u64 map;
+
+	map = ( iob != NULL ) ? virt_to_bus(iob->data) : 0;
+
+	rd->dma_lo = map;
+	rd->dma_hi = map >> 32;
+	e->iob = iob;
+	rd->csum1_start = ETH_HLEN;
+	rd->csum2_start = ETH_HLEN;
+	rd->csum1 = 0;
+	rd->csum2 = 0;
+
+	wmb();
+
+	rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize;
+}
+
+/* Resume receiving using existing skb,
+ * Note: DMA address is not changed by chip.
+ * 	 MTU not changed while receiver active.
+ */
+static inline void skge_rx_reuse(struct skge_element *e, unsigned int size)
+{
+	struct skge_rx_desc *rd = e->desc;
+
+	rd->csum2 = 0;
+	rd->csum2_start = ETH_HLEN;
+
+	wmb();
+
+	rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | size;
+}
+
+
+/* Free all  buffers in receive ring, assumes receiver stopped */
+static void skge_rx_clean(struct skge_port *skge)
+{
+	struct skge_ring *ring = &skge->rx_ring;
+	struct skge_element *e;
+
+	e = ring->start;
+	do {
+		struct skge_rx_desc *rd = e->desc;
+		rd->control = 0;
+		if (e->iob) {
+			free_iob(e->iob);
+			e->iob = NULL;
+		}
+	} while ((e = e->next) != ring->start);
+}
+
+static void skge_link_up(struct skge_port *skge)
+{
+	skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG),
+		    LED_BLK_OFF|LED_SYNC_OFF|LED_ON);
+
+	netdev_link_up(skge->netdev);
+
+	DBG2(PFX "%s: Link is up at %d Mbps, %s duplex\n",
+	     skge->netdev->name, skge->speed,
+	     skge->duplex == DUPLEX_FULL ? "full" : "half");
+}
+
+static void skge_link_down(struct skge_port *skge)
+{
+	skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
+	netdev_link_down(skge->netdev);
+
+	DBG2(PFX "%s: Link is down.\n", skge->netdev->name);
+}
+
+
+static void xm_link_down(struct skge_hw *hw, int port)
+{
+	struct net_device *dev = hw->dev[port];
+	struct skge_port *skge = netdev_priv(dev);
+
+	xm_write16(hw, port, XM_IMSK, XM_IMSK_DISABLE);
+
+	if (netdev_link_ok(dev))
+		skge_link_down(skge);
+}
+
+static int __xm_phy_read(struct skge_hw *hw, int port, u16 reg, u16 *val)
+{
+	int i;
+
+	xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr);
+	*val = xm_read16(hw, port, XM_PHY_DATA);
+
+	if (hw->phy_type == SK_PHY_XMAC)
+		goto ready;
+
+	for (i = 0; i < PHY_RETRIES; i++) {
+		if (xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_RDY)
+			goto ready;
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+ ready:
+	*val = xm_read16(hw, port, XM_PHY_DATA);
+
+	return 0;
+}
+
+static u16 xm_phy_read(struct skge_hw *hw, int port, u16 reg)
+{
+	u16 v = 0;
+	if (__xm_phy_read(hw, port, reg, &v))
+		DBG(PFX "%s: phy read timed out\n",
+		       hw->dev[port]->name);
+	return v;
+}
+
+static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val)
+{
+	int i;
+
+	xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr);
+	for (i = 0; i < PHY_RETRIES; i++) {
+		if (!(xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_BUSY))
+			goto ready;
+		udelay(1);
+	}
+	return -EIO;
+
+ ready:
+	xm_write16(hw, port, XM_PHY_DATA, val);
+	for (i = 0; i < PHY_RETRIES; i++) {
+		if (!(xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_BUSY))
+			return 0;
+		udelay(1);
+	}
+	return -ETIMEDOUT;
+}
+
+static void genesis_init(struct skge_hw *hw)
+{
+	/* set blink source counter */
+	skge_write32(hw, B2_BSC_INI, (SK_BLK_DUR * SK_FACT_53) / 100);
+	skge_write8(hw, B2_BSC_CTRL, BSC_START);
+
+	/* configure mac arbiter */
+	skge_write16(hw, B3_MA_TO_CTRL, MA_RST_CLR);
+
+	/* configure mac arbiter timeout values */
+	skge_write8(hw, B3_MA_TOINI_RX1, SK_MAC_TO_53);
+	skge_write8(hw, B3_MA_TOINI_RX2, SK_MAC_TO_53);
+	skge_write8(hw, B3_MA_TOINI_TX1, SK_MAC_TO_53);
+	skge_write8(hw, B3_MA_TOINI_TX2, SK_MAC_TO_53);
+
+	skge_write8(hw, B3_MA_RCINI_RX1, 0);
+	skge_write8(hw, B3_MA_RCINI_RX2, 0);
+	skge_write8(hw, B3_MA_RCINI_TX1, 0);
+	skge_write8(hw, B3_MA_RCINI_TX2, 0);
+
+	/* configure packet arbiter timeout */
+	skge_write16(hw, B3_PA_CTRL, PA_RST_CLR);
+	skge_write16(hw, B3_PA_TOINI_RX1, SK_PKT_TO_MAX);
+	skge_write16(hw, B3_PA_TOINI_TX1, SK_PKT_TO_MAX);
+	skge_write16(hw, B3_PA_TOINI_RX2, SK_PKT_TO_MAX);
+	skge_write16(hw, B3_PA_TOINI_TX2, SK_PKT_TO_MAX);
+}
+
+static void genesis_reset(struct skge_hw *hw, int port)
+{
+	const u8 zero[8]  = { 0 };
+	u32 reg;
+
+	skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+
+	/* reset the statistics module */
+	xm_write32(hw, port, XM_GP_PORT, XM_GP_RES_STAT);
+	xm_write16(hw, port, XM_IMSK, XM_IMSK_DISABLE);
+	xm_write32(hw, port, XM_MODE, 0);		/* clear Mode Reg */
+	xm_write16(hw, port, XM_TX_CMD, 0);	/* reset TX CMD Reg */
+	xm_write16(hw, port, XM_RX_CMD, 0);	/* reset RX CMD Reg */
+
+	/* disable Broadcom PHY IRQ */
+	if (hw->phy_type == SK_PHY_BCOM)
+		xm_write16(hw, port, PHY_BCOM_INT_MASK, 0xffff);
+
+	xm_outhash(hw, port, XM_HSM, zero);
+
+	/* Flush TX and RX fifo */
+	reg = xm_read32(hw, port, XM_MODE);
+	xm_write32(hw, port, XM_MODE, reg | XM_MD_FTF);
+	xm_write32(hw, port, XM_MODE, reg | XM_MD_FRF);
+}
+
+
+/* Convert mode to MII values  */
+static const u16 phy_pause_map[] = {
+	[FLOW_MODE_NONE] =	0,
+	[FLOW_MODE_LOC_SEND] =	PHY_AN_PAUSE_ASYM,
+	[FLOW_MODE_SYMMETRIC] = PHY_AN_PAUSE_CAP,
+	[FLOW_MODE_SYM_OR_REM]  = PHY_AN_PAUSE_CAP | PHY_AN_PAUSE_ASYM,
+};
+
+/* special defines for FIBER (88E1011S only) */
+static const u16 fiber_pause_map[] = {
+	[FLOW_MODE_NONE]	= PHY_X_P_NO_PAUSE,
+	[FLOW_MODE_LOC_SEND]	= PHY_X_P_ASYM_MD,
+	[FLOW_MODE_SYMMETRIC]	= PHY_X_P_SYM_MD,
+	[FLOW_MODE_SYM_OR_REM]	= PHY_X_P_BOTH_MD,
+};
+
+
+/* Check status of Broadcom phy link */
+static void bcom_check_link(struct skge_hw *hw, int port)
+{
+	struct net_device *dev = hw->dev[port];
+	struct skge_port *skge = netdev_priv(dev);
+	u16 status;
+
+	/* read twice because of latch */
+	xm_phy_read(hw, port, PHY_BCOM_STAT);
+	status = xm_phy_read(hw, port, PHY_BCOM_STAT);
+
+	if ((status & PHY_ST_LSYNC) == 0) {
+		xm_link_down(hw, port);
+		return;
+	}
+
+	if (skge->autoneg == AUTONEG_ENABLE) {
+		u16 lpa, aux;
+
+		if (!(status & PHY_ST_AN_OVER))
+			return;
+
+		lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
+		if (lpa & PHY_B_AN_RF) {
+			DBG(PFX "%s: remote fault\n",
+			       dev->name);
+			return;
+		}
+
+		aux = xm_phy_read(hw, port, PHY_BCOM_AUX_STAT);
+
+		/* Check Duplex mismatch */
+		switch (aux & PHY_B_AS_AN_RES_MSK) {
+		case PHY_B_RES_1000FD:
+			skge->duplex = DUPLEX_FULL;
+			break;
+		case PHY_B_RES_1000HD:
+			skge->duplex = DUPLEX_HALF;
+			break;
+		default:
+			DBG(PFX "%s: duplex mismatch\n",
+			       dev->name);
+			return;
+		}
+
+		/* We are using IEEE 802.3z/D5.0 Table 37-4 */
+		switch (aux & PHY_B_AS_PAUSE_MSK) {
+		case PHY_B_AS_PAUSE_MSK:
+			skge->flow_status = FLOW_STAT_SYMMETRIC;
+			break;
+		case PHY_B_AS_PRR:
+			skge->flow_status = FLOW_STAT_REM_SEND;
+			break;
+		case PHY_B_AS_PRT:
+			skge->flow_status = FLOW_STAT_LOC_SEND;
+			break;
+		default:
+			skge->flow_status = FLOW_STAT_NONE;
+		}
+		skge->speed = SPEED_1000;
+	}
+
+	if (!netdev_link_ok(dev))
+		genesis_link_up(skge);
+}
+
+/* Broadcom 5400 only supports giagabit! SysKonnect did not put an additional
+ * Phy on for 100 or 10Mbit operation
+ */
+static void bcom_phy_init(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	unsigned int i;
+	u16 id1, r, ext, ctl;
+
+	/* magic workaround patterns for Broadcom */
+	static const struct {
+		u16 reg;
+		u16 val;
+	} A1hack[] = {
+		{ 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1104 },
+		{ 0x17, 0x0013 }, { 0x15, 0x0404 }, { 0x17, 0x8006 },
+		{ 0x15, 0x0132 }, { 0x17, 0x8006 }, { 0x15, 0x0232 },
+		{ 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 },
+	}, C0hack[] = {
+		{ 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1204 },
+		{ 0x17, 0x0013 }, { 0x15, 0x0A04 }, { 0x18, 0x0420 },
+	};
+
+	/* read Id from external PHY (all have the same address) */
+	id1 = xm_phy_read(hw, port, PHY_XMAC_ID1);
+
+	/* Optimize MDIO transfer by suppressing preamble. */
+	r = xm_read16(hw, port, XM_MMU_CMD);
+	r |=  XM_MMU_NO_PRE;
+	xm_write16(hw, port, XM_MMU_CMD,r);
+
+	switch (id1) {
+	case PHY_BCOM_ID1_C0:
+		/*
+		 * Workaround BCOM Errata for the C0 type.
+		 * Write magic patterns to reserved registers.
+		 */
+		for (i = 0; i < ARRAY_SIZE(C0hack); i++)
+			xm_phy_write(hw, port,
+				     C0hack[i].reg, C0hack[i].val);
+
+		break;
+	case PHY_BCOM_ID1_A1:
+		/*
+		 * Workaround BCOM Errata for the A1 type.
+		 * Write magic patterns to reserved registers.
+		 */
+		for (i = 0; i < ARRAY_SIZE(A1hack); i++)
+			xm_phy_write(hw, port,
+				     A1hack[i].reg, A1hack[i].val);
+		break;
+	}
+
+	/*
+	 * Workaround BCOM Errata (#10523) for all BCom PHYs.
+	 * Disable Power Management after reset.
+	 */
+	r = xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL);
+	r |= PHY_B_AC_DIS_PM;
+	xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL, r);
+
+	/* Dummy read */
+	xm_read16(hw, port, XM_ISRC);
+
+	ext = PHY_B_PEC_EN_LTR; /* enable tx led */
+	ctl = PHY_CT_SP1000;	/* always 1000mbit */
+
+	if (skge->autoneg == AUTONEG_ENABLE) {
+		/*
+		 * Workaround BCOM Errata #1 for the C5 type.
+		 * 1000Base-T Link Acquisition Failure in Slave Mode
+		 * Set Repeater/DTE bit 10 of the 1000Base-T Control Register
+		 */
+		u16 adv = PHY_B_1000C_RD;
+		if (skge->advertising & ADVERTISED_1000baseT_Half)
+			adv |= PHY_B_1000C_AHD;
+		if (skge->advertising & ADVERTISED_1000baseT_Full)
+			adv |= PHY_B_1000C_AFD;
+		xm_phy_write(hw, port, PHY_BCOM_1000T_CTRL, adv);
+
+		ctl |= PHY_CT_ANE | PHY_CT_RE_CFG;
+	} else {
+		if (skge->duplex == DUPLEX_FULL)
+			ctl |= PHY_CT_DUP_MD;
+		/* Force to slave */
+		xm_phy_write(hw, port, PHY_BCOM_1000T_CTRL, PHY_B_1000C_MSE);
+	}
+
+	/* Set autonegotiation pause parameters */
+	xm_phy_write(hw, port, PHY_BCOM_AUNE_ADV,
+		     phy_pause_map[skge->flow_control] | PHY_AN_CSMA);
+
+	xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, ext);
+	xm_phy_write(hw, port, PHY_BCOM_CTRL, ctl);
+
+	/* Use link status change interrupt */
+	xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
+}
+
+static void xm_phy_init(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	u16 ctrl = 0;
+
+	if (skge->autoneg == AUTONEG_ENABLE) {
+		if (skge->advertising & ADVERTISED_1000baseT_Half)
+			ctrl |= PHY_X_AN_HD;
+		if (skge->advertising & ADVERTISED_1000baseT_Full)
+			ctrl |= PHY_X_AN_FD;
+
+		ctrl |= fiber_pause_map[skge->flow_control];
+
+		xm_phy_write(hw, port, PHY_XMAC_AUNE_ADV, ctrl);
+
+		/* Restart Auto-negotiation */
+		ctrl = PHY_CT_ANE | PHY_CT_RE_CFG;
+	} else {
+		/* Set DuplexMode in Config register */
+		if (skge->duplex == DUPLEX_FULL)
+			ctrl |= PHY_CT_DUP_MD;
+		/*
+		 * Do NOT enable Auto-negotiation here. This would hold
+		 * the link down because no IDLEs are transmitted
+		 */
+	}
+
+	xm_phy_write(hw, port, PHY_XMAC_CTRL, ctrl);
+
+	/* Poll PHY for status changes */
+	skge->use_xm_link_timer = 1;
+}
+
+static int xm_check_link(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	u16 status;
+
+	/* read twice because of latch */
+	xm_phy_read(hw, port, PHY_XMAC_STAT);
+	status = xm_phy_read(hw, port, PHY_XMAC_STAT);
+
+	if ((status & PHY_ST_LSYNC) == 0) {
+		xm_link_down(hw, port);
+		return 0;
+	}
+
+	if (skge->autoneg == AUTONEG_ENABLE) {
+		u16 lpa, res;
+
+		if (!(status & PHY_ST_AN_OVER))
+			return 0;
+
+		lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
+		if (lpa & PHY_B_AN_RF) {
+			DBG(PFX "%s: remote fault\n",
+			       dev->name);
+			return 0;
+		}
+
+		res = xm_phy_read(hw, port, PHY_XMAC_RES_ABI);
+
+		/* Check Duplex mismatch */
+		switch (res & (PHY_X_RS_HD | PHY_X_RS_FD)) {
+		case PHY_X_RS_FD:
+			skge->duplex = DUPLEX_FULL;
+			break;
+		case PHY_X_RS_HD:
+			skge->duplex = DUPLEX_HALF;
+			break;
+		default:
+			DBG(PFX "%s: duplex mismatch\n",
+			       dev->name);
+			return 0;
+		}
+
+		/* We are using IEEE 802.3z/D5.0 Table 37-4 */
+		if ((skge->flow_control == FLOW_MODE_SYMMETRIC ||
+		     skge->flow_control == FLOW_MODE_SYM_OR_REM) &&
+		    (lpa & PHY_X_P_SYM_MD))
+			skge->flow_status = FLOW_STAT_SYMMETRIC;
+		else if (skge->flow_control == FLOW_MODE_SYM_OR_REM &&
+			 (lpa & PHY_X_RS_PAUSE) == PHY_X_P_ASYM_MD)
+			/* Enable PAUSE receive, disable PAUSE transmit */
+			skge->flow_status  = FLOW_STAT_REM_SEND;
+		else if (skge->flow_control == FLOW_MODE_LOC_SEND &&
+			 (lpa & PHY_X_RS_PAUSE) == PHY_X_P_BOTH_MD)
+			/* Disable PAUSE receive, enable PAUSE transmit */
+			skge->flow_status = FLOW_STAT_LOC_SEND;
+		else
+			skge->flow_status = FLOW_STAT_NONE;
+
+		skge->speed = SPEED_1000;
+	}
+
+	if (!netdev_link_ok(dev))
+		genesis_link_up(skge);
+	return 1;
+}
+
+/* Poll to check for link coming up.
+ *
+ * Since internal PHY is wired to a level triggered pin, can't
+ * get an interrupt when carrier is detected, need to poll for
+ * link coming up.
+ */
+static void xm_link_timer(struct skge_port *skge)
+{
+	struct net_device *dev = skge->netdev;
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	int i;
+
+	/*
+	 * Verify that the link by checking GPIO register three times.
+	 * This pin has the signal from the link_sync pin connected to it.
+	 */
+	for (i = 0; i < 3; i++) {
+		if (xm_read16(hw, port, XM_GP_PORT) & XM_GP_INP_ASS)
+			return;
+	}
+
+        /* Re-enable interrupt to detect link down */
+	if (xm_check_link(dev)) {
+		u16 msk = xm_read16(hw, port, XM_IMSK);
+		msk &= ~XM_IS_INP_ASS;
+		xm_write16(hw, port, XM_IMSK, msk);
+		xm_read16(hw, port, XM_ISRC);
+	}
+}
+
+static void genesis_mac_init(struct skge_hw *hw, int port)
+{
+	struct net_device *dev = hw->dev[port];
+	struct skge_port *skge = netdev_priv(dev);
+	int i;
+	u32 r;
+	const u8 zero[6]  = { 0 };
+
+	for (i = 0; i < 10; i++) {
+		skge_write16(hw, SK_REG(port, TX_MFF_CTRL1),
+			     MFF_SET_MAC_RST);
+		if (skge_read16(hw, SK_REG(port, TX_MFF_CTRL1)) & MFF_SET_MAC_RST)
+			goto reset_ok;
+		udelay(1);
+	}
+
+	DBG(PFX "%s: genesis reset failed\n", dev->name);
+
+ reset_ok:
+	/* Unreset the XMAC. */
+	skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_CLR_MAC_RST);
+
+	/*
+	 * Perform additional initialization for external PHYs,
+	 * namely for the 1000baseTX cards that use the XMAC's
+	 * GMII mode.
+	 */
+	if (hw->phy_type != SK_PHY_XMAC) {
+		/* Take external Phy out of reset */
+		r = skge_read32(hw, B2_GP_IO);
+		if (port == 0)
+			r |= GP_DIR_0|GP_IO_0;
+		else
+			r |= GP_DIR_2|GP_IO_2;
+
+		skge_write32(hw, B2_GP_IO, r);
+
+		/* Enable GMII interface */
+		xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
+	}
+
+
+	switch(hw->phy_type) {
+	case SK_PHY_XMAC:
+		xm_phy_init(skge);
+		break;
+	case SK_PHY_BCOM:
+		bcom_phy_init(skge);
+		bcom_check_link(hw, port);
+	}
+
+	/* Set Station Address */
+	xm_outaddr(hw, port, XM_SA, dev->ll_addr);
+
+	/* We don't use match addresses so clear */
+	for (i = 1; i < 16; i++)
+		xm_outaddr(hw, port, XM_EXM(i), zero);
+
+	/* Clear MIB counters */
+	xm_write16(hw, port, XM_STAT_CMD,
+			XM_SC_CLR_RXC | XM_SC_CLR_TXC);
+	/* Clear two times according to Errata #3 */
+	xm_write16(hw, port, XM_STAT_CMD,
+			XM_SC_CLR_RXC | XM_SC_CLR_TXC);
+
+	/* configure Rx High Water Mark (XM_RX_HI_WM) */
+	xm_write16(hw, port, XM_RX_HI_WM, 1450);
+
+	/* We don't need the FCS appended to the packet. */
+	r = XM_RX_LENERR_OK | XM_RX_STRIP_FCS;
+
+	if (skge->duplex == DUPLEX_HALF) {
+		/*
+		 * If in manual half duplex mode the other side might be in
+		 * full duplex mode, so ignore if a carrier extension is not seen
+		 * on frames received
+		 */
+		r |= XM_RX_DIS_CEXT;
+	}
+	xm_write16(hw, port, XM_RX_CMD, r);
+
+	/* We want short frames padded to 60 bytes. */
+	xm_write16(hw, port, XM_TX_CMD, XM_TX_AUTO_PAD);
+
+	xm_write16(hw, port, XM_TX_THR, 512);
+
+	/*
+	 * Enable the reception of all error frames. This is is
+	 * a necessary evil due to the design of the XMAC. The
+	 * XMAC's receive FIFO is only 8K in size, however jumbo
+	 * frames can be up to 9000 bytes in length. When bad
+	 * frame filtering is enabled, the XMAC's RX FIFO operates
+	 * in 'store and forward' mode. For this to work, the
+	 * entire frame has to fit into the FIFO, but that means
+	 * that jumbo frames larger than 8192 bytes will be
+	 * truncated. Disabling all bad frame filtering causes
+	 * the RX FIFO to operate in streaming mode, in which
+	 * case the XMAC will start transferring frames out of the
+	 * RX FIFO as soon as the FIFO threshold is reached.
+	 */
+	xm_write32(hw, port, XM_MODE, XM_DEF_MODE);
+
+
+	/*
+	 * Initialize the Receive Counter Event Mask (XM_RX_EV_MSK)
+	 *	- Enable all bits excepting 'Octets Rx OK Low CntOv'
+	 *	  and 'Octets Rx OK Hi Cnt Ov'.
+	 */
+	xm_write32(hw, port, XM_RX_EV_MSK, XMR_DEF_MSK);
+
+	/*
+	 * Initialize the Transmit Counter Event Mask (XM_TX_EV_MSK)
+	 *	- Enable all bits excepting 'Octets Tx OK Low CntOv'
+	 *	  and 'Octets Tx OK Hi Cnt Ov'.
+	 */
+	xm_write32(hw, port, XM_TX_EV_MSK, XMT_DEF_MSK);
+
+	/* Configure MAC arbiter */
+	skge_write16(hw, B3_MA_TO_CTRL, MA_RST_CLR);
+
+	/* configure timeout values */
+	skge_write8(hw, B3_MA_TOINI_RX1, 72);
+	skge_write8(hw, B3_MA_TOINI_RX2, 72);
+	skge_write8(hw, B3_MA_TOINI_TX1, 72);
+	skge_write8(hw, B3_MA_TOINI_TX2, 72);
+
+	skge_write8(hw, B3_MA_RCINI_RX1, 0);
+	skge_write8(hw, B3_MA_RCINI_RX2, 0);
+	skge_write8(hw, B3_MA_RCINI_TX1, 0);
+	skge_write8(hw, B3_MA_RCINI_TX2, 0);
+
+	/* Configure Rx MAC FIFO */
+	skge_write8(hw, SK_REG(port, RX_MFF_CTRL2), MFF_RST_CLR);
+	skge_write16(hw, SK_REG(port, RX_MFF_CTRL1), MFF_ENA_TIM_PAT);
+	skge_write8(hw, SK_REG(port, RX_MFF_CTRL2), MFF_ENA_OP_MD);
+
+	/* Configure Tx MAC FIFO */
+	skge_write8(hw, SK_REG(port, TX_MFF_CTRL2), MFF_RST_CLR);
+	skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_TX_CTRL_DEF);
+	skge_write8(hw, SK_REG(port, TX_MFF_CTRL2), MFF_ENA_OP_MD);
+
+	/* enable timeout timers */
+	skge_write16(hw, B3_PA_CTRL,
+		     (port == 0) ? PA_ENA_TO_TX1 : PA_ENA_TO_TX2);
+}
+
+static void genesis_stop(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	unsigned retries = 1000;
+	u16 cmd;
+
+	/* Disable Tx and Rx */
+	cmd = xm_read16(hw, port, XM_MMU_CMD);
+	cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
+	xm_write16(hw, port, XM_MMU_CMD, cmd);
+
+	genesis_reset(hw, port);
+
+	/* Clear Tx packet arbiter timeout IRQ */
+	skge_write16(hw, B3_PA_CTRL,
+		     port == 0 ? PA_CLR_TO_TX1 : PA_CLR_TO_TX2);
+
+	/* Reset the MAC */
+	skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_CLR_MAC_RST);
+	do {
+		skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_SET_MAC_RST);
+		if (!(skge_read16(hw, SK_REG(port, TX_MFF_CTRL1)) & MFF_SET_MAC_RST))
+			break;
+	} while (--retries > 0);
+
+	/* For external PHYs there must be special handling */
+	if (hw->phy_type != SK_PHY_XMAC) {
+		u32 reg = skge_read32(hw, B2_GP_IO);
+		if (port == 0) {
+			reg |= GP_DIR_0;
+			reg &= ~GP_IO_0;
+		} else {
+			reg |= GP_DIR_2;
+			reg &= ~GP_IO_2;
+		}
+		skge_write32(hw, B2_GP_IO, reg);
+		skge_read32(hw, B2_GP_IO);
+	}
+
+	xm_write16(hw, port, XM_MMU_CMD,
+			xm_read16(hw, port, XM_MMU_CMD)
+			& ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX));
+
+	xm_read16(hw, port, XM_MMU_CMD);
+}
+
+static void genesis_link_up(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	u16 cmd, msk;
+	u32 mode;
+
+	cmd = xm_read16(hw, port, XM_MMU_CMD);
+
+	/*
+	 * enabling pause frame reception is required for 1000BT
+	 * because the XMAC is not reset if the link is going down
+	 */
+	if (skge->flow_status == FLOW_STAT_NONE ||
+	    skge->flow_status == FLOW_STAT_LOC_SEND)
+		/* Disable Pause Frame Reception */
+		cmd |= XM_MMU_IGN_PF;
+	else
+		/* Enable Pause Frame Reception */
+		cmd &= ~XM_MMU_IGN_PF;
+
+	xm_write16(hw, port, XM_MMU_CMD, cmd);
+
+	mode = xm_read32(hw, port, XM_MODE);
+	if (skge->flow_status== FLOW_STAT_SYMMETRIC ||
+	    skge->flow_status == FLOW_STAT_LOC_SEND) {
+		/*
+		 * Configure Pause Frame Generation
+		 * Use internal and external Pause Frame Generation.
+		 * Sending pause frames is edge triggered.
+		 * Send a Pause frame with the maximum pause time if
+		 * internal oder external FIFO full condition occurs.
+		 * Send a zero pause time frame to re-start transmission.
+		 */
+		/* XM_PAUSE_DA = '010000C28001' (default) */
+		/* XM_MAC_PTIME = 0xffff (maximum) */
+		/* remember this value is defined in big endian (!) */
+		xm_write16(hw, port, XM_MAC_PTIME, 0xffff);
+
+		mode |= XM_PAUSE_MODE;
+		skge_write16(hw, SK_REG(port, RX_MFF_CTRL1), MFF_ENA_PAUSE);
+	} else {
+		/*
+		 * disable pause frame generation is required for 1000BT
+		 * because the XMAC is not reset if the link is going down
+		 */
+		/* Disable Pause Mode in Mode Register */
+		mode &= ~XM_PAUSE_MODE;
+
+		skge_write16(hw, SK_REG(port, RX_MFF_CTRL1), MFF_DIS_PAUSE);
+	}
+
+	xm_write32(hw, port, XM_MODE, mode);
+
+	/* Turn on detection of Tx underrun */
+	msk = xm_read16(hw, port, XM_IMSK);
+	msk &= ~XM_IS_TXF_UR;
+	xm_write16(hw, port, XM_IMSK, msk);
+
+	xm_read16(hw, port, XM_ISRC);
+
+	/* get MMU Command Reg. */
+	cmd = xm_read16(hw, port, XM_MMU_CMD);
+	if (hw->phy_type != SK_PHY_XMAC && skge->duplex == DUPLEX_FULL)
+		cmd |= XM_MMU_GMII_FD;
+
+	/*
+	 * Workaround BCOM Errata (#10523) for all BCom Phys
+	 * Enable Power Management after link up
+	 */
+	if (hw->phy_type == SK_PHY_BCOM) {
+		xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
+			     xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL)
+			     & ~PHY_B_AC_DIS_PM);
+		xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
+	}
+
+	/* enable Rx/Tx */
+	xm_write16(hw, port, XM_MMU_CMD,
+			cmd | XM_MMU_ENA_RX | XM_MMU_ENA_TX);
+	skge_link_up(skge);
+}
+
+
+static inline void bcom_phy_intr(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	u16 isrc;
+
+	isrc = xm_phy_read(hw, port, PHY_BCOM_INT_STAT);
+	DBGIO(PFX "%s: phy interrupt status 0x%x\n",
+	     skge->netdev->name, isrc);
+
+	if (isrc & PHY_B_IS_PSE)
+		DBG(PFX "%s: uncorrectable pair swap error\n",
+		    hw->dev[port]->name);
+
+	/* Workaround BCom Errata:
+	 *	enable and disable loopback mode if "NO HCD" occurs.
+	 */
+	if (isrc & PHY_B_IS_NO_HDCL) {
+		u16 ctrl = xm_phy_read(hw, port, PHY_BCOM_CTRL);
+		xm_phy_write(hw, port, PHY_BCOM_CTRL,
+				  ctrl | PHY_CT_LOOP);
+		xm_phy_write(hw, port, PHY_BCOM_CTRL,
+				  ctrl & ~PHY_CT_LOOP);
+	}
+
+	if (isrc & (PHY_B_IS_AN_PR | PHY_B_IS_LST_CHANGE))
+		bcom_check_link(hw, port);
+
+}
+
+static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val)
+{
+	int i;
+
+	gma_write16(hw, port, GM_SMI_DATA, val);
+	gma_write16(hw, port, GM_SMI_CTRL,
+			 GM_SMI_CT_PHY_AD(hw->phy_addr) | GM_SMI_CT_REG_AD(reg));
+	for (i = 0; i < PHY_RETRIES; i++) {
+		udelay(1);
+
+		if (!(gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY))
+			return 0;
+	}
+
+	DBG(PFX "%s: phy write timeout port %x reg %x val %x\n",
+	    hw->dev[port]->name,
+	    port, reg, val);
+	return -EIO;
+}
+
+static int __gm_phy_read(struct skge_hw *hw, int port, u16 reg, u16 *val)
+{
+	int i;
+
+	gma_write16(hw, port, GM_SMI_CTRL,
+			 GM_SMI_CT_PHY_AD(hw->phy_addr)
+			 | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD);
+
+	for (i = 0; i < PHY_RETRIES; i++) {
+		udelay(1);
+		if (gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL)
+			goto ready;
+	}
+
+	return -ETIMEDOUT;
+ ready:
+	*val = gma_read16(hw, port, GM_SMI_DATA);
+	return 0;
+}
+
+static u16 gm_phy_read(struct skge_hw *hw, int port, u16 reg)
+{
+	u16 v = 0;
+	if (__gm_phy_read(hw, port, reg, &v))
+		DBG(PFX "%s: phy read timeout port %x reg %x val %x\n",
+	       hw->dev[port]->name,
+	       port, reg, v);
+	return v;
+}
+
+/* Marvell Phy Initialization */
+static void yukon_init(struct skge_hw *hw, int port)
+{
+	struct skge_port *skge = netdev_priv(hw->dev[port]);
+	u16 ctrl, ct1000, adv;
+
+	if (skge->autoneg == AUTONEG_ENABLE) {
+		u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
+
+		ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK |
+			  PHY_M_EC_MAC_S_MSK);
+		ectrl |= PHY_M_EC_MAC_S(MAC_TX_CLK_25_MHZ);
+
+		ectrl |= PHY_M_EC_M_DSC(0) | PHY_M_EC_S_DSC(1);
+
+		gm_phy_write(hw, port, PHY_MARV_EXT_CTRL, ectrl);
+	}
+
+	ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
+	if (skge->autoneg == AUTONEG_DISABLE)
+		ctrl &= ~PHY_CT_ANE;
+
+	ctrl |= PHY_CT_RESET;
+	gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+
+	ctrl = 0;
+	ct1000 = 0;
+	adv = PHY_AN_CSMA;
+
+	if (skge->autoneg == AUTONEG_ENABLE) {
+		if (hw->copper) {
+			if (skge->advertising & ADVERTISED_1000baseT_Full)
+				ct1000 |= PHY_M_1000C_AFD;
+			if (skge->advertising & ADVERTISED_1000baseT_Half)
+				ct1000 |= PHY_M_1000C_AHD;
+			if (skge->advertising & ADVERTISED_100baseT_Full)
+				adv |= PHY_M_AN_100_FD;
+			if (skge->advertising & ADVERTISED_100baseT_Half)
+				adv |= PHY_M_AN_100_HD;
+			if (skge->advertising & ADVERTISED_10baseT_Full)
+				adv |= PHY_M_AN_10_FD;
+			if (skge->advertising & ADVERTISED_10baseT_Half)
+				adv |= PHY_M_AN_10_HD;
+
+			/* Set Flow-control capabilities */
+			adv |= phy_pause_map[skge->flow_control];
+		} else {
+			if (skge->advertising & ADVERTISED_1000baseT_Full)
+				adv |= PHY_M_AN_1000X_AFD;
+			if (skge->advertising & ADVERTISED_1000baseT_Half)
+				adv |= PHY_M_AN_1000X_AHD;
+
+			adv |= fiber_pause_map[skge->flow_control];
+		}
+
+		/* Restart Auto-negotiation */
+		ctrl |= PHY_CT_ANE | PHY_CT_RE_CFG;
+	} else {
+		/* forced speed/duplex settings */
+		ct1000 = PHY_M_1000C_MSE;
+
+		if (skge->duplex == DUPLEX_FULL)
+			ctrl |= PHY_CT_DUP_MD;
+
+		switch (skge->speed) {
+		case SPEED_1000:
+			ctrl |= PHY_CT_SP1000;
+			break;
+		case SPEED_100:
+			ctrl |= PHY_CT_SP100;
+			break;
+		}
+
+		ctrl |= PHY_CT_RESET;
+	}
+
+	gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, ct1000);
+
+	gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, adv);
+	gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+
+	/* Enable phy interrupt on autonegotiation complete (or link up) */
+	if (skge->autoneg == AUTONEG_ENABLE)
+		gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_MSK);
+	else
+		gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_DEF_MSK);
+}
+
+static void yukon_reset(struct skge_hw *hw, int port)
+{
+	gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);/* disable PHY IRQs */
+	gma_write16(hw, port, GM_MC_ADDR_H1, 0);	/* clear MC hash */
+	gma_write16(hw, port, GM_MC_ADDR_H2, 0);
+	gma_write16(hw, port, GM_MC_ADDR_H3, 0);
+	gma_write16(hw, port, GM_MC_ADDR_H4, 0);
+
+	gma_write16(hw, port, GM_RX_CTRL,
+			 gma_read16(hw, port, GM_RX_CTRL)
+			 | GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
+}
+
+/* Apparently, early versions of Yukon-Lite had wrong chip_id? */
+static int is_yukon_lite_a0(struct skge_hw *hw)
+{
+	u32 reg;
+	int ret;
+
+	if (hw->chip_id != CHIP_ID_YUKON)
+		return 0;
+
+	reg = skge_read32(hw, B2_FAR);
+	skge_write8(hw, B2_FAR + 3, 0xff);
+	ret = (skge_read8(hw, B2_FAR + 3) != 0);
+	skge_write32(hw, B2_FAR, reg);
+	return ret;
+}
+
+static void yukon_mac_init(struct skge_hw *hw, int port)
+{
+	struct skge_port *skge = netdev_priv(hw->dev[port]);
+	int i;
+	u32 reg;
+	const u8 *addr = hw->dev[port]->ll_addr;
+
+	/* WA code for COMA mode -- set PHY reset */
+	if (hw->chip_id == CHIP_ID_YUKON_LITE &&
+	    hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
+		reg = skge_read32(hw, B2_GP_IO);
+		reg |= GP_DIR_9 | GP_IO_9;
+		skge_write32(hw, B2_GP_IO, reg);
+	}
+
+	/* hard reset */
+	skge_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+	skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
+
+	/* WA code for COMA mode -- clear PHY reset */
+	if (hw->chip_id == CHIP_ID_YUKON_LITE &&
+	    hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
+		reg = skge_read32(hw, B2_GP_IO);
+		reg |= GP_DIR_9;
+		reg &= ~GP_IO_9;
+		skge_write32(hw, B2_GP_IO, reg);
+	}
+
+	/* Set hardware config mode */
+	reg = GPC_INT_POL_HI | GPC_DIS_FC | GPC_DIS_SLEEP |
+		GPC_ENA_XC | GPC_ANEG_ADV_ALL_M | GPC_ENA_PAUSE;
+	reg |= hw->copper ? GPC_HWCFG_GMII_COP : GPC_HWCFG_GMII_FIB;
+
+	/* Clear GMC reset */
+	skge_write32(hw, SK_REG(port, GPHY_CTRL), reg | GPC_RST_SET);
+	skge_write32(hw, SK_REG(port, GPHY_CTRL), reg | GPC_RST_CLR);
+	skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON | GMC_RST_CLR);
+
+	if (skge->autoneg == AUTONEG_DISABLE) {
+		reg = GM_GPCR_AU_ALL_DIS;
+		gma_write16(hw, port, GM_GP_CTRL,
+				 gma_read16(hw, port, GM_GP_CTRL) | reg);
+
+		switch (skge->speed) {
+		case SPEED_1000:
+			reg &= ~GM_GPCR_SPEED_100;
+			reg |= GM_GPCR_SPEED_1000;
+			break;
+		case SPEED_100:
+			reg &= ~GM_GPCR_SPEED_1000;
+			reg |= GM_GPCR_SPEED_100;
+			break;
+		case SPEED_10:
+			reg &= ~(GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100);
+			break;
+		}
+
+		if (skge->duplex == DUPLEX_FULL)
+			reg |= GM_GPCR_DUP_FULL;
+	} else
+		reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL;
+
+	switch (skge->flow_control) {
+	case FLOW_MODE_NONE:
+		skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+		reg |= GM_GPCR_FC_TX_DIS | GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS;
+		break;
+	case FLOW_MODE_LOC_SEND:
+		/* disable Rx flow-control */
+		reg |= GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS;
+		break;
+	case FLOW_MODE_SYMMETRIC:
+	case FLOW_MODE_SYM_OR_REM:
+		/* enable Tx & Rx flow-control */
+		break;
+	}
+
+	gma_write16(hw, port, GM_GP_CTRL, reg);
+	skge_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
+
+	yukon_init(hw, port);
+
+	/* MIB clear */
+	reg = gma_read16(hw, port, GM_PHY_ADDR);
+	gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR);
+
+	for (i = 0; i < GM_MIB_CNT_SIZE; i++)
+		gma_read16(hw, port, GM_MIB_CNT_BASE + 8*i);
+	gma_write16(hw, port, GM_PHY_ADDR, reg);
+
+	/* transmit control */
+	gma_write16(hw, port, GM_TX_CTRL, TX_COL_THR(TX_COL_DEF));
+
+	/* receive control reg: unicast + multicast + no FCS  */
+	gma_write16(hw, port, GM_RX_CTRL,
+			 GM_RXCR_UCF_ENA | GM_RXCR_CRC_DIS | GM_RXCR_MCF_ENA);
+
+	/* transmit flow control */
+	gma_write16(hw, port, GM_TX_FLOW_CTRL, 0xffff);
+
+	/* transmit parameter */
+	gma_write16(hw, port, GM_TX_PARAM,
+			 TX_JAM_LEN_VAL(TX_JAM_LEN_DEF) |
+			 TX_JAM_IPG_VAL(TX_JAM_IPG_DEF) |
+			 TX_IPG_JAM_DATA(TX_IPG_JAM_DEF));
+
+	/* configure the Serial Mode Register */
+	reg = DATA_BLIND_VAL(DATA_BLIND_DEF)
+		| GM_SMOD_VLAN_ENA
+		| IPG_DATA_VAL(IPG_DATA_DEF);
+
+	gma_write16(hw, port, GM_SERIAL_MODE, reg);
+
+	/* physical address: used for pause frames */
+	gma_set_addr(hw, port, GM_SRC_ADDR_1L, addr);
+	/* virtual address for data */
+	gma_set_addr(hw, port, GM_SRC_ADDR_2L, addr);
+
+	/* enable interrupt mask for counter overflows */
+	gma_write16(hw, port, GM_TX_IRQ_MSK, 0);
+	gma_write16(hw, port, GM_RX_IRQ_MSK, 0);
+	gma_write16(hw, port, GM_TR_IRQ_MSK, 0);
+
+	/* Initialize Mac Fifo */
+
+	/* Configure Rx MAC FIFO */
+	skge_write16(hw, SK_REG(port, RX_GMF_FL_MSK), RX_FF_FL_DEF_MSK);
+	reg = GMF_OPER_ON | GMF_RX_F_FL_ON;
+
+	/* disable Rx GMAC FIFO Flush for YUKON-Lite Rev. A0 only */
+	if (is_yukon_lite_a0(hw))
+		reg &= ~GMF_RX_F_FL_ON;
+
+	skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR);
+	skge_write16(hw, SK_REG(port, RX_GMF_CTRL_T), reg);
+	/*
+	 * because Pause Packet Truncation in GMAC is not working
+	 * we have to increase the Flush Threshold to 64 bytes
+	 * in order to flush pause packets in Rx FIFO on Yukon-1
+	 */
+	skge_write16(hw, SK_REG(port, RX_GMF_FL_THR), RX_GMF_FL_THR_DEF+1);
+
+	/* Configure Tx MAC FIFO */
+	skge_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR);
+	skge_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON);
+}
+
+/* Go into power down mode */
+static void yukon_suspend(struct skge_hw *hw, int port)
+{
+	u16 ctrl;
+
+	ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+	ctrl |= PHY_M_PC_POL_R_DIS;
+	gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+	ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
+	ctrl |= PHY_CT_RESET;
+	gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+
+	/* switch IEEE compatible power down mode on */
+	ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
+	ctrl |= PHY_CT_PDOWN;
+	gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+}
+
+static void yukon_stop(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+
+	skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+	yukon_reset(hw, port);
+
+	gma_write16(hw, port, GM_GP_CTRL,
+			 gma_read16(hw, port, GM_GP_CTRL)
+			 & ~(GM_GPCR_TX_ENA|GM_GPCR_RX_ENA));
+	gma_read16(hw, port, GM_GP_CTRL);
+
+	yukon_suspend(hw, port);
+
+	/* set GPHY Control reset */
+	skge_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+	skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
+}
+
+static u16 yukon_speed(const struct skge_hw *hw __unused, u16 aux)
+{
+	switch (aux & PHY_M_PS_SPEED_MSK) {
+	case PHY_M_PS_SPEED_1000:
+		return SPEED_1000;
+	case PHY_M_PS_SPEED_100:
+		return SPEED_100;
+	default:
+		return SPEED_10;
+	}
+}
+
+static void yukon_link_up(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	u16 reg;
+
+	/* Enable Transmit FIFO Underrun */
+	skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);
+
+	reg = gma_read16(hw, port, GM_GP_CTRL);
+	if (skge->duplex == DUPLEX_FULL || skge->autoneg == AUTONEG_ENABLE)
+		reg |= GM_GPCR_DUP_FULL;
+
+	/* enable Rx/Tx */
+	reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA;
+	gma_write16(hw, port, GM_GP_CTRL, reg);
+
+	gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_DEF_MSK);
+	skge_link_up(skge);
+}
+
+static void yukon_link_down(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	u16 ctrl;
+
+	ctrl = gma_read16(hw, port, GM_GP_CTRL);
+	ctrl &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
+	gma_write16(hw, port, GM_GP_CTRL, ctrl);
+
+	if (skge->flow_status == FLOW_STAT_REM_SEND) {
+		ctrl = gm_phy_read(hw, port, PHY_MARV_AUNE_ADV);
+		ctrl |= PHY_M_AN_ASP;
+		/* restore Asymmetric Pause bit */
+		gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, ctrl);
+	}
+
+	skge_link_down(skge);
+
+	yukon_init(hw, port);
+}
+
+static void yukon_phy_intr(struct skge_port *skge)
+{
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	const char *reason = NULL;
+	u16 istatus, phystat;
+
+	istatus = gm_phy_read(hw, port, PHY_MARV_INT_STAT);
+	phystat = gm_phy_read(hw, port, PHY_MARV_PHY_STAT);
+
+	DBGIO(PFX "%s: phy interrupt status 0x%x 0x%x\n",
+	     skge->netdev->name, istatus, phystat);
+
+	if (istatus & PHY_M_IS_AN_COMPL) {
+		if (gm_phy_read(hw, port, PHY_MARV_AUNE_LP)
+		    & PHY_M_AN_RF) {
+			reason = "remote fault";
+			goto failed;
+		}
+
+		if (gm_phy_read(hw, port, PHY_MARV_1000T_STAT) & PHY_B_1000S_MSF) {
+			reason = "master/slave fault";
+			goto failed;
+		}
+
+		if (!(phystat & PHY_M_PS_SPDUP_RES)) {
+			reason = "speed/duplex";
+			goto failed;
+		}
+
+		skge->duplex = (phystat & PHY_M_PS_FULL_DUP)
+			? DUPLEX_FULL : DUPLEX_HALF;
+		skge->speed = yukon_speed(hw, phystat);
+
+		/* We are using IEEE 802.3z/D5.0 Table 37-4 */
+		switch (phystat & PHY_M_PS_PAUSE_MSK) {
+		case PHY_M_PS_PAUSE_MSK:
+			skge->flow_status = FLOW_STAT_SYMMETRIC;
+			break;
+		case PHY_M_PS_RX_P_EN:
+			skge->flow_status = FLOW_STAT_REM_SEND;
+			break;
+		case PHY_M_PS_TX_P_EN:
+			skge->flow_status = FLOW_STAT_LOC_SEND;
+			break;
+		default:
+			skge->flow_status = FLOW_STAT_NONE;
+		}
+
+		if (skge->flow_status == FLOW_STAT_NONE ||
+		    (skge->speed < SPEED_1000 && skge->duplex == DUPLEX_HALF))
+			skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+		else
+			skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
+		yukon_link_up(skge);
+		return;
+	}
+
+	if (istatus & PHY_M_IS_LSP_CHANGE)
+		skge->speed = yukon_speed(hw, phystat);
+
+	if (istatus & PHY_M_IS_DUP_CHANGE)
+		skge->duplex = (phystat & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF;
+	if (istatus & PHY_M_IS_LST_CHANGE) {
+		if (phystat & PHY_M_PS_LINK_UP)
+			yukon_link_up(skge);
+		else
+			yukon_link_down(skge);
+	}
+	return;
+ failed:
+	DBG(PFX "%s: autonegotiation failed (%s)\n",
+	       skge->netdev->name, reason);
+
+	/* XXX restart autonegotiation? */
+}
+
+static void skge_ramset(struct skge_hw *hw, u16 q, u32 start, size_t len)
+{
+	u32 end;
+
+	start /= 8;
+	len /= 8;
+	end = start + len - 1;
+
+	skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR);
+	skge_write32(hw, RB_ADDR(q, RB_START), start);
+	skge_write32(hw, RB_ADDR(q, RB_WP), start);
+	skge_write32(hw, RB_ADDR(q, RB_RP), start);
+	skge_write32(hw, RB_ADDR(q, RB_END), end);
+
+	if (q == Q_R1 || q == Q_R2) {
+		/* Set thresholds on receive queue's */
+		skge_write32(hw, RB_ADDR(q, RB_RX_UTPP),
+			     start + (2*len)/3);
+		skge_write32(hw, RB_ADDR(q, RB_RX_LTPP),
+			     start + (len/3));
+	} else {
+		/* Enable store & forward on Tx queue's because
+		 * Tx FIFO is only 4K on Genesis and 1K on Yukon
+		 */
+		skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_STFWD);
+	}
+
+	skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD);
+}
+
+/* Setup Bus Memory Interface */
+static void skge_qset(struct skge_port *skge, u16 q,
+		      const struct skge_element *e)
+{
+	struct skge_hw *hw = skge->hw;
+	u32 watermark = 0x600;
+	u64 base = skge->dma + (e->desc - skge->mem);
+
+	/* optimization to reduce window on 32bit/33mhz */
+	if ((skge_read16(hw, B0_CTST) & (CS_BUS_CLOCK | CS_BUS_SLOT_SZ)) == 0)
+		watermark /= 2;
+
+	skge_write32(hw, Q_ADDR(q, Q_CSR), CSR_CLR_RESET);
+	skge_write32(hw, Q_ADDR(q, Q_F), watermark);
+	skge_write32(hw, Q_ADDR(q, Q_DA_H), (u32)(base >> 32));
+	skge_write32(hw, Q_ADDR(q, Q_DA_L), (u32)base);
+}
+
+void skge_free(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+
+	free(skge->rx_ring.start);
+	skge->rx_ring.start = NULL;
+
+	free(skge->tx_ring.start);
+	skge->tx_ring.start = NULL;
+
+	free_dma(skge->mem, RING_SIZE);
+	skge->mem = NULL;
+	skge->dma = 0;
+}
+
+static int skge_up(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+	u32 chunk, ram_addr;
+	int err;
+
+	DBG2(PFX "%s: enabling interface\n", dev->name);
+
+	skge->mem = malloc_dma(RING_SIZE, SKGE_RING_ALIGN);
+	skge->dma = virt_to_bus(skge->mem);
+	if (!skge->mem)
+		return -ENOMEM;
+	memset(skge->mem, 0, RING_SIZE);
+
+	assert(!(skge->dma & 7));
+
+	/* FIXME: find out whether 64 bit gPXE will be loaded > 4GB */
+	if ((u64)skge->dma >> 32 != ((u64) skge->dma + RING_SIZE) >> 32) {
+		DBG(PFX "pci_alloc_consistent region crosses 4G boundary\n");
+		err = -EINVAL;
+		goto err;
+	}
+
+	err = skge_ring_alloc(&skge->rx_ring, skge->mem, skge->dma, NUM_RX_DESC);
+	if (err)
+		goto err;
+
+	/* this call relies on e->iob and d->control to be 0
+	 * This is assured by calling memset() on skge->mem and using zalloc()
+	 * for the skge_element structures.
+	 */
+	skge_rx_refill(dev);
+
+	err = skge_ring_alloc(&skge->tx_ring, skge->mem + RX_RING_SIZE,
+			      skge->dma + RX_RING_SIZE, NUM_TX_DESC);
+	if (err)
+		goto err;
+
+	/* Initialize MAC */
+	if (hw->chip_id == CHIP_ID_GENESIS)
+		genesis_mac_init(hw, port);
+	else
+		yukon_mac_init(hw, port);
+
+	/* Configure RAMbuffers - equally between ports and tx/rx */
+	chunk = (hw->ram_size  - hw->ram_offset) / (hw->ports * 2);
+	ram_addr = hw->ram_offset + 2 * chunk * port;
+
+	skge_ramset(hw, rxqaddr[port], ram_addr, chunk);
+	skge_qset(skge, rxqaddr[port], skge->rx_ring.to_clean);
+
+	assert(!(skge->tx_ring.to_use != skge->tx_ring.to_clean));
+	skge_ramset(hw, txqaddr[port], ram_addr+chunk, chunk);
+	skge_qset(skge, txqaddr[port], skge->tx_ring.to_use);
+
+	/* Start receiver BMU */
+	wmb();
+	skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_START | CSR_IRQ_CL_F);
+	skge_led(skge, LED_MODE_ON);
+
+	hw->intr_mask |= portmask[port];
+	skge_write32(hw, B0_IMSK, hw->intr_mask);
+
+	return 0;
+
+ err:
+	skge_rx_clean(skge);
+	skge_free(dev);
+
+	return err;
+}
+
+/* stop receiver */
+static void skge_rx_stop(struct skge_hw *hw, int port)
+{
+	skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_STOP);
+	skge_write32(hw, RB_ADDR(port ? Q_R2 : Q_R1, RB_CTRL),
+		     RB_RST_SET|RB_DIS_OP_MD);
+	skge_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_SET_RESET);
+}
+
+static void skge_down(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_hw *hw = skge->hw;
+	int port = skge->port;
+
+	if (skge->mem == NULL)
+		return;
+
+	DBG2(PFX "%s: disabling interface\n", dev->name);
+
+	if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC)
+		skge->use_xm_link_timer = 0;
+
+	netdev_link_down(dev);
+
+	hw->intr_mask &= ~portmask[port];
+	skge_write32(hw, B0_IMSK, hw->intr_mask);
+
+	skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
+	if (hw->chip_id == CHIP_ID_GENESIS)
+		genesis_stop(skge);
+	else
+		yukon_stop(skge);
+
+	/* Stop transmitter */
+	skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP);
+	skge_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL),
+		     RB_RST_SET|RB_DIS_OP_MD);
+
+
+	/* Disable Force Sync bit and Enable Alloc bit */
+	skge_write8(hw, SK_REG(port, TXA_CTRL),
+		    TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC);
+
+	/* Stop Interval Timer and Limit Counter of Tx Arbiter */
+	skge_write32(hw, SK_REG(port, TXA_ITI_INI), 0L);
+	skge_write32(hw, SK_REG(port, TXA_LIM_INI), 0L);
+
+	/* Reset PCI FIFO */
+	skge_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_SET_RESET);
+	skge_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
+
+	/* Reset the RAM Buffer async Tx queue */
+	skge_write8(hw, RB_ADDR(port == 0 ? Q_XA1 : Q_XA2, RB_CTRL), RB_RST_SET);
+
+	skge_rx_stop(hw, port);
+
+	if (hw->chip_id == CHIP_ID_GENESIS) {
+		skge_write8(hw, SK_REG(port, TX_MFF_CTRL2), MFF_RST_SET);
+		skge_write8(hw, SK_REG(port, RX_MFF_CTRL2), MFF_RST_SET);
+	} else {
+		skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
+		skge_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
+	}
+
+	skge_led(skge, LED_MODE_OFF);
+
+	skge_tx_clean(dev);
+
+	skge_rx_clean(skge);
+
+	skge_free(dev);
+	return;
+}
+
+static inline int skge_tx_avail(const struct skge_ring *ring)
+{
+	mb();
+	return ((ring->to_clean > ring->to_use) ? 0 : NUM_TX_DESC)
+		+ (ring->to_clean - ring->to_use) - 1;
+}
+
+static int skge_xmit_frame(struct net_device *dev, struct io_buffer *iob)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_hw *hw = skge->hw;
+	struct skge_element *e;
+	struct skge_tx_desc *td;
+	u32 control, len;
+	u64 map;
+
+	if (skge_tx_avail(&skge->tx_ring) < 1)
+		return -EBUSY;
+
+	e = skge->tx_ring.to_use;
+	td = e->desc;
+	assert(!(td->control & BMU_OWN));
+	e->iob = iob;
+	len = iob_len(iob);
+	map = virt_to_bus(iob->data);
+
+	td->dma_lo = map;
+	td->dma_hi = map >> 32;
+
+	control = BMU_CHECK;
+
+	control |= BMU_EOF| BMU_IRQ_EOF;
+	/* Make sure all the descriptors written */
+	wmb();
+	td->control = BMU_OWN | BMU_SW | BMU_STF | control | len;
+	wmb();
+
+	skge_write8(hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_START);
+
+	DBGIO(PFX "%s: tx queued, slot %td, len %d\n",
+	     dev->name, e - skge->tx_ring.start, (unsigned int)len);
+
+	skge->tx_ring.to_use = e->next;
+	wmb();
+
+	if (skge_tx_avail(&skge->tx_ring) <= 1) {
+		DBG(PFX "%s: transmit queue full\n", dev->name);
+	}
+
+	return 0;
+}
+
+/* Free all buffers in transmit ring */
+static void skge_tx_clean(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_element *e;
+
+	for (e = skge->tx_ring.to_clean; e != skge->tx_ring.to_use; e = e->next) {
+		struct skge_tx_desc *td = e->desc;
+		td->control = 0;
+	}
+
+	skge->tx_ring.to_clean = e;
+}
+
+static const u8 pause_mc_addr[ETH_ALEN] = { 0x1, 0x80, 0xc2, 0x0, 0x0, 0x1 };
+
+static inline u16 phy_length(const struct skge_hw *hw, u32 status)
+{
+	if (hw->chip_id == CHIP_ID_GENESIS)
+		return status >> XMR_FS_LEN_SHIFT;
+	else
+		return status >> GMR_FS_LEN_SHIFT;
+}
+
+static inline int bad_phy_status(const struct skge_hw *hw, u32 status)
+{
+	if (hw->chip_id == CHIP_ID_GENESIS)
+		return (status & (XMR_FS_ERR | XMR_FS_2L_VLAN)) != 0;
+	else
+		return (status & GMR_FS_ANY_ERR) ||
+			(status & GMR_FS_RX_OK) == 0;
+}
+
+/* Free all buffers in Tx ring which are no longer owned by device */
+static void skge_tx_done(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_ring *ring = &skge->tx_ring;
+	struct skge_element *e;
+
+	skge_write8(skge->hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F);
+
+	for (e = ring->to_clean; e != ring->to_use; e = e->next) {
+		u32 control = ((const struct skge_tx_desc *) e->desc)->control;
+
+		if (control & BMU_OWN)
+			break;
+
+		netdev_tx_complete(dev, e->iob);
+	}
+	skge->tx_ring.to_clean = e;
+
+	/* Can run lockless until we need to synchronize to restart queue. */
+	mb();
+}
+
+static void skge_rx_refill(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_ring *ring = &skge->rx_ring;
+	struct skge_element *e;
+	struct io_buffer *iob;
+	struct skge_rx_desc *rd;
+	u32 control;
+	int i;
+
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		e = ring->to_clean;
+		rd = e->desc;
+		iob = e->iob;
+		control = rd->control;
+
+		/* nothing to do here */
+		if (iob || (control & BMU_OWN))
+			continue;
+
+		DBG2("refilling rx desc %d: ", (ring->to_clean - ring->start));
+
+		iob = alloc_iob(RX_BUF_SIZE);
+		if (iob) {
+			skge_rx_setup(skge, e, iob, RX_BUF_SIZE);
+		} else {
+			DBG("descr %d: alloc_iob() failed\n",
+			     (ring->to_clean - ring->start));
+			/* We pass the descriptor to the NIC even if the
+			 * allocation failed. The card will stop as soon as it
+			 * encounters a descriptor with the OWN bit set to 0,
+			 * thus never getting to the next descriptor that might
+			 * contain a valid io_buffer. This would effectively
+			 * stall the receive.
+			 */
+			skge_rx_setup(skge, e, NULL, 0);
+		}
+
+		ring->to_clean = e->next;
+	}
+}
+
+static void skge_rx_done(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_ring *ring = &skge->rx_ring;
+	struct skge_rx_desc *rd;
+	struct skge_element *e;
+	struct io_buffer *iob;
+	u32 control;
+	u16 len;
+	int i;
+
+	e = ring->to_clean;
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		iob = e->iob;
+		rd = e->desc;
+
+		rmb();
+		control = rd->control;
+
+		if ((control & BMU_OWN))
+			break;
+
+		if (!iob)
+			continue;
+
+		len = control & BMU_BBC;
+
+		/* catch RX errors */
+		if ((bad_phy_status(skge->hw, rd->status)) ||
+		   (phy_length(skge->hw, rd->status) != len)) {
+			/* report receive errors */
+			DBG("rx error\n");
+			netdev_rx_err(dev, iob, -EIO);
+		} else {
+			DBG2("received packet, len %d\n", len);
+			iob_put(iob, len);
+			netdev_rx(dev, iob);
+		}
+
+		/* io_buffer passed to core, make sure we don't reuse it */
+		e->iob = NULL;
+
+		e = e->next;
+	}
+	skge_rx_refill(dev);
+}
+
+static void skge_poll(struct net_device *dev)
+{
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_hw *hw = skge->hw;
+	u32 status;
+
+	/* reading this register ACKs interrupts */
+	status = skge_read32(hw, B0_SP_ISRC);
+
+	/* Link event? */
+	if (status & IS_EXT_REG) {
+		skge_phyirq(hw);
+		if (skge->use_xm_link_timer)
+			xm_link_timer(skge);
+	}
+
+	skge_tx_done(dev);
+
+	skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F);
+
+	skge_rx_done(dev);
+
+	/* restart receiver */
+	wmb();
+	skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR), CSR_START);
+
+	skge_read32(hw, B0_IMSK);
+
+	return;
+}
+
+static void skge_phyirq(struct skge_hw *hw)
+{
+	int port;
+
+	for (port = 0; port < hw->ports; port++) {
+		struct net_device *dev = hw->dev[port];
+		struct skge_port *skge = netdev_priv(dev);
+
+		if (hw->chip_id != CHIP_ID_GENESIS)
+			yukon_phy_intr(skge);
+		else if (hw->phy_type == SK_PHY_BCOM)
+			bcom_phy_intr(skge);
+	}
+
+	hw->intr_mask |= IS_EXT_REG;
+	skge_write32(hw, B0_IMSK, hw->intr_mask);
+	skge_read32(hw, B0_IMSK);
+}
+
+static const struct {
+	u8 id;
+	const char *name;
+} skge_chips[] = {
+	{ CHIP_ID_GENESIS,	"Genesis" },
+	{ CHIP_ID_YUKON,	 "Yukon" },
+	{ CHIP_ID_YUKON_LITE,	 "Yukon-Lite"},
+	{ CHIP_ID_YUKON_LP,	 "Yukon-LP"},
+};
+
+static const char *skge_board_name(const struct skge_hw *hw)
+{
+	unsigned int i;
+	static char buf[16];
+
+	for (i = 0; i < ARRAY_SIZE(skge_chips); i++)
+		if (skge_chips[i].id == hw->chip_id)
+			return skge_chips[i].name;
+
+	snprintf(buf, sizeof buf, "chipid 0x%x", hw->chip_id);
+	return buf;
+}
+
+
+/*
+ * Setup the board data structure, but don't bring up
+ * the port(s)
+ */
+static int skge_reset(struct skge_hw *hw)
+{
+	u32 reg;
+	u16 ctst, pci_status;
+	u8 t8, mac_cfg, pmd_type;
+	int i;
+
+	ctst = skge_read16(hw, B0_CTST);
+
+	/* do a SW reset */
+	skge_write8(hw, B0_CTST, CS_RST_SET);
+	skge_write8(hw, B0_CTST, CS_RST_CLR);
+
+	/* clear PCI errors, if any */
+	skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+	skge_write8(hw, B2_TST_CTRL2, 0);
+
+	pci_read_config_word(hw->pdev, PCI_STATUS, &pci_status);
+	pci_write_config_word(hw->pdev, PCI_STATUS,
+			      pci_status | PCI_STATUS_ERROR_BITS);
+	skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+	skge_write8(hw, B0_CTST, CS_MRST_CLR);
+
+	/* restore CLK_RUN bits (for Yukon-Lite) */
+	skge_write16(hw, B0_CTST,
+		     ctst & (CS_CLK_RUN_HOT|CS_CLK_RUN_RST|CS_CLK_RUN_ENA));
+
+	hw->chip_id = skge_read8(hw, B2_CHIP_ID);
+	hw->phy_type = skge_read8(hw, B2_E_1) & 0xf;
+	pmd_type = skge_read8(hw, B2_PMD_TYP);
+	hw->copper = (pmd_type == 'T' || pmd_type == '1');
+
+	switch (hw->chip_id) {
+	case CHIP_ID_GENESIS:
+		switch (hw->phy_type) {
+		case SK_PHY_XMAC:
+			hw->phy_addr = PHY_ADDR_XMAC;
+			break;
+		case SK_PHY_BCOM:
+			hw->phy_addr = PHY_ADDR_BCOM;
+			break;
+		default:
+			DBG(PFX "unsupported phy type 0x%x\n",
+			       hw->phy_type);
+			return -EOPNOTSUPP;
+		}
+		break;
+
+	case CHIP_ID_YUKON:
+	case CHIP_ID_YUKON_LITE:
+	case CHIP_ID_YUKON_LP:
+		if (hw->phy_type < SK_PHY_MARV_COPPER && pmd_type != 'S')
+			hw->copper = 1;
+
+		hw->phy_addr = PHY_ADDR_MARV;
+		break;
+
+	default:
+		DBG(PFX "unsupported chip type 0x%x\n",
+		       hw->chip_id);
+		return -EOPNOTSUPP;
+	}
+
+	mac_cfg = skge_read8(hw, B2_MAC_CFG);
+	hw->ports = (mac_cfg & CFG_SNG_MAC) ? 1 : 2;
+	hw->chip_rev = (mac_cfg & CFG_CHIP_R_MSK) >> 4;
+
+	/* read the adapters RAM size */
+	t8 = skge_read8(hw, B2_E_0);
+	if (hw->chip_id == CHIP_ID_GENESIS) {
+		if (t8 == 3) {
+			/* special case: 4 x 64k x 36, offset = 0x80000 */
+			hw->ram_size = 0x100000;
+			hw->ram_offset = 0x80000;
+		} else
+			hw->ram_size = t8 * 512;
+	}
+	else if (t8 == 0)
+		hw->ram_size = 0x20000;
+	else
+		hw->ram_size = t8 * 4096;
+
+	hw->intr_mask = IS_HW_ERR;
+
+	/* Use PHY IRQ for all but fiber based Genesis board */
+	if (!(hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC))
+		hw->intr_mask |= IS_EXT_REG;
+
+	if (hw->chip_id == CHIP_ID_GENESIS)
+		genesis_init(hw);
+	else {
+		/* switch power to VCC (WA for VAUX problem) */
+		skge_write8(hw, B0_POWER_CTRL,
+			    PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON);
+
+		/* avoid boards with stuck Hardware error bits */
+		if ((skge_read32(hw, B0_ISRC) & IS_HW_ERR) &&
+		    (skge_read32(hw, B0_HWE_ISRC) & IS_IRQ_SENSOR)) {
+			DBG(PFX "stuck hardware sensor bit\n");
+			hw->intr_mask &= ~IS_HW_ERR;
+		}
+
+		/* Clear PHY COMA */
+		skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+		pci_read_config_dword(hw->pdev, PCI_DEV_REG1, &reg);
+		reg &= ~PCI_PHY_COMA;
+		pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg);
+		skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+
+		for (i = 0; i < hw->ports; i++) {
+			skge_write16(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET);
+			skge_write16(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_CLR);
+		}
+	}
+
+	/* turn off hardware timer (unused) */
+	skge_write8(hw, B2_TI_CTRL, TIM_STOP);
+	skge_write8(hw, B2_TI_CTRL, TIM_CLR_IRQ);
+	skge_write8(hw, B0_LED, LED_STAT_ON);
+
+	/* enable the Tx Arbiters */
+	for (i = 0; i < hw->ports; i++)
+		skge_write8(hw, SK_REG(i, TXA_CTRL), TXA_ENA_ARB);
+
+	/* Initialize ram interface */
+	skge_write16(hw, B3_RI_CTRL, RI_RST_CLR);
+
+	skge_write8(hw, B3_RI_WTO_R1, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_WTO_XA1, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_WTO_XS1, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_RTO_R1, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_RTO_XA1, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_RTO_XS1, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_WTO_R2, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_WTO_XA2, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_WTO_XS2, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_RTO_R2, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_RTO_XA2, SK_RI_TO_53);
+	skge_write8(hw, B3_RI_RTO_XS2, SK_RI_TO_53);
+
+	skge_write32(hw, B0_HWE_IMSK, IS_ERR_MSK);
+
+	/* Set interrupt moderation for Transmit only
+	 * Receive interrupts avoided by NAPI
+	 */
+	skge_write32(hw, B2_IRQM_MSK, IS_XA1_F|IS_XA2_F);
+	skge_write32(hw, B2_IRQM_INI, skge_usecs2clk(hw, 100));
+	skge_write32(hw, B2_IRQM_CTRL, TIM_START);
+
+	skge_write32(hw, B0_IMSK, hw->intr_mask);
+
+	for (i = 0; i < hw->ports; i++) {
+		if (hw->chip_id == CHIP_ID_GENESIS)
+			genesis_reset(hw, i);
+		else
+			yukon_reset(hw, i);
+	}
+
+	return 0;
+}
+
+/* Initialize network device */
+static struct net_device *skge_devinit(struct skge_hw *hw, int port,
+				       int highmem __unused)
+{
+	struct skge_port *skge;
+	struct net_device *dev = alloc_etherdev(sizeof(*skge));
+
+	if (!dev) {
+		DBG(PFX "etherdev alloc failed\n");
+		return NULL;
+	}
+
+	dev->dev = &hw->pdev->dev;
+
+	skge = netdev_priv(dev);
+	skge->netdev = dev;
+	skge->hw = hw;
+
+	/* Auto speed and flow control */
+	skge->autoneg = AUTONEG_ENABLE;
+	skge->flow_control = FLOW_MODE_SYM_OR_REM;
+	skge->duplex = -1;
+	skge->speed = -1;
+	skge->advertising = skge_supported_modes(hw);
+
+	hw->dev[port] = dev;
+
+	skge->port = port;
+
+	/* read the mac address */
+	memcpy(dev->hw_addr, (void *) (hw->regs + B2_MAC_1 + port*8), ETH_ALEN);
+
+	/* device is off until link detection */
+	netdev_link_down(dev);
+
+	return dev;
+}
+
+static void skge_show_addr(struct net_device *dev)
+{
+	DBG2(PFX "%s: addr %s\n",
+	     dev->name, netdev_addr(dev));
+}
+
+static int skge_probe(struct pci_device *pdev,
+				const struct pci_device_id *ent __unused)
+{
+	struct net_device *dev, *dev1;
+	struct skge_hw *hw;
+	int err, using_dac = 0;
+
+	adjust_pci_device(pdev);
+
+	err = -ENOMEM;
+	hw = zalloc(sizeof(*hw));
+	if (!hw) {
+		DBG(PFX "cannot allocate hardware struct\n");
+		goto err_out_free_regions;
+	}
+
+	hw->pdev = pdev;
+
+	hw->regs = (u32)ioremap(pci_bar_start(pdev, PCI_BASE_ADDRESS_0),
+				SKGE_REG_SIZE);
+	if (!hw->regs) {
+		DBG(PFX "cannot map device registers\n");
+		goto err_out_free_hw;
+	}
+
+	err = skge_reset(hw);
+	if (err)
+		goto err_out_iounmap;
+
+	DBG(PFX " addr 0x%llx irq %d chip %s rev %d\n",
+	    (unsigned long long)pdev->ioaddr, pdev->irq,
+	    skge_board_name(hw), hw->chip_rev);
+
+	dev = skge_devinit(hw, 0, using_dac);
+	if (!dev)
+		goto err_out_led_off;
+
+	netdev_init ( dev, &skge_operations );
+
+	err = register_netdev(dev);
+	if (err) {
+		DBG(PFX "cannot register net device\n");
+		goto err_out_free_netdev;
+	}
+
+	skge_show_addr(dev);
+
+	if (hw->ports > 1 && (dev1 = skge_devinit(hw, 1, using_dac))) {
+		if (register_netdev(dev1) == 0)
+			skge_show_addr(dev1);
+		else {
+			/* Failure to register second port need not be fatal */
+			DBG(PFX "register of second port failed\n");
+			hw->dev[1] = NULL;
+			netdev_nullify(dev1);
+			netdev_put(dev1);
+		}
+	}
+	pci_set_drvdata(pdev, hw);
+
+	return 0;
+
+err_out_free_netdev:
+	netdev_nullify(dev);
+	netdev_put(dev);
+err_out_led_off:
+	skge_write16(hw, B0_LED, LED_STAT_OFF);
+err_out_iounmap:
+	iounmap((void*)hw->regs);
+err_out_free_hw:
+	free(hw);
+err_out_free_regions:
+	pci_set_drvdata(pdev, NULL);
+	return err;
+}
+
+static void skge_remove(struct pci_device *pdev)
+{
+	struct skge_hw *hw  = pci_get_drvdata(pdev);
+	struct net_device *dev0, *dev1;
+
+	if (!hw)
+		return;
+
+	if ((dev1 = hw->dev[1]))
+		unregister_netdev(dev1);
+	dev0 = hw->dev[0];
+	unregister_netdev(dev0);
+
+	hw->intr_mask = 0;
+	skge_write32(hw, B0_IMSK, 0);
+	skge_read32(hw, B0_IMSK);
+
+	skge_write16(hw, B0_LED, LED_STAT_OFF);
+	skge_write8(hw, B0_CTST, CS_RST_SET);
+
+	if (dev1) {
+		netdev_nullify(dev1);
+		netdev_put(dev1);
+	}
+	netdev_nullify(dev0);
+	netdev_put(dev0);
+
+	iounmap((void*)hw->regs);
+	free(hw);
+	pci_set_drvdata(pdev, NULL);
+}
+
+/*
+ * Enable or disable IRQ masking.
+ *
+ * @v netdev		Device to control.
+ * @v enable		Zero to mask off IRQ, non-zero to enable IRQ.
+ *
+ * This is a gPXE Network Driver API function.
+ */
+static void skge_net_irq ( struct net_device *dev, int enable ) {
+	struct skge_port *skge = netdev_priv(dev);
+	struct skge_hw *hw = skge->hw;
+
+	if (enable)
+		hw->intr_mask |= portmask[skge->port];
+	else
+		hw->intr_mask &= ~portmask[skge->port];
+	skge_write32(hw, B0_IMSK, hw->intr_mask);
+}
+
+struct pci_driver skge_driver __pci_driver = {
+	.ids      = skge_id_table,
+	.id_count = ( sizeof (skge_id_table) / sizeof (skge_id_table[0]) ),
+	.probe    = skge_probe,
+	.remove   = skge_remove
+};
+
diff --git a/gpxe/src/drivers/net/skge.h b/gpxe/src/drivers/net/skge.h
new file mode 100755
index 0000000..6b08daf
--- /dev/null
+++ b/gpxe/src/drivers/net/skge.h
@@ -0,0 +1,2623 @@
+/*
+ * Definitions for the new Marvell Yukon / SysKonnect driver.
+ */
+#ifndef _SKGE_H
+#define _SKGE_H
+
+FILE_LICENCE ( GPL2_ONLY );
+
+/* PCI config registers */
+#define PCI_DEV_REG1	0x40
+#define  PCI_PHY_COMA	0x8000000
+#define  PCI_VIO	0x2000000
+
+#define PCI_DEV_REG2	0x44
+#define  PCI_VPD_ROM_SZ	7L<<14	/* VPD ROM size 0=256, 1=512, ... */
+#define  PCI_REV_DESC	1<<2	/* Reverse Descriptor bytes */
+
+#define DRV_NAME		"skge"
+#define DRV_VERSION		"1.13"
+#define PFX			DRV_NAME " "
+
+#define NUM_TX_DESC		8
+#define NUM_RX_DESC		8
+
+/* mdeck used a 16 byte alignment, but datasheet says 8 bytes is sufficient */
+#define SKGE_RING_ALIGN		8
+#define RX_BUF_SIZE		1536
+#define PHY_RETRIES	        1000
+
+#define TX_RING_SIZE	( NUM_TX_DESC * sizeof ( struct skge_rx_desc ) )
+#define RX_RING_SIZE	( NUM_RX_DESC * sizeof ( struct skge_tx_desc ) )
+#define RING_SIZE	( TX_RING_SIZE + RX_RING_SIZE )
+
+#define SKGE_REG_SIZE	0x4000
+
+#define SKGE_EEPROM_MAGIC	0x9933aabb
+
+/* Added for gPXE ------------------ */
+
+/* from ethtool.h */
+#define AUTONEG_DISABLE	0x00
+#define AUTONEG_ENABLE	0x01
+
+#define DUPLEX_HALF	0x00
+#define DUPLEX_FULL	0x01
+
+#define SPEED_10	10
+#define SPEED_100	100
+#define SPEED_1000	1000
+
+#define ADVERTISED_10baseT_Half  	(1 << 0)
+#define ADVERTISED_10baseT_Full  	(1 << 1)
+#define ADVERTISED_100baseT_Half 	(1 << 2)
+#define ADVERTISED_100baseT_Full 	(1 << 3)
+#define ADVERTISED_1000baseT_Half	(1 << 4)
+#define ADVERTISED_1000baseT_Full	(1 << 5)
+
+#define SUPPORTED_10baseT_Half  	(1 << 0)
+#define SUPPORTED_10baseT_Full  	(1 << 1)
+#define SUPPORTED_100baseT_Half 	(1 << 2)
+#define SUPPORTED_100baseT_Full 	(1 << 3)
+#define SUPPORTED_1000baseT_Half	(1 << 4)
+#define SUPPORTED_1000baseT_Full	(1 << 5)
+#define SUPPORTED_Autoneg		(1 << 6)
+#define SUPPORTED_TP			(1 << 7)
+#define SUPPORTED_FIBRE			(1 << 10)
+
+/* from kernel.h */
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/* ----------------------------------- */
+
+#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \
+			       PCI_STATUS_SIG_SYSTEM_ERROR | \
+			       PCI_STATUS_REC_MASTER_ABORT | \
+			       PCI_STATUS_REC_TARGET_ABORT | \
+			       PCI_STATUS_PARITY)
+
+enum csr_regs {
+	B0_RAP	= 0x0000,
+	B0_CTST	= 0x0004,
+	B0_LED	= 0x0006,
+	B0_POWER_CTRL	= 0x0007,
+	B0_ISRC	= 0x0008,
+	B0_IMSK	= 0x000c,
+	B0_HWE_ISRC	= 0x0010,
+	B0_HWE_IMSK	= 0x0014,
+	B0_SP_ISRC	= 0x0018,
+	B0_XM1_IMSK	= 0x0020,
+	B0_XM1_ISRC	= 0x0028,
+	B0_XM1_PHY_ADDR	= 0x0030,
+	B0_XM1_PHY_DATA	= 0x0034,
+	B0_XM2_IMSK	= 0x0040,
+	B0_XM2_ISRC	= 0x0048,
+	B0_XM2_PHY_ADDR	= 0x0050,
+	B0_XM2_PHY_DATA	= 0x0054,
+	B0_R1_CSR	= 0x0060,
+	B0_R2_CSR	= 0x0064,
+	B0_XS1_CSR	= 0x0068,
+	B0_XA1_CSR	= 0x006c,
+	B0_XS2_CSR	= 0x0070,
+	B0_XA2_CSR	= 0x0074,
+
+	B2_MAC_1	= 0x0100,
+	B2_MAC_2	= 0x0108,
+	B2_MAC_3	= 0x0110,
+	B2_CONN_TYP	= 0x0118,
+	B2_PMD_TYP	= 0x0119,
+	B2_MAC_CFG	= 0x011a,
+	B2_CHIP_ID	= 0x011b,
+	B2_E_0		= 0x011c,
+	B2_E_1		= 0x011d,
+	B2_E_2		= 0x011e,
+	B2_E_3		= 0x011f,
+	B2_FAR		= 0x0120,
+	B2_FDP		= 0x0124,
+	B2_LD_CTRL	= 0x0128,
+	B2_LD_TEST	= 0x0129,
+	B2_TI_INI	= 0x0130,
+	B2_TI_VAL	= 0x0134,
+	B2_TI_CTRL	= 0x0138,
+	B2_TI_TEST	= 0x0139,
+	B2_IRQM_INI	= 0x0140,
+	B2_IRQM_VAL	= 0x0144,
+	B2_IRQM_CTRL	= 0x0148,
+	B2_IRQM_TEST	= 0x0149,
+	B2_IRQM_MSK	= 0x014c,
+	B2_IRQM_HWE_MSK	= 0x0150,
+	B2_TST_CTRL1	= 0x0158,
+	B2_TST_CTRL2	= 0x0159,
+	B2_GP_IO	= 0x015c,
+	B2_I2C_CTRL	= 0x0160,
+	B2_I2C_DATA	= 0x0164,
+	B2_I2C_IRQ	= 0x0168,
+	B2_I2C_SW	= 0x016c,
+	B2_BSC_INI	= 0x0170,
+	B2_BSC_VAL	= 0x0174,
+	B2_BSC_CTRL	= 0x0178,
+	B2_BSC_STAT	= 0x0179,
+	B2_BSC_TST	= 0x017a,
+
+	B3_RAM_ADDR	= 0x0180,
+	B3_RAM_DATA_LO	= 0x0184,
+	B3_RAM_DATA_HI	= 0x0188,
+	B3_RI_WTO_R1	= 0x0190,
+	B3_RI_WTO_XA1	= 0x0191,
+	B3_RI_WTO_XS1	= 0x0192,
+	B3_RI_RTO_R1	= 0x0193,
+	B3_RI_RTO_XA1	= 0x0194,
+	B3_RI_RTO_XS1	= 0x0195,
+	B3_RI_WTO_R2	= 0x0196,
+	B3_RI_WTO_XA2	= 0x0197,
+	B3_RI_WTO_XS2	= 0x0198,
+	B3_RI_RTO_R2	= 0x0199,
+	B3_RI_RTO_XA2	= 0x019a,
+	B3_RI_RTO_XS2	= 0x019b,
+	B3_RI_TO_VAL	= 0x019c,
+	B3_RI_CTRL	= 0x01a0,
+	B3_RI_TEST	= 0x01a2,
+	B3_MA_TOINI_RX1	= 0x01b0,
+	B3_MA_TOINI_RX2	= 0x01b1,
+	B3_MA_TOINI_TX1	= 0x01b2,
+	B3_MA_TOINI_TX2	= 0x01b3,
+	B3_MA_TOVAL_RX1	= 0x01b4,
+	B3_MA_TOVAL_RX2	= 0x01b5,
+	B3_MA_TOVAL_TX1	= 0x01b6,
+	B3_MA_TOVAL_TX2	= 0x01b7,
+	B3_MA_TO_CTRL	= 0x01b8,
+	B3_MA_TO_TEST	= 0x01ba,
+	B3_MA_RCINI_RX1	= 0x01c0,
+	B3_MA_RCINI_RX2	= 0x01c1,
+	B3_MA_RCINI_TX1	= 0x01c2,
+	B3_MA_RCINI_TX2	= 0x01c3,
+	B3_MA_RCVAL_RX1	= 0x01c4,
+	B3_MA_RCVAL_RX2	= 0x01c5,
+	B3_MA_RCVAL_TX1	= 0x01c6,
+	B3_MA_RCVAL_TX2	= 0x01c7,
+	B3_MA_RC_CTRL	= 0x01c8,
+	B3_MA_RC_TEST	= 0x01ca,
+	B3_PA_TOINI_RX1	= 0x01d0,
+	B3_PA_TOINI_RX2	= 0x01d4,
+	B3_PA_TOINI_TX1	= 0x01d8,
+	B3_PA_TOINI_TX2	= 0x01dc,
+	B3_PA_TOVAL_RX1	= 0x01e0,
+	B3_PA_TOVAL_RX2	= 0x01e4,
+	B3_PA_TOVAL_TX1	= 0x01e8,
+	B3_PA_TOVAL_TX2	= 0x01ec,
+	B3_PA_CTRL	= 0x01f0,
+	B3_PA_TEST	= 0x01f2,
+};
+
+/*	B0_CTST			16 bit	Control/Status register */
+enum {
+	CS_CLK_RUN_HOT	= 1<<13,/* CLK_RUN hot m. (YUKON-Lite only) */
+	CS_CLK_RUN_RST	= 1<<12,/* CLK_RUN reset  (YUKON-Lite only) */
+	CS_CLK_RUN_ENA	= 1<<11,/* CLK_RUN enable (YUKON-Lite only) */
+	CS_VAUX_AVAIL	= 1<<10,/* VAUX available (YUKON only) */
+	CS_BUS_CLOCK	= 1<<9,	/* Bus Clock 0/1 = 33/66 MHz */
+	CS_BUS_SLOT_SZ	= 1<<8,	/* Slot Size 0/1 = 32/64 bit slot */
+	CS_ST_SW_IRQ	= 1<<7,	/* Set IRQ SW Request */
+	CS_CL_SW_IRQ	= 1<<6,	/* Clear IRQ SW Request */
+	CS_STOP_DONE	= 1<<5,	/* Stop Master is finished */
+	CS_STOP_MAST	= 1<<4,	/* Command Bit to stop the master */
+	CS_MRST_CLR	= 1<<3,	/* Clear Master reset	*/
+	CS_MRST_SET	= 1<<2,	/* Set Master reset	*/
+	CS_RST_CLR	= 1<<1,	/* Clear Software reset	*/
+	CS_RST_SET	= 1,	/* Set   Software reset	*/
+
+/*	B0_LED			 8 Bit	LED register */
+/* Bit  7.. 2:	reserved */
+	LED_STAT_ON	= 1<<1,	/* Status LED on	*/
+	LED_STAT_OFF	= 1,		/* Status LED off	*/
+
+/*	B0_POWER_CTRL	 8 Bit	Power Control reg (YUKON only) */
+	PC_VAUX_ENA	= 1<<7,	/* Switch VAUX Enable  */
+	PC_VAUX_DIS	= 1<<6,	/* Switch VAUX Disable */
+	PC_VCC_ENA	= 1<<5,	/* Switch VCC Enable  */
+	PC_VCC_DIS	= 1<<4,	/* Switch VCC Disable */
+	PC_VAUX_ON	= 1<<3,	/* Switch VAUX On  */
+	PC_VAUX_OFF	= 1<<2,	/* Switch VAUX Off */
+	PC_VCC_ON	= 1<<1,	/* Switch VCC On  */
+	PC_VCC_OFF	= 1<<0,	/* Switch VCC Off */
+};
+
+/*	B2_IRQM_MSK 	32 bit	IRQ Moderation Mask */
+enum {
+	IS_ALL_MSK	= 0xbffffffful,	/* All Interrupt bits */
+	IS_HW_ERR	= 1<<31,	/* Interrupt HW Error */
+					/* Bit 30:	reserved */
+	IS_PA_TO_RX1	= 1<<29,	/* Packet Arb Timeout Rx1 */
+	IS_PA_TO_RX2	= 1<<28,	/* Packet Arb Timeout Rx2 */
+	IS_PA_TO_TX1	= 1<<27,	/* Packet Arb Timeout Tx1 */
+	IS_PA_TO_TX2	= 1<<26,	/* Packet Arb Timeout Tx2 */
+	IS_I2C_READY	= 1<<25,	/* IRQ on end of I2C Tx */
+	IS_IRQ_SW	= 1<<24,	/* SW forced IRQ	*/
+	IS_EXT_REG	= 1<<23,	/* IRQ from LM80 or PHY (GENESIS only) */
+					/* IRQ from PHY (YUKON only) */
+	IS_TIMINT	= 1<<22,	/* IRQ from Timer	*/
+	IS_MAC1		= 1<<21,	/* IRQ from MAC 1	*/
+	IS_LNK_SYNC_M1	= 1<<20,	/* Link Sync Cnt wrap MAC 1 */
+	IS_MAC2		= 1<<19,	/* IRQ from MAC 2	*/
+	IS_LNK_SYNC_M2	= 1<<18,	/* Link Sync Cnt wrap MAC 2 */
+/* Receive Queue 1 */
+	IS_R1_B		= 1<<17,	/* Q_R1 End of Buffer */
+	IS_R1_F		= 1<<16,	/* Q_R1 End of Frame */
+	IS_R1_C		= 1<<15,	/* Q_R1 Encoding Error */
+/* Receive Queue 2 */
+	IS_R2_B		= 1<<14,	/* Q_R2 End of Buffer */
+	IS_R2_F		= 1<<13,	/* Q_R2 End of Frame */
+	IS_R2_C		= 1<<12,	/* Q_R2 Encoding Error */
+/* Synchronous Transmit Queue 1 */
+	IS_XS1_B	= 1<<11,	/* Q_XS1 End of Buffer */
+	IS_XS1_F	= 1<<10,	/* Q_XS1 End of Frame */
+	IS_XS1_C	= 1<<9,		/* Q_XS1 Encoding Error */
+/* Asynchronous Transmit Queue 1 */
+	IS_XA1_B	= 1<<8,		/* Q_XA1 End of Buffer */
+	IS_XA1_F	= 1<<7,		/* Q_XA1 End of Frame */
+	IS_XA1_C	= 1<<6,		/* Q_XA1 Encoding Error */
+/* Synchronous Transmit Queue 2 */
+	IS_XS2_B	= 1<<5,		/* Q_XS2 End of Buffer */
+	IS_XS2_F	= 1<<4,		/* Q_XS2 End of Frame */
+	IS_XS2_C	= 1<<3,		/* Q_XS2 Encoding Error */
+/* Asynchronous Transmit Queue 2 */
+	IS_XA2_B	= 1<<2,		/* Q_XA2 End of Buffer */
+	IS_XA2_F	= 1<<1,		/* Q_XA2 End of Frame */
+	IS_XA2_C	= 1<<0,		/* Q_XA2 Encoding Error */
+
+	IS_TO_PORT1	= IS_PA_TO_RX1 | IS_PA_TO_TX1,
+	IS_TO_PORT2	= IS_PA_TO_RX2 | IS_PA_TO_TX2,
+
+	IS_PORT_1	= IS_XA1_F| IS_R1_F | IS_TO_PORT1 | IS_MAC1,
+	IS_PORT_2	= IS_XA2_F| IS_R2_F | IS_TO_PORT2 | IS_MAC2,
+};
+
+
+/*	B2_IRQM_HWE_MSK	32 bit	IRQ Moderation HW Error Mask */
+enum {
+	IS_IRQ_TIST_OV	= 1<<13, /* Time Stamp Timer Overflow (YUKON only) */
+	IS_IRQ_SENSOR	= 1<<12, /* IRQ from Sensor (YUKON only) */
+	IS_IRQ_MST_ERR	= 1<<11, /* IRQ master error detected */
+	IS_IRQ_STAT	= 1<<10, /* IRQ status exception */
+	IS_NO_STAT_M1	= 1<<9,	/* No Rx Status from MAC 1 */
+	IS_NO_STAT_M2	= 1<<8,	/* No Rx Status from MAC 2 */
+	IS_NO_TIST_M1	= 1<<7,	/* No Time Stamp from MAC 1 */
+	IS_NO_TIST_M2	= 1<<6,	/* No Time Stamp from MAC 2 */
+	IS_RAM_RD_PAR	= 1<<5,	/* RAM Read  Parity Error */
+	IS_RAM_WR_PAR	= 1<<4,	/* RAM Write Parity Error */
+	IS_M1_PAR_ERR	= 1<<3,	/* MAC 1 Parity Error */
+	IS_M2_PAR_ERR	= 1<<2,	/* MAC 2 Parity Error */
+	IS_R1_PAR_ERR	= 1<<1,	/* Queue R1 Parity Error */
+	IS_R2_PAR_ERR	= 1<<0,	/* Queue R2 Parity Error */
+
+	IS_ERR_MSK	= IS_IRQ_MST_ERR | IS_IRQ_STAT
+			| IS_RAM_RD_PAR | IS_RAM_WR_PAR
+			| IS_M1_PAR_ERR | IS_M2_PAR_ERR
+			| IS_R1_PAR_ERR | IS_R2_PAR_ERR,
+};
+
+/*	B2_TST_CTRL1	 8 bit	Test Control Register 1 */
+enum {
+	TST_FRC_DPERR_MR = 1<<7, /* force DATAPERR on MST RD */
+	TST_FRC_DPERR_MW = 1<<6, /* force DATAPERR on MST WR */
+	TST_FRC_DPERR_TR = 1<<5, /* force DATAPERR on TRG RD */
+	TST_FRC_DPERR_TW = 1<<4, /* force DATAPERR on TRG WR */
+	TST_FRC_APERR_M	 = 1<<3, /* force ADDRPERR on MST */
+	TST_FRC_APERR_T	 = 1<<2, /* force ADDRPERR on TRG */
+	TST_CFG_WRITE_ON = 1<<1, /* Enable  Config Reg WR */
+	TST_CFG_WRITE_OFF= 1<<0, /* Disable Config Reg WR */
+};
+
+/*	B2_MAC_CFG		 8 bit	MAC Configuration / Chip Revision */
+enum {
+	CFG_CHIP_R_MSK	  = 0xf<<4,	/* Bit 7.. 4: Chip Revision */
+					/* Bit 3.. 2:	reserved */
+	CFG_DIS_M2_CLK	  = 1<<1,	/* Disable Clock for 2nd MAC */
+	CFG_SNG_MAC	  = 1<<0,	/* MAC Config: 0=2 MACs / 1=1 MAC*/
+};
+
+/*	B2_CHIP_ID		 8 bit 	Chip Identification Number */
+enum {
+	CHIP_ID_GENESIS	   = 0x0a, /* Chip ID for GENESIS */
+	CHIP_ID_YUKON	   = 0xb0, /* Chip ID for YUKON */
+	CHIP_ID_YUKON_LITE = 0xb1, /* Chip ID for YUKON-Lite (Rev. A1-A3) */
+	CHIP_ID_YUKON_LP   = 0xb2, /* Chip ID for YUKON-LP */
+	CHIP_ID_YUKON_XL   = 0xb3, /* Chip ID for YUKON-2 XL */
+	CHIP_ID_YUKON_EC   = 0xb6, /* Chip ID for YUKON-2 EC */
+	CHIP_ID_YUKON_FE   = 0xb7, /* Chip ID for YUKON-2 FE */
+
+	CHIP_REV_YU_LITE_A1  = 3,	/* Chip Rev. for YUKON-Lite A1,A2 */
+	CHIP_REV_YU_LITE_A3  = 7,	/* Chip Rev. for YUKON-Lite A3 */
+};
+
+/*	B2_TI_CTRL		 8 bit	Timer control */
+/*	B2_IRQM_CTRL	 8 bit	IRQ Moderation Timer Control */
+enum {
+	TIM_START	= 1<<2,	/* Start Timer */
+	TIM_STOP	= 1<<1,	/* Stop  Timer */
+	TIM_CLR_IRQ	= 1<<0,	/* Clear Timer IRQ (!IRQM) */
+};
+
+/*	B2_TI_TEST		 8 Bit	Timer Test */
+/*	B2_IRQM_TEST	 8 bit	IRQ Moderation Timer Test */
+/*	B28_DPT_TST		 8 bit	Descriptor Poll Timer Test Reg */
+enum {
+	TIM_T_ON	= 1<<2,	/* Test mode on */
+	TIM_T_OFF	= 1<<1,	/* Test mode off */
+	TIM_T_STEP	= 1<<0,	/* Test step */
+};
+
+/*	B2_GP_IO		32 bit	General Purpose I/O Register */
+enum {
+	GP_DIR_9 = 1<<25, /* IO_9 direct, 0=In/1=Out */
+	GP_DIR_8 = 1<<24, /* IO_8 direct, 0=In/1=Out */
+	GP_DIR_7 = 1<<23, /* IO_7 direct, 0=In/1=Out */
+	GP_DIR_6 = 1<<22, /* IO_6 direct, 0=In/1=Out */
+	GP_DIR_5 = 1<<21, /* IO_5 direct, 0=In/1=Out */
+	GP_DIR_4 = 1<<20, /* IO_4 direct, 0=In/1=Out */
+	GP_DIR_3 = 1<<19, /* IO_3 direct, 0=In/1=Out */
+	GP_DIR_2 = 1<<18, /* IO_2 direct, 0=In/1=Out */
+	GP_DIR_1 = 1<<17, /* IO_1 direct, 0=In/1=Out */
+	GP_DIR_0 = 1<<16, /* IO_0 direct, 0=In/1=Out */
+
+	GP_IO_9	= 1<<9,	/* IO_9 pin */
+	GP_IO_8	= 1<<8,	/* IO_8 pin */
+	GP_IO_7	= 1<<7,	/* IO_7 pin */
+	GP_IO_6	= 1<<6,	/* IO_6 pin */
+	GP_IO_5	= 1<<5,	/* IO_5 pin */
+	GP_IO_4	= 1<<4,	/* IO_4 pin */
+	GP_IO_3	= 1<<3,	/* IO_3 pin */
+	GP_IO_2	= 1<<2,	/* IO_2 pin */
+	GP_IO_1	= 1<<1,	/* IO_1 pin */
+	GP_IO_0	= 1<<0,	/* IO_0 pin */
+};
+
+/* Descriptor Bit Definition */
+/*	TxCtrl		Transmit Buffer Control Field */
+/*	RxCtrl		Receive  Buffer Control Field */
+enum {
+	BMU_OWN		= 1<<31, /* OWN bit: 0=host/1=BMU */
+	BMU_STF		= 1<<30, /* Start of Frame */
+	BMU_EOF		= 1<<29, /* End of Frame */
+	BMU_IRQ_EOB	= 1<<28, /* Req "End of Buffer" IRQ */
+	BMU_IRQ_EOF	= 1<<27, /* Req "End of Frame" IRQ */
+				/* TxCtrl specific bits */
+	BMU_STFWD	= 1<<26, /* (Tx)	Store & Forward Frame */
+	BMU_NO_FCS	= 1<<25, /* (Tx) Disable MAC FCS (CRC) generation */
+	BMU_SW	= 1<<24, /* (Tx)	1 bit res. for SW use */
+				/* RxCtrl specific bits */
+	BMU_DEV_0	= 1<<26, /* (Rx)	Transfer data to Dev0 */
+	BMU_STAT_VAL	= 1<<25, /* (Rx)	Rx Status Valid */
+	BMU_TIST_VAL	= 1<<24, /* (Rx)	Rx TimeStamp Valid */
+			/* Bit 23..16:	BMU Check Opcodes */
+	BMU_CHECK	= 0x55<<16, /* Default BMU check */
+	BMU_TCP_CHECK	= 0x56<<16, /* Descr with TCP ext */
+	BMU_UDP_CHECK	= 0x57<<16, /* Descr with UDP ext (YUKON only) */
+	BMU_BBC		= 0xffffL, /* Bit 15.. 0:	Buffer Byte Counter */
+};
+
+/*	B2_BSC_CTRL		 8 bit	Blink Source Counter Control */
+enum {
+	 BSC_START	= 1<<1,	/* Start Blink Source Counter */
+	 BSC_STOP	= 1<<0,	/* Stop  Blink Source Counter */
+};
+
+/*	B2_BSC_STAT		 8 bit	Blink Source Counter Status */
+enum {
+	BSC_SRC		= 1<<0,	/* Blink Source, 0=Off / 1=On */
+};
+
+/*	B2_BSC_TST		16 bit	Blink Source Counter Test Reg */
+enum {
+	BSC_T_ON	= 1<<2,	/* Test mode on */
+	BSC_T_OFF	= 1<<1,	/* Test mode off */
+	BSC_T_STEP	= 1<<0,	/* Test step */
+};
+
+/*	B3_RAM_ADDR		32 bit	RAM Address, to read or write */
+					/* Bit 31..19:	reserved */
+#define RAM_ADR_RAN	0x0007ffffL	/* Bit 18.. 0:	RAM Address Range */
+/* RAM Interface Registers */
+
+/*	B3_RI_CTRL		16 bit	RAM Iface Control Register */
+enum {
+	RI_CLR_RD_PERR	= 1<<9,	/* Clear IRQ RAM Read Parity Err */
+	RI_CLR_WR_PERR	= 1<<8,	/* Clear IRQ RAM Write Parity Err*/
+
+	RI_RST_CLR	= 1<<1,	/* Clear RAM Interface Reset */
+	RI_RST_SET	= 1<<0,	/* Set   RAM Interface Reset */
+};
+
+/* MAC Arbiter Registers */
+/*	B3_MA_TO_CTRL	16 bit	MAC Arbiter Timeout Ctrl Reg */
+enum {
+	MA_FOE_ON	= 1<<3,	/* XMAC Fast Output Enable ON */
+	MA_FOE_OFF	= 1<<2,	/* XMAC Fast Output Enable OFF */
+	MA_RST_CLR	= 1<<1,	/* Clear MAC Arbiter Reset */
+	MA_RST_SET	= 1<<0,	/* Set   MAC Arbiter Reset */
+
+};
+
+/* Timeout values */
+#define SK_MAC_TO_53	72		/* MAC arbiter timeout */
+#define SK_PKT_TO_53	0x2000		/* Packet arbiter timeout */
+#define SK_PKT_TO_MAX	0xffff		/* Maximum value */
+#define SK_RI_TO_53	36		/* RAM interface timeout */
+
+/* Packet Arbiter Registers */
+/*	B3_PA_CTRL		16 bit	Packet Arbiter Ctrl Register */
+enum {
+	PA_CLR_TO_TX2	= 1<<13,/* Clear IRQ Packet Timeout TX2 */
+	PA_CLR_TO_TX1	= 1<<12,/* Clear IRQ Packet Timeout TX1 */
+	PA_CLR_TO_RX2	= 1<<11,/* Clear IRQ Packet Timeout RX2 */
+	PA_CLR_TO_RX1	= 1<<10,/* Clear IRQ Packet Timeout RX1 */
+	PA_ENA_TO_TX2	= 1<<9,	/* Enable  Timeout Timer TX2 */
+	PA_DIS_TO_TX2	= 1<<8,	/* Disable Timeout Timer TX2 */
+	PA_ENA_TO_TX1	= 1<<7,	/* Enable  Timeout Timer TX1 */
+	PA_DIS_TO_TX1	= 1<<6,	/* Disable Timeout Timer TX1 */
+	PA_ENA_TO_RX2	= 1<<5,	/* Enable  Timeout Timer RX2 */
+	PA_DIS_TO_RX2	= 1<<4,	/* Disable Timeout Timer RX2 */
+	PA_ENA_TO_RX1	= 1<<3,	/* Enable  Timeout Timer RX1 */
+	PA_DIS_TO_RX1	= 1<<2,	/* Disable Timeout Timer RX1 */
+	PA_RST_CLR	= 1<<1,	/* Clear MAC Arbiter Reset */
+	PA_RST_SET	= 1<<0,	/* Set   MAC Arbiter Reset */
+};
+
+#define PA_ENA_TO_ALL	(PA_ENA_TO_RX1 | PA_ENA_TO_RX2 |\
+						PA_ENA_TO_TX1 | PA_ENA_TO_TX2)
+
+
+/* Transmit Arbiter Registers MAC 1 and 2, use SK_REG() to access */
+/*	TXA_ITI_INI		32 bit	Tx Arb Interval Timer Init Val */
+/*	TXA_ITI_VAL		32 bit	Tx Arb Interval Timer Value */
+/*	TXA_LIM_INI		32 bit	Tx Arb Limit Counter Init Val */
+/*	TXA_LIM_VAL		32 bit	Tx Arb Limit Counter Value */
+
+#define TXA_MAX_VAL	0x00ffffffUL	/* Bit 23.. 0:	Max TXA Timer/Cnt Val */
+
+/*	TXA_CTRL		 8 bit	Tx Arbiter Control Register */
+enum {
+	TXA_ENA_FSYNC	= 1<<7,	/* Enable  force of sync Tx queue */
+	TXA_DIS_FSYNC	= 1<<6,	/* Disable force of sync Tx queue */
+	TXA_ENA_ALLOC	= 1<<5,	/* Enable  alloc of free bandwidth */
+	TXA_DIS_ALLOC	= 1<<4,	/* Disable alloc of free bandwidth */
+	TXA_START_RC	= 1<<3,	/* Start sync Rate Control */
+	TXA_STOP_RC	= 1<<2,	/* Stop  sync Rate Control */
+	TXA_ENA_ARB	= 1<<1,	/* Enable  Tx Arbiter */
+	TXA_DIS_ARB	= 1<<0,	/* Disable Tx Arbiter */
+};
+
+/*
+ *	Bank 4 - 5
+ */
+/* Transmit Arbiter Registers MAC 1 and 2, use SK_REG() to access */
+enum {
+	TXA_ITI_INI	= 0x0200,/* 32 bit	Tx Arb Interval Timer Init Val*/
+	TXA_ITI_VAL	= 0x0204,/* 32 bit	Tx Arb Interval Timer Value */
+	TXA_LIM_INI	= 0x0208,/* 32 bit	Tx Arb Limit Counter Init Val */
+	TXA_LIM_VAL	= 0x020c,/* 32 bit	Tx Arb Limit Counter Value */
+	TXA_CTRL	= 0x0210,/*  8 bit	Tx Arbiter Control Register */
+	TXA_TEST	= 0x0211,/*  8 bit	Tx Arbiter Test Register */
+	TXA_STAT	= 0x0212,/*  8 bit	Tx Arbiter Status Register */
+};
+
+
+enum {
+	B6_EXT_REG	= 0x0300,/* External registers (GENESIS only) */
+	B7_CFG_SPC	= 0x0380,/* copy of the Configuration register */
+	B8_RQ1_REGS	= 0x0400,/* Receive Queue 1 */
+	B8_RQ2_REGS	= 0x0480,/* Receive Queue 2 */
+	B8_TS1_REGS	= 0x0600,/* Transmit sync queue 1 */
+	B8_TA1_REGS	= 0x0680,/* Transmit async queue 1 */
+	B8_TS2_REGS	= 0x0700,/* Transmit sync queue 2 */
+	B8_TA2_REGS	= 0x0780,/* Transmit sync queue 2 */
+	B16_RAM_REGS	= 0x0800,/* RAM Buffer Registers */
+};
+
+/* Queue Register Offsets, use Q_ADDR() to access */
+enum {
+	B8_Q_REGS = 0x0400, /* base of Queue registers */
+	Q_D	= 0x00,	/* 8*32	bit	Current Descriptor */
+	Q_DA_L	= 0x20,	/* 32 bit	Current Descriptor Address Low dWord */
+	Q_DA_H	= 0x24,	/* 32 bit	Current Descriptor Address High dWord */
+	Q_AC_L	= 0x28,	/* 32 bit	Current Address Counter Low dWord */
+	Q_AC_H	= 0x2c,	/* 32 bit	Current Address Counter High dWord */
+	Q_BC	= 0x30,	/* 32 bit	Current Byte Counter */
+	Q_CSR	= 0x34,	/* 32 bit	BMU Control/Status Register */
+	Q_F	= 0x38,	/* 32 bit	Flag Register */
+	Q_T1	= 0x3c,	/* 32 bit	Test Register 1 */
+	Q_T1_TR	= 0x3c,	/*  8 bit	Test Register 1 Transfer SM */
+	Q_T1_WR	= 0x3d,	/*  8 bit	Test Register 1 Write Descriptor SM */
+	Q_T1_RD	= 0x3e,	/*  8 bit	Test Register 1 Read Descriptor SM */
+	Q_T1_SV	= 0x3f,	/*  8 bit	Test Register 1 Supervisor SM */
+	Q_T2	= 0x40,	/* 32 bit	Test Register 2	*/
+	Q_T3	= 0x44,	/* 32 bit	Test Register 3	*/
+
+};
+#define Q_ADDR(reg, offs) (B8_Q_REGS + (reg) + (offs))
+
+/* RAM Buffer Register Offsets */
+enum {
+
+	RB_START= 0x00,/* 32 bit	RAM Buffer Start Address */
+	RB_END	= 0x04,/* 32 bit	RAM Buffer End Address */
+	RB_WP	= 0x08,/* 32 bit	RAM Buffer Write Pointer */
+	RB_RP	= 0x0c,/* 32 bit	RAM Buffer Read Pointer */
+	RB_RX_UTPP= 0x10,/* 32 bit	Rx Upper Threshold, Pause Packet */
+	RB_RX_LTPP= 0x14,/* 32 bit	Rx Lower Threshold, Pause Packet */
+	RB_RX_UTHP= 0x18,/* 32 bit	Rx Upper Threshold, High Prio */
+	RB_RX_LTHP= 0x1c,/* 32 bit	Rx Lower Threshold, High Prio */
+	/* 0x10 - 0x1f:	reserved at Tx RAM Buffer Registers */
+	RB_PC	= 0x20,/* 32 bit	RAM Buffer Packet Counter */
+	RB_LEV	= 0x24,/* 32 bit	RAM Buffer Level Register */
+	RB_CTRL	= 0x28,/* 32 bit	RAM Buffer Control Register */
+	RB_TST1	= 0x29,/*  8 bit	RAM Buffer Test Register 1 */
+	RB_TST2	= 0x2a,/*  8 bit	RAM Buffer Test Register 2 */
+};
+
+/* Receive and Transmit Queues */
+enum {
+	Q_R1	= 0x0000,	/* Receive Queue 1 */
+	Q_R2	= 0x0080,	/* Receive Queue 2 */
+	Q_XS1	= 0x0200,	/* Synchronous Transmit Queue 1 */
+	Q_XA1	= 0x0280,	/* Asynchronous Transmit Queue 1 */
+	Q_XS2	= 0x0300,	/* Synchronous Transmit Queue 2 */
+	Q_XA2	= 0x0380,	/* Asynchronous Transmit Queue 2 */
+};
+
+/* Different MAC Types */
+enum {
+	SK_MAC_XMAC =	0,	/* Xaqti XMAC II */
+	SK_MAC_GMAC =	1,	/* Marvell GMAC */
+};
+
+/* Different PHY Types */
+enum {
+	SK_PHY_XMAC	= 0,/* integrated in XMAC II */
+	SK_PHY_BCOM	= 1,/* Broadcom BCM5400 */
+	SK_PHY_LONE	= 2,/* Level One LXT1000  [not supported]*/
+	SK_PHY_NAT	= 3,/* National DP83891  [not supported] */
+	SK_PHY_MARV_COPPER= 4,/* Marvell 88E1011S */
+	SK_PHY_MARV_FIBER = 5,/* Marvell 88E1011S working on fiber */
+};
+
+/* PHY addresses (bits 12..8 of PHY address reg) */
+enum {
+	PHY_ADDR_XMAC	= 0<<8,
+	PHY_ADDR_BCOM	= 1<<8,
+
+/* GPHY address (bits 15..11 of SMI control reg) */
+	PHY_ADDR_MARV	= 0,
+};
+
+#define RB_ADDR(offs, queue) ((u16)B16_RAM_REGS + (u16)(queue) + (offs))
+
+/* Receive MAC FIFO, Receive LED, and Link_Sync regs (GENESIS only) */
+enum {
+	RX_MFF_EA	= 0x0c00,/* 32 bit	Receive MAC FIFO End Address */
+	RX_MFF_WP	= 0x0c04,/* 32 bit	Receive MAC FIFO Write Pointer */
+
+	RX_MFF_RP	= 0x0c0c,/* 32 bit	Receive MAC FIFO Read Pointer */
+	RX_MFF_PC	= 0x0c10,/* 32 bit	Receive MAC FIFO Packet Cnt */
+	RX_MFF_LEV	= 0x0c14,/* 32 bit	Receive MAC FIFO Level */
+	RX_MFF_CTRL1	= 0x0c18,/* 16 bit	Receive MAC FIFO Control Reg 1*/
+	RX_MFF_STAT_TO	= 0x0c1a,/*  8 bit	Receive MAC Status Timeout */
+	RX_MFF_TIST_TO	= 0x0c1b,/*  8 bit	Receive MAC Time Stamp Timeout */
+	RX_MFF_CTRL2	= 0x0c1c,/*  8 bit	Receive MAC FIFO Control Reg 2*/
+	RX_MFF_TST1	= 0x0c1d,/*  8 bit	Receive MAC FIFO Test Reg 1 */
+	RX_MFF_TST2	= 0x0c1e,/*  8 bit	Receive MAC FIFO Test Reg 2 */
+
+	RX_LED_INI	= 0x0c20,/* 32 bit	Receive LED Cnt Init Value */
+	RX_LED_VAL	= 0x0c24,/* 32 bit	Receive LED Cnt Current Value */
+	RX_LED_CTRL	= 0x0c28,/*  8 bit	Receive LED Cnt Control Reg */
+	RX_LED_TST	= 0x0c29,/*  8 bit	Receive LED Cnt Test Register */
+
+	LNK_SYNC_INI	= 0x0c30,/* 32 bit	Link Sync Cnt Init Value */
+	LNK_SYNC_VAL	= 0x0c34,/* 32 bit	Link Sync Cnt Current Value */
+	LNK_SYNC_CTRL	= 0x0c38,/*  8 bit	Link Sync Cnt Control Register */
+	LNK_SYNC_TST	= 0x0c39,/*  8 bit	Link Sync Cnt Test Register */
+	LNK_LED_REG	= 0x0c3c,/*  8 bit	Link LED Register */
+};
+
+/* Receive and Transmit MAC FIFO Registers (GENESIS only) */
+/*	RX_MFF_CTRL1	16 bit	Receive MAC FIFO Control Reg 1 */
+enum {
+	MFF_ENA_RDY_PAT	= 1<<13,	/* Enable  Ready Patch */
+	MFF_DIS_RDY_PAT	= 1<<12,	/* Disable Ready Patch */
+	MFF_ENA_TIM_PAT	= 1<<11,	/* Enable  Timing Patch */
+	MFF_DIS_TIM_PAT	= 1<<10,	/* Disable Timing Patch */
+	MFF_ENA_ALM_FUL	= 1<<9,	/* Enable  AlmostFull Sign */
+	MFF_DIS_ALM_FUL	= 1<<8,	/* Disable AlmostFull Sign */
+	MFF_ENA_PAUSE	= 1<<7,	/* Enable  Pause Signaling */
+	MFF_DIS_PAUSE	= 1<<6,	/* Disable Pause Signaling */
+	MFF_ENA_FLUSH	= 1<<5,	/* Enable  Frame Flushing */
+	MFF_DIS_FLUSH	= 1<<4,	/* Disable Frame Flushing */
+	MFF_ENA_TIST	= 1<<3,	/* Enable  Time Stamp Gener */
+	MFF_DIS_TIST	= 1<<2,	/* Disable Time Stamp Gener */
+	MFF_CLR_INTIST	= 1<<1,	/* Clear IRQ No Time Stamp */
+	MFF_CLR_INSTAT	= 1<<0,	/* Clear IRQ No Status */
+	MFF_RX_CTRL_DEF = MFF_ENA_TIM_PAT,
+};
+
+/*	TX_MFF_CTRL1	16 bit	Transmit MAC FIFO Control Reg 1 */
+enum {
+	MFF_CLR_PERR	= 1<<15, /* Clear Parity Error IRQ */
+
+	MFF_ENA_PKT_REC	= 1<<13, /* Enable  Packet Recovery */
+	MFF_DIS_PKT_REC	= 1<<12, /* Disable Packet Recovery */
+
+	MFF_ENA_W4E	= 1<<7,	/* Enable  Wait for Empty */
+	MFF_DIS_W4E	= 1<<6,	/* Disable Wait for Empty */
+
+	MFF_ENA_LOOPB	= 1<<3,	/* Enable  Loopback */
+	MFF_DIS_LOOPB	= 1<<2,	/* Disable Loopback */
+	MFF_CLR_MAC_RST	= 1<<1,	/* Clear XMAC Reset */
+	MFF_SET_MAC_RST	= 1<<0,	/* Set   XMAC Reset */
+
+	MFF_TX_CTRL_DEF	 = MFF_ENA_PKT_REC | (u16) MFF_ENA_TIM_PAT | MFF_ENA_FLUSH,
+};
+
+
+/*	RX_MFF_TST2	 	 8 bit	Receive MAC FIFO Test Register 2 */
+/*	TX_MFF_TST2	 	 8 bit	Transmit MAC FIFO Test Register 2 */
+enum {
+	MFF_WSP_T_ON	= 1<<6,	/* Tx: Write Shadow Ptr TestOn */
+	MFF_WSP_T_OFF	= 1<<5,	/* Tx: Write Shadow Ptr TstOff */
+	MFF_WSP_INC	= 1<<4,	/* Tx: Write Shadow Ptr Increment */
+	MFF_PC_DEC	= 1<<3,	/* Packet Counter Decrement */
+	MFF_PC_T_ON	= 1<<2,	/* Packet Counter Test On */
+	MFF_PC_T_OFF	= 1<<1,	/* Packet Counter Test Off */
+	MFF_PC_INC	= 1<<0,	/* Packet Counter Increment */
+};
+
+/*	RX_MFF_TST1	 	 8 bit	Receive MAC FIFO Test Register 1 */
+/*	TX_MFF_TST1	 	 8 bit	Transmit MAC FIFO Test Register 1 */
+enum {
+	MFF_WP_T_ON	= 1<<6,	/* Write Pointer Test On */
+	MFF_WP_T_OFF	= 1<<5,	/* Write Pointer Test Off */
+	MFF_WP_INC	= 1<<4,	/* Write Pointer Increm */
+
+	MFF_RP_T_ON	= 1<<2,	/* Read Pointer Test On */
+	MFF_RP_T_OFF	= 1<<1,	/* Read Pointer Test Off */
+	MFF_RP_DEC	= 1<<0,	/* Read Pointer Decrement */
+};
+
+/*	RX_MFF_CTRL2	 8 bit	Receive MAC FIFO Control Reg 2 */
+/*	TX_MFF_CTRL2	 8 bit	Transmit MAC FIFO Control Reg 2 */
+enum {
+	MFF_ENA_OP_MD	= 1<<3,	/* Enable  Operation Mode */
+	MFF_DIS_OP_MD	= 1<<2,	/* Disable Operation Mode */
+	MFF_RST_CLR	= 1<<1,	/* Clear MAC FIFO Reset */
+	MFF_RST_SET	= 1<<0,	/* Set   MAC FIFO Reset */
+};
+
+
+/*	Link LED Counter Registers (GENESIS only) */
+
+/*	RX_LED_CTRL		 8 bit	Receive LED Cnt Control Reg */
+/*	TX_LED_CTRL		 8 bit	Transmit LED Cnt Control Reg */
+/*	LNK_SYNC_CTRL	 8 bit	Link Sync Cnt Control Register */
+enum {
+	LED_START	= 1<<2,	/* Start Timer */
+	LED_STOP	= 1<<1,	/* Stop Timer */
+	LED_STATE	= 1<<0,	/* Rx/Tx: LED State, 1=LED on */
+};
+
+/*	RX_LED_TST		 8 bit	Receive LED Cnt Test Register */
+/*	TX_LED_TST		 8 bit	Transmit LED Cnt Test Register */
+/*	LNK_SYNC_TST	 8 bit	Link Sync Cnt Test Register */
+enum {
+	LED_T_ON	= 1<<2,	/* LED Counter Test mode On */
+	LED_T_OFF	= 1<<1,	/* LED Counter Test mode Off */
+	LED_T_STEP	= 1<<0,	/* LED Counter Step */
+};
+
+/*	LNK_LED_REG	 	 8 bit	Link LED Register */
+enum {
+	LED_BLK_ON	= 1<<5,	/* Link LED Blinking On */
+	LED_BLK_OFF	= 1<<4,	/* Link LED Blinking Off */
+	LED_SYNC_ON	= 1<<3,	/* Use Sync Wire to switch LED */
+	LED_SYNC_OFF	= 1<<2,	/* Disable Sync Wire Input */
+	LED_ON	= 1<<1,	/* switch LED on */
+	LED_OFF	= 1<<0,	/* switch LED off */
+};
+
+/* Receive GMAC FIFO (YUKON) */
+enum {
+	RX_GMF_EA	= 0x0c40,/* 32 bit	Rx GMAC FIFO End Address */
+	RX_GMF_AF_THR	= 0x0c44,/* 32 bit	Rx GMAC FIFO Almost Full Thresh. */
+	RX_GMF_CTRL_T	= 0x0c48,/* 32 bit	Rx GMAC FIFO Control/Test */
+	RX_GMF_FL_MSK	= 0x0c4c,/* 32 bit	Rx GMAC FIFO Flush Mask */
+	RX_GMF_FL_THR	= 0x0c50,/* 32 bit	Rx GMAC FIFO Flush Threshold */
+	RX_GMF_WP	= 0x0c60,/* 32 bit	Rx GMAC FIFO Write Pointer */
+	RX_GMF_WLEV	= 0x0c68,/* 32 bit	Rx GMAC FIFO Write Level */
+	RX_GMF_RP	= 0x0c70,/* 32 bit	Rx GMAC FIFO Read Pointer */
+	RX_GMF_RLEV	= 0x0c78,/* 32 bit	Rx GMAC FIFO Read Level */
+};
+
+
+/*	TXA_TEST		 8 bit	Tx Arbiter Test Register */
+enum {
+	TXA_INT_T_ON	= 1<<5,	/* Tx Arb Interval Timer Test On */
+	TXA_INT_T_OFF	= 1<<4,	/* Tx Arb Interval Timer Test Off */
+	TXA_INT_T_STEP	= 1<<3,	/* Tx Arb Interval Timer Step */
+	TXA_LIM_T_ON	= 1<<2,	/* Tx Arb Limit Timer Test On */
+	TXA_LIM_T_OFF	= 1<<1,	/* Tx Arb Limit Timer Test Off */
+	TXA_LIM_T_STEP	= 1<<0,	/* Tx Arb Limit Timer Step */
+};
+
+/*	TXA_STAT		 8 bit	Tx Arbiter Status Register */
+enum {
+	TXA_PRIO_XS	= 1<<0,	/* sync queue has prio to send */
+};
+
+
+/*	Q_BC			32 bit	Current Byte Counter */
+
+/* BMU Control Status Registers */
+/*	B0_R1_CSR		32 bit	BMU Ctrl/Stat Rx Queue 1 */
+/*	B0_R2_CSR		32 bit	BMU Ctrl/Stat Rx Queue 2 */
+/*	B0_XA1_CSR		32 bit	BMU Ctrl/Stat Sync Tx Queue 1 */
+/*	B0_XS1_CSR		32 bit	BMU Ctrl/Stat Async Tx Queue 1 */
+/*	B0_XA2_CSR		32 bit	BMU Ctrl/Stat Sync Tx Queue 2 */
+/*	B0_XS2_CSR		32 bit	BMU Ctrl/Stat Async Tx Queue 2 */
+/*	Q_CSR			32 bit	BMU Control/Status Register */
+
+enum {
+	CSR_SV_IDLE	= 1<<24,	/* BMU SM Idle */
+
+	CSR_DESC_CLR	= 1<<21,	/* Clear Reset for Descr */
+	CSR_DESC_SET	= 1<<20,	/* Set   Reset for Descr */
+	CSR_FIFO_CLR	= 1<<19,	/* Clear Reset for FIFO */
+	CSR_FIFO_SET	= 1<<18,	/* Set   Reset for FIFO */
+	CSR_HPI_RUN	= 1<<17,	/* Release HPI SM */
+	CSR_HPI_RST	= 1<<16,	/* Reset   HPI SM to Idle */
+	CSR_SV_RUN	= 1<<15,	/* Release Supervisor SM */
+	CSR_SV_RST	= 1<<14,	/* Reset   Supervisor SM */
+	CSR_DREAD_RUN	= 1<<13,	/* Release Descr Read SM */
+	CSR_DREAD_RST	= 1<<12,	/* Reset   Descr Read SM */
+	CSR_DWRITE_RUN	= 1<<11,	/* Release Descr Write SM */
+	CSR_DWRITE_RST	= 1<<10,	/* Reset   Descr Write SM */
+	CSR_TRANS_RUN	= 1<<9,		/* Release Transfer SM */
+	CSR_TRANS_RST	= 1<<8,		/* Reset   Transfer SM */
+	CSR_ENA_POL	= 1<<7,		/* Enable  Descr Polling */
+	CSR_DIS_POL	= 1<<6,		/* Disable Descr Polling */
+	CSR_STOP	= 1<<5,		/* Stop  Rx/Tx Queue */
+	CSR_START	= 1<<4,		/* Start Rx/Tx Queue */
+	CSR_IRQ_CL_P	= 1<<3,		/* (Rx)	Clear Parity IRQ */
+	CSR_IRQ_CL_B	= 1<<2,		/* Clear EOB IRQ */
+	CSR_IRQ_CL_F	= 1<<1,		/* Clear EOF IRQ */
+	CSR_IRQ_CL_C	= 1<<0,		/* Clear ERR IRQ */
+};
+
+#define CSR_SET_RESET	(CSR_DESC_SET | CSR_FIFO_SET | CSR_HPI_RST |\
+			CSR_SV_RST | CSR_DREAD_RST | CSR_DWRITE_RST |\
+			CSR_TRANS_RST)
+#define CSR_CLR_RESET	(CSR_DESC_CLR | CSR_FIFO_CLR | CSR_HPI_RUN |\
+			CSR_SV_RUN | CSR_DREAD_RUN | CSR_DWRITE_RUN |\
+			CSR_TRANS_RUN)
+
+/*	Q_F				32 bit	Flag Register */
+enum {
+	F_ALM_FULL	= 1<<27,	/* Rx FIFO: almost full */
+	F_EMPTY		= 1<<27,	/* Tx FIFO: empty flag */
+	F_FIFO_EOF	= 1<<26,	/* Tag (EOF Flag) bit in FIFO */
+	F_WM_REACHED	= 1<<25,	/* Watermark reached */
+
+	F_FIFO_LEVEL	= 0x1fL<<16,	/* Bit 23..16:	# of Qwords in FIFO */
+	F_WATER_MARK	= 0x0007ffL,	/* Bit 10.. 0:	Watermark */
+};
+
+/* RAM Buffer Register Offsets, use RB_ADDR(Queue, Offs) to access */
+/*	RB_START		32 bit	RAM Buffer Start Address */
+/*	RB_END			32 bit	RAM Buffer End Address */
+/*	RB_WP			32 bit	RAM Buffer Write Pointer */
+/*	RB_RP			32 bit	RAM Buffer Read Pointer */
+/*	RB_RX_UTPP		32 bit	Rx Upper Threshold, Pause Pack */
+/*	RB_RX_LTPP		32 bit	Rx Lower Threshold, Pause Pack */
+/*	RB_RX_UTHP		32 bit	Rx Upper Threshold, High Prio */
+/*	RB_RX_LTHP		32 bit	Rx Lower Threshold, High Prio */
+/*	RB_PC			32 bit	RAM Buffer Packet Counter */
+/*	RB_LEV			32 bit	RAM Buffer Level Register */
+
+#define RB_MSK	0x0007ffff	/* Bit 18.. 0:	RAM Buffer Pointer Bits */
+/*	RB_TST2			 8 bit	RAM Buffer Test Register 2 */
+/*	RB_TST1			 8 bit	RAM Buffer Test Register 1 */
+
+/*	RB_CTRL			 8 bit	RAM Buffer Control Register */
+enum {
+	RB_ENA_STFWD	= 1<<5,	/* Enable  Store & Forward */
+	RB_DIS_STFWD	= 1<<4,	/* Disable Store & Forward */
+	RB_ENA_OP_MD	= 1<<3,	/* Enable  Operation Mode */
+	RB_DIS_OP_MD	= 1<<2,	/* Disable Operation Mode */
+	RB_RST_CLR	= 1<<1,	/* Clear RAM Buf STM Reset */
+	RB_RST_SET	= 1<<0,	/* Set   RAM Buf STM Reset */
+};
+
+/* Transmit MAC FIFO and Transmit LED Registers (GENESIS only), */
+enum {
+	TX_MFF_EA	= 0x0d00,/* 32 bit	Transmit MAC FIFO End Address */
+	TX_MFF_WP	= 0x0d04,/* 32 bit	Transmit MAC FIFO WR Pointer */
+	TX_MFF_WSP	= 0x0d08,/* 32 bit	Transmit MAC FIFO WR Shadow Ptr */
+	TX_MFF_RP	= 0x0d0c,/* 32 bit	Transmit MAC FIFO RD Pointer */
+	TX_MFF_PC	= 0x0d10,/* 32 bit	Transmit MAC FIFO Packet Cnt */
+	TX_MFF_LEV	= 0x0d14,/* 32 bit	Transmit MAC FIFO Level */
+	TX_MFF_CTRL1	= 0x0d18,/* 16 bit	Transmit MAC FIFO Ctrl Reg 1 */
+	TX_MFF_WAF	= 0x0d1a,/*  8 bit	Transmit MAC Wait after flush */
+
+	TX_MFF_CTRL2	= 0x0d1c,/*  8 bit	Transmit MAC FIFO Ctrl Reg 2 */
+	TX_MFF_TST1	= 0x0d1d,/*  8 bit	Transmit MAC FIFO Test Reg 1 */
+	TX_MFF_TST2	= 0x0d1e,/*  8 bit	Transmit MAC FIFO Test Reg 2 */
+
+	TX_LED_INI	= 0x0d20,/* 32 bit	Transmit LED Cnt Init Value */
+	TX_LED_VAL	= 0x0d24,/* 32 bit	Transmit LED Cnt Current Val */
+	TX_LED_CTRL	= 0x0d28,/*  8 bit	Transmit LED Cnt Control Reg */
+	TX_LED_TST	= 0x0d29,/*  8 bit	Transmit LED Cnt Test Reg */
+};
+
+/* Counter and Timer constants, for a host clock of 62.5 MHz */
+#define SK_XMIT_DUR		0x002faf08UL	/*  50 ms */
+#define SK_BLK_DUR		0x01dcd650UL	/* 500 ms */
+
+#define SK_DPOLL_DEF	0x00ee6b28UL	/* 250 ms at 62.5 MHz */
+
+#define SK_DPOLL_MAX	0x00ffffffUL	/* 268 ms at 62.5 MHz */
+					/* 215 ms at 78.12 MHz */
+
+#define SK_FACT_62		100	/* is given in percent */
+#define SK_FACT_53		 85     /* on GENESIS:	53.12 MHz */
+#define SK_FACT_78		125	/* on YUKON:	78.12 MHz */
+
+
+/* Transmit GMAC FIFO (YUKON only) */
+enum {
+	TX_GMF_EA	= 0x0d40,/* 32 bit	Tx GMAC FIFO End Address */
+	TX_GMF_AE_THR	= 0x0d44,/* 32 bit	Tx GMAC FIFO Almost Empty Thresh.*/
+	TX_GMF_CTRL_T	= 0x0d48,/* 32 bit	Tx GMAC FIFO Control/Test */
+
+	TX_GMF_WP	= 0x0d60,/* 32 bit 	Tx GMAC FIFO Write Pointer */
+	TX_GMF_WSP	= 0x0d64,/* 32 bit 	Tx GMAC FIFO Write Shadow Ptr. */
+	TX_GMF_WLEV	= 0x0d68,/* 32 bit 	Tx GMAC FIFO Write Level */
+
+	TX_GMF_RP	= 0x0d70,/* 32 bit 	Tx GMAC FIFO Read Pointer */
+	TX_GMF_RSTP	= 0x0d74,/* 32 bit 	Tx GMAC FIFO Restart Pointer */
+	TX_GMF_RLEV	= 0x0d78,/* 32 bit 	Tx GMAC FIFO Read Level */
+
+	/* Descriptor Poll Timer Registers */
+	B28_DPT_INI	= 0x0e00,/* 24 bit	Descriptor Poll Timer Init Val */
+	B28_DPT_VAL	= 0x0e04,/* 24 bit	Descriptor Poll Timer Curr Val */
+	B28_DPT_CTRL	= 0x0e08,/*  8 bit	Descriptor Poll Timer Ctrl Reg */
+
+	B28_DPT_TST	= 0x0e0a,/*  8 bit	Descriptor Poll Timer Test Reg */
+
+	/* Time Stamp Timer Registers (YUKON only) */
+	GMAC_TI_ST_VAL	= 0x0e14,/* 32 bit	Time Stamp Timer Curr Val */
+	GMAC_TI_ST_CTRL	= 0x0e18,/*  8 bit	Time Stamp Timer Ctrl Reg */
+	GMAC_TI_ST_TST	= 0x0e1a,/*  8 bit	Time Stamp Timer Test Reg */
+};
+
+
+enum {
+	LINKLED_OFF 	     = 0x01,
+	LINKLED_ON  	     = 0x02,
+	LINKLED_LINKSYNC_OFF = 0x04,
+	LINKLED_LINKSYNC_ON  = 0x08,
+	LINKLED_BLINK_OFF    = 0x10,
+	LINKLED_BLINK_ON     = 0x20,
+};
+
+/* GMAC and GPHY Control Registers (YUKON only) */
+enum {
+	GMAC_CTRL	= 0x0f00,/* 32 bit	GMAC Control Reg */
+	GPHY_CTRL	= 0x0f04,/* 32 bit	GPHY Control Reg */
+	GMAC_IRQ_SRC	= 0x0f08,/*  8 bit	GMAC Interrupt Source Reg */
+	GMAC_IRQ_MSK	= 0x0f0c,/*  8 bit	GMAC Interrupt Mask Reg */
+	GMAC_LINK_CTRL	= 0x0f10,/* 16 bit	Link Control Reg */
+
+/* Wake-up Frame Pattern Match Control Registers (YUKON only) */
+
+	WOL_REG_OFFS	= 0x20,/* HW-Bug: Address is + 0x20 against spec. */
+
+	WOL_CTRL_STAT	= 0x0f20,/* 16 bit	WOL Control/Status Reg */
+	WOL_MATCH_CTL	= 0x0f22,/*  8 bit	WOL Match Control Reg */
+	WOL_MATCH_RES	= 0x0f23,/*  8 bit	WOL Match Result Reg */
+	WOL_MAC_ADDR	= 0x0f24,/* 32 bit	WOL MAC Address */
+	WOL_PATT_RPTR	= 0x0f2c,/*  8 bit	WOL Pattern Read Pointer */
+
+/* WOL Pattern Length Registers (YUKON only) */
+
+	WOL_PATT_LEN_LO	= 0x0f30,/* 32 bit	WOL Pattern Length 3..0 */
+	WOL_PATT_LEN_HI	= 0x0f34,/* 24 bit	WOL Pattern Length 6..4 */
+
+/* WOL Pattern Counter Registers (YUKON only) */
+
+	WOL_PATT_CNT_0	= 0x0f38,/* 32 bit	WOL Pattern Counter 3..0 */
+	WOL_PATT_CNT_4	= 0x0f3c,/* 24 bit	WOL Pattern Counter 6..4 */
+};
+#define WOL_REGS(port, x)	(x + (port)*0x80)
+
+enum {
+	WOL_PATT_RAM_1	= 0x1000,/*  WOL Pattern RAM Link 1 */
+	WOL_PATT_RAM_2	= 0x1400,/*  WOL Pattern RAM Link 2 */
+};
+#define WOL_PATT_RAM_BASE(port)	(WOL_PATT_RAM_1 + (port)*0x400)
+
+enum {
+	BASE_XMAC_1	= 0x2000,/* XMAC 1 registers */
+	BASE_GMAC_1	= 0x2800,/* GMAC 1 registers */
+	BASE_XMAC_2	= 0x3000,/* XMAC 2 registers */
+	BASE_GMAC_2	= 0x3800,/* GMAC 2 registers */
+};
+
+/*
+ * Receive Frame Status Encoding
+ */
+enum {
+	XMR_FS_LEN	= 0x3fff<<18,	/* Bit 31..18:	Rx Frame Length */
+	XMR_FS_LEN_SHIFT = 18,
+	XMR_FS_2L_VLAN	= 1<<17, /* Bit 17:	tagged wh 2Lev VLAN ID*/
+	XMR_FS_1_VLAN	= 1<<16, /* Bit 16:	tagged wh 1ev VLAN ID*/
+	XMR_FS_BC	= 1<<15, /* Bit 15:	Broadcast Frame */
+	XMR_FS_MC	= 1<<14, /* Bit 14:	Multicast Frame */
+	XMR_FS_UC	= 1<<13, /* Bit 13:	Unicast Frame */
+
+	XMR_FS_BURST	= 1<<11, /* Bit 11:	Burst Mode */
+	XMR_FS_CEX_ERR	= 1<<10, /* Bit 10:	Carrier Ext. Error */
+	XMR_FS_802_3	= 1<<9, /* Bit  9:	802.3 Frame */
+	XMR_FS_COL_ERR	= 1<<8, /* Bit  8:	Collision Error */
+	XMR_FS_CAR_ERR	= 1<<7, /* Bit  7:	Carrier Event Error */
+	XMR_FS_LEN_ERR	= 1<<6, /* Bit  6:	In-Range Length Error */
+	XMR_FS_FRA_ERR	= 1<<5, /* Bit  5:	Framing Error */
+	XMR_FS_RUNT	= 1<<4, /* Bit  4:	Runt Frame */
+	XMR_FS_LNG_ERR	= 1<<3, /* Bit  3:	Giant (Jumbo) Frame */
+	XMR_FS_FCS_ERR	= 1<<2, /* Bit  2:	Frame Check Sequ Err */
+	XMR_FS_ERR	= 1<<1, /* Bit  1:	Frame Error */
+	XMR_FS_MCTRL	= 1<<0, /* Bit  0:	MAC Control Packet */
+
+/*
+ * XMR_FS_ERR will be set if
+ *	XMR_FS_FCS_ERR, XMR_FS_LNG_ERR, XMR_FS_RUNT,
+ *	XMR_FS_FRA_ERR, XMR_FS_LEN_ERR, or XMR_FS_CEX_ERR
+ * is set. XMR_FS_LNG_ERR and XMR_FS_LEN_ERR will issue
+ * XMR_FS_ERR unless the corresponding bit in the Receive Command
+ * Register is set.
+ */
+};
+
+/*
+,* XMAC-PHY Registers, indirect addressed over the XMAC
+ */
+enum {
+	PHY_XMAC_CTRL		= 0x00,/* 16 bit r/w	PHY Control Register */
+	PHY_XMAC_STAT		= 0x01,/* 16 bit r/w	PHY Status Register */
+	PHY_XMAC_ID0		= 0x02,/* 16 bit r/o	PHY ID0 Register */
+	PHY_XMAC_ID1		= 0x03,/* 16 bit r/o	PHY ID1 Register */
+	PHY_XMAC_AUNE_ADV	= 0x04,/* 16 bit r/w	Auto-Neg. Advertisement */
+	PHY_XMAC_AUNE_LP	= 0x05,/* 16 bit r/o	Link Partner Abi Reg */
+	PHY_XMAC_AUNE_EXP	= 0x06,/* 16 bit r/o	Auto-Neg. Expansion Reg */
+	PHY_XMAC_NEPG		= 0x07,/* 16 bit r/w	Next Page Register */
+	PHY_XMAC_NEPG_LP	= 0x08,/* 16 bit r/o	Next Page Link Partner */
+
+	PHY_XMAC_EXT_STAT	= 0x0f,/* 16 bit r/o	Ext Status Register */
+	PHY_XMAC_RES_ABI	= 0x10,/* 16 bit r/o	PHY Resolved Ability */
+};
+/*
+ * Broadcom-PHY Registers, indirect addressed over XMAC
+ */
+enum {
+	PHY_BCOM_CTRL		= 0x00,/* 16 bit r/w	PHY Control Register */
+	PHY_BCOM_STAT		= 0x01,/* 16 bit r/o	PHY Status Register */
+	PHY_BCOM_ID0		= 0x02,/* 16 bit r/o	PHY ID0 Register */
+	PHY_BCOM_ID1		= 0x03,/* 16 bit r/o	PHY ID1 Register */
+	PHY_BCOM_AUNE_ADV	= 0x04,/* 16 bit r/w	Auto-Neg. Advertisement */
+	PHY_BCOM_AUNE_LP	= 0x05,/* 16 bit r/o	Link Part Ability Reg */
+	PHY_BCOM_AUNE_EXP	= 0x06,/* 16 bit r/o	Auto-Neg. Expansion Reg */
+	PHY_BCOM_NEPG		= 0x07,/* 16 bit r/w	Next Page Register */
+	PHY_BCOM_NEPG_LP	= 0x08,/* 16 bit r/o	Next Page Link Partner */
+	/* Broadcom-specific registers */
+	PHY_BCOM_1000T_CTRL	= 0x09,/* 16 bit r/w	1000Base-T Control Reg */
+	PHY_BCOM_1000T_STAT	= 0x0a,/* 16 bit r/o	1000Base-T Status Reg */
+	PHY_BCOM_EXT_STAT	= 0x0f,/* 16 bit r/o	Extended Status Reg */
+	PHY_BCOM_P_EXT_CTRL	= 0x10,/* 16 bit r/w	PHY Extended Ctrl Reg */
+	PHY_BCOM_P_EXT_STAT	= 0x11,/* 16 bit r/o	PHY Extended Stat Reg */
+	PHY_BCOM_RE_CTR		= 0x12,/* 16 bit r/w	Receive Error Counter */
+	PHY_BCOM_FC_CTR		= 0x13,/* 16 bit r/w	False Carrier Sense Cnt */
+	PHY_BCOM_RNO_CTR	= 0x14,/* 16 bit r/w	Receiver NOT_OK Cnt */
+
+	PHY_BCOM_AUX_CTRL	= 0x18,/* 16 bit r/w	Auxiliary Control Reg */
+	PHY_BCOM_AUX_STAT	= 0x19,/* 16 bit r/o	Auxiliary Stat Summary */
+	PHY_BCOM_INT_STAT	= 0x1a,/* 16 bit r/o	Interrupt Status Reg */
+	PHY_BCOM_INT_MASK	= 0x1b,/* 16 bit r/w	Interrupt Mask Reg */
+};
+
+/*
+ * Marvel-PHY Registers, indirect addressed over GMAC
+ */
+enum {
+	PHY_MARV_CTRL		= 0x00,/* 16 bit r/w	PHY Control Register */
+	PHY_MARV_STAT		= 0x01,/* 16 bit r/o	PHY Status Register */
+	PHY_MARV_ID0		= 0x02,/* 16 bit r/o	PHY ID0 Register */
+	PHY_MARV_ID1		= 0x03,/* 16 bit r/o	PHY ID1 Register */
+	PHY_MARV_AUNE_ADV	= 0x04,/* 16 bit r/w	Auto-Neg. Advertisement */
+	PHY_MARV_AUNE_LP	= 0x05,/* 16 bit r/o	Link Part Ability Reg */
+	PHY_MARV_AUNE_EXP	= 0x06,/* 16 bit r/o	Auto-Neg. Expansion Reg */
+	PHY_MARV_NEPG		= 0x07,/* 16 bit r/w	Next Page Register */
+	PHY_MARV_NEPG_LP	= 0x08,/* 16 bit r/o	Next Page Link Partner */
+	/* Marvel-specific registers */
+	PHY_MARV_1000T_CTRL	= 0x09,/* 16 bit r/w	1000Base-T Control Reg */
+	PHY_MARV_1000T_STAT	= 0x0a,/* 16 bit r/o	1000Base-T Status Reg */
+	PHY_MARV_EXT_STAT	= 0x0f,/* 16 bit r/o	Extended Status Reg */
+	PHY_MARV_PHY_CTRL	= 0x10,/* 16 bit r/w	PHY Specific Ctrl Reg */
+	PHY_MARV_PHY_STAT	= 0x11,/* 16 bit r/o	PHY Specific Stat Reg */
+	PHY_MARV_INT_MASK	= 0x12,/* 16 bit r/w	Interrupt Mask Reg */
+	PHY_MARV_INT_STAT	= 0x13,/* 16 bit r/o	Interrupt Status Reg */
+	PHY_MARV_EXT_CTRL	= 0x14,/* 16 bit r/w	Ext. PHY Specific Ctrl */
+	PHY_MARV_RXE_CNT	= 0x15,/* 16 bit r/w	Receive Error Counter */
+	PHY_MARV_EXT_ADR	= 0x16,/* 16 bit r/w	Ext. Ad. for Cable Diag. */
+	PHY_MARV_PORT_IRQ	= 0x17,/* 16 bit r/o	Port 0 IRQ (88E1111 only) */
+	PHY_MARV_LED_CTRL	= 0x18,/* 16 bit r/w	LED Control Reg */
+	PHY_MARV_LED_OVER	= 0x19,/* 16 bit r/w	Manual LED Override Reg */
+	PHY_MARV_EXT_CTRL_2	= 0x1a,/* 16 bit r/w	Ext. PHY Specific Ctrl 2 */
+	PHY_MARV_EXT_P_STAT	= 0x1b,/* 16 bit r/w	Ext. PHY Spec. Stat Reg */
+	PHY_MARV_CABLE_DIAG	= 0x1c,/* 16 bit r/o	Cable Diagnostic Reg */
+	PHY_MARV_PAGE_ADDR	= 0x1d,/* 16 bit r/w	Extended Page Address Reg */
+	PHY_MARV_PAGE_DATA	= 0x1e,/* 16 bit r/w	Extended Page Data Reg */
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+	PHY_MARV_FE_LED_PAR	= 0x16,/* 16 bit r/w	LED Parallel Select Reg. */
+	PHY_MARV_FE_LED_SER	= 0x17,/* 16 bit r/w	LED Stream Select S. LED */
+	PHY_MARV_FE_VCT_TX	= 0x1a,/* 16 bit r/w	VCT Reg. for TXP/N Pins */
+	PHY_MARV_FE_VCT_RX	= 0x1b,/* 16 bit r/o	VCT Reg. for RXP/N Pins */
+	PHY_MARV_FE_SPEC_2	= 0x1c,/* 16 bit r/w	Specific Control Reg. 2 */
+};
+
+enum {
+	PHY_CT_RESET	= 1<<15, /* Bit 15: (sc)	clear all PHY related regs */
+	PHY_CT_LOOP	= 1<<14, /* Bit 14:	enable Loopback over PHY */
+	PHY_CT_SPS_LSB	= 1<<13, /* Bit 13:	Speed select, lower bit */
+	PHY_CT_ANE	= 1<<12, /* Bit 12:	Auto-Negotiation Enabled */
+	PHY_CT_PDOWN	= 1<<11, /* Bit 11:	Power Down Mode */
+	PHY_CT_ISOL	= 1<<10, /* Bit 10:	Isolate Mode */
+	PHY_CT_RE_CFG	= 1<<9, /* Bit  9:	(sc) Restart Auto-Negotiation */
+	PHY_CT_DUP_MD	= 1<<8, /* Bit  8:	Duplex Mode */
+	PHY_CT_COL_TST	= 1<<7, /* Bit  7:	Collision Test enabled */
+	PHY_CT_SPS_MSB	= 1<<6, /* Bit  6:	Speed select, upper bit */
+};
+
+enum {
+	PHY_CT_SP1000	= PHY_CT_SPS_MSB, /* enable speed of 1000 Mbps */
+	PHY_CT_SP100	= PHY_CT_SPS_LSB, /* enable speed of  100 Mbps */
+	PHY_CT_SP10	= 0,		  /* enable speed of   10 Mbps */
+};
+
+enum {
+	PHY_ST_EXT_ST	= 1<<8, /* Bit  8:	Extended Status Present */
+
+	PHY_ST_PRE_SUP	= 1<<6, /* Bit  6:	Preamble Suppression */
+	PHY_ST_AN_OVER	= 1<<5, /* Bit  5:	Auto-Negotiation Over */
+	PHY_ST_REM_FLT	= 1<<4, /* Bit  4:	Remote Fault Condition Occured */
+	PHY_ST_AN_CAP	= 1<<3, /* Bit  3:	Auto-Negotiation Capability */
+	PHY_ST_LSYNC	= 1<<2, /* Bit  2:	Link Synchronized */
+	PHY_ST_JAB_DET	= 1<<1, /* Bit  1:	Jabber Detected */
+	PHY_ST_EXT_REG	= 1<<0, /* Bit  0:	Extended Register available */
+};
+
+enum {
+	PHY_I1_OUI_MSK	= 0x3f<<10, /* Bit 15..10:	Organization Unique ID */
+	PHY_I1_MOD_NUM	= 0x3f<<4, /* Bit  9.. 4:	Model Number */
+	PHY_I1_REV_MSK	= 0xf, /* Bit  3.. 0:	Revision Number */
+};
+
+/* different Broadcom PHY Ids */
+enum {
+	PHY_BCOM_ID1_A1	= 0x6041,
+	PHY_BCOM_ID1_B2 = 0x6043,
+	PHY_BCOM_ID1_C0	= 0x6044,
+	PHY_BCOM_ID1_C5	= 0x6047,
+};
+
+/* different Marvell PHY Ids */
+enum {
+	PHY_MARV_ID0_VAL= 0x0141, /* Marvell Unique Identifier */
+	PHY_MARV_ID1_B0	= 0x0C23, /* Yukon (PHY 88E1011) */
+	PHY_MARV_ID1_B2	= 0x0C25, /* Yukon-Plus (PHY 88E1011) */
+	PHY_MARV_ID1_C2	= 0x0CC2, /* Yukon-EC (PHY 88E1111) */
+	PHY_MARV_ID1_Y2	= 0x0C91, /* Yukon-2 (PHY 88E1112) */
+};
+
+/* Advertisement register bits */
+enum {
+	PHY_AN_NXT_PG	= 1<<15, /* Bit 15:	Request Next Page */
+	PHY_AN_ACK	= 1<<14, /* Bit 14:	(ro) Acknowledge Received */
+	PHY_AN_RF	= 1<<13, /* Bit 13:	Remote Fault Bits */
+
+	PHY_AN_PAUSE_ASYM = 1<<11,/* Bit 11:	Try for asymmetric */
+	PHY_AN_PAUSE_CAP = 1<<10, /* Bit 10:	Try for pause */
+	PHY_AN_100BASE4	= 1<<9, /* Bit 9:	Try for 100mbps 4k packets */
+	PHY_AN_100FULL	= 1<<8, /* Bit 8:	Try for 100mbps full-duplex */
+	PHY_AN_100HALF	= 1<<7, /* Bit 7:	Try for 100mbps half-duplex */
+	PHY_AN_10FULL	= 1<<6, /* Bit 6:	Try for 10mbps full-duplex */
+	PHY_AN_10HALF	= 1<<5, /* Bit 5:	Try for 10mbps half-duplex */
+	PHY_AN_CSMA	= 1<<0, /* Bit 0:	Only selector supported */
+	PHY_AN_SEL	= 0x1f, /* Bit 4..0:	Selector Field, 00001=Ethernet*/
+	PHY_AN_FULL	= PHY_AN_100FULL | PHY_AN_10FULL | PHY_AN_CSMA,
+	PHY_AN_ALL	= PHY_AN_10HALF | PHY_AN_10FULL |
+			  PHY_AN_100HALF | PHY_AN_100FULL,
+};
+
+/* Xmac Specific */
+enum {
+	PHY_X_AN_NXT_PG	= 1<<15, /* Bit 15:	Request Next Page */
+	PHY_X_AN_ACK	= 1<<14, /* Bit 14:	(ro) Acknowledge Received */
+	PHY_X_AN_RFB	= 3<<12,/* Bit 13..12:	Remote Fault Bits */
+
+	PHY_X_AN_PAUSE	= 3<<7,/* Bit  8.. 7:	Pause Bits */
+	PHY_X_AN_HD	= 1<<6, /* Bit  6:	Half Duplex */
+	PHY_X_AN_FD	= 1<<5, /* Bit  5:	Full Duplex */
+};
+
+/* Pause Bits (PHY_X_AN_PAUSE and PHY_X_RS_PAUSE) encoding */
+enum {
+	PHY_X_P_NO_PAUSE= 0<<7,/* Bit  8..7:	no Pause Mode */
+	PHY_X_P_SYM_MD	= 1<<7, /* Bit  8..7:	symmetric Pause Mode */
+	PHY_X_P_ASYM_MD	= 2<<7,/* Bit  8..7:	asymmetric Pause Mode */
+	PHY_X_P_BOTH_MD	= 3<<7,/* Bit  8..7:	both Pause Mode */
+};
+
+
+/*****  PHY_XMAC_EXT_STAT	16 bit r/w	Extended Status Register *****/
+enum {
+	PHY_X_EX_FD	= 1<<15, /* Bit 15:	Device Supports Full Duplex */
+	PHY_X_EX_HD	= 1<<14, /* Bit 14:	Device Supports Half Duplex */
+};
+
+/*****  PHY_XMAC_RES_ABI	16 bit r/o	PHY Resolved Ability *****/
+enum {
+	PHY_X_RS_PAUSE	= 3<<7,	/* Bit  8..7:	selected Pause Mode */
+	PHY_X_RS_HD	= 1<<6,	/* Bit  6:	Half Duplex Mode selected */
+	PHY_X_RS_FD	= 1<<5,	/* Bit  5:	Full Duplex Mode selected */
+	PHY_X_RS_ABLMIS = 1<<4,	/* Bit  4:	duplex or pause cap mismatch */
+	PHY_X_RS_PAUMIS = 1<<3,	/* Bit  3:	pause capability mismatch */
+};
+
+/* Remote Fault Bits (PHY_X_AN_RFB) encoding */
+enum {
+	X_RFB_OK	= 0<<12,/* Bit 13..12	No errors, Link OK */
+	X_RFB_LF	= 1<<12,/* Bit 13..12	Link Failure */
+	X_RFB_OFF	= 2<<12,/* Bit 13..12	Offline */
+	X_RFB_AN_ERR	= 3<<12,/* Bit 13..12	Auto-Negotiation Error */
+};
+
+/* Broadcom-Specific */
+/*****  PHY_BCOM_1000T_CTRL	16 bit r/w	1000Base-T Control Reg *****/
+enum {
+	PHY_B_1000C_TEST	= 7<<13,/* Bit 15..13:	Test Modes */
+	PHY_B_1000C_MSE	= 1<<12, /* Bit 12:	Master/Slave Enable */
+	PHY_B_1000C_MSC	= 1<<11, /* Bit 11:	M/S Configuration */
+	PHY_B_1000C_RD	= 1<<10, /* Bit 10:	Repeater/DTE */
+	PHY_B_1000C_AFD	= 1<<9, /* Bit  9:	Advertise Full Duplex */
+	PHY_B_1000C_AHD	= 1<<8, /* Bit  8:	Advertise Half Duplex */
+};
+
+/*****  PHY_BCOM_1000T_STAT	16 bit r/o	1000Base-T Status Reg *****/
+/*****  PHY_MARV_1000T_STAT	16 bit r/o	1000Base-T Status Reg *****/
+enum {
+	PHY_B_1000S_MSF	= 1<<15, /* Bit 15:	Master/Slave Fault */
+	PHY_B_1000S_MSR	= 1<<14, /* Bit 14:	Master/Slave Result */
+	PHY_B_1000S_LRS	= 1<<13, /* Bit 13:	Local Receiver Status */
+	PHY_B_1000S_RRS	= 1<<12, /* Bit 12:	Remote Receiver Status */
+	PHY_B_1000S_LP_FD	= 1<<11, /* Bit 11:	Link Partner can FD */
+	PHY_B_1000S_LP_HD	= 1<<10, /* Bit 10:	Link Partner can HD */
+									/* Bit  9..8:	reserved */
+	PHY_B_1000S_IEC	= 0xff, /* Bit  7..0:	Idle Error Count */
+};
+
+/*****  PHY_BCOM_EXT_STAT	16 bit r/o	Extended Status Register *****/
+enum {
+	PHY_B_ES_X_FD_CAP	= 1<<15, /* Bit 15:	1000Base-X FD capable */
+	PHY_B_ES_X_HD_CAP	= 1<<14, /* Bit 14:	1000Base-X HD capable */
+	PHY_B_ES_T_FD_CAP	= 1<<13, /* Bit 13:	1000Base-T FD capable */
+	PHY_B_ES_T_HD_CAP	= 1<<12, /* Bit 12:	1000Base-T HD capable */
+};
+
+/*****  PHY_BCOM_P_EXT_CTRL	16 bit r/w	PHY Extended Control Reg *****/
+enum {
+	PHY_B_PEC_MAC_PHY	= 1<<15, /* Bit 15:	10BIT/GMI-Interface */
+	PHY_B_PEC_DIS_CROSS	= 1<<14, /* Bit 14:	Disable MDI Crossover */
+	PHY_B_PEC_TX_DIS	= 1<<13, /* Bit 13:	Tx output Disabled */
+	PHY_B_PEC_INT_DIS	= 1<<12, /* Bit 12:	Interrupts Disabled */
+	PHY_B_PEC_F_INT	= 1<<11, /* Bit 11:	Force Interrupt */
+	PHY_B_PEC_BY_45	= 1<<10, /* Bit 10:	Bypass 4B5B-Decoder */
+	PHY_B_PEC_BY_SCR	= 1<<9, /* Bit  9:	Bypass Scrambler */
+	PHY_B_PEC_BY_MLT3	= 1<<8, /* Bit  8:	Bypass MLT3 Encoder */
+	PHY_B_PEC_BY_RXA	= 1<<7, /* Bit  7:	Bypass Rx Alignm. */
+	PHY_B_PEC_RES_SCR	= 1<<6, /* Bit  6:	Reset Scrambler */
+	PHY_B_PEC_EN_LTR	= 1<<5, /* Bit  5:	Ena LED Traffic Mode */
+	PHY_B_PEC_LED_ON	= 1<<4, /* Bit  4:	Force LED's on */
+	PHY_B_PEC_LED_OFF	= 1<<3, /* Bit  3:	Force LED's off */
+	PHY_B_PEC_EX_IPG	= 1<<2, /* Bit  2:	Extend Tx IPG Mode */
+	PHY_B_PEC_3_LED	= 1<<1, /* Bit  1:	Three Link LED mode */
+	PHY_B_PEC_HIGH_LA	= 1<<0, /* Bit  0:	GMII FIFO Elasticy */
+};
+
+/*****  PHY_BCOM_P_EXT_STAT	16 bit r/o	PHY Extended Status Reg *****/
+enum {
+	PHY_B_PES_CROSS_STAT	= 1<<13, /* Bit 13:	MDI Crossover Status */
+	PHY_B_PES_INT_STAT	= 1<<12, /* Bit 12:	Interrupt Status */
+	PHY_B_PES_RRS	= 1<<11, /* Bit 11:	Remote Receiver Stat. */
+	PHY_B_PES_LRS	= 1<<10, /* Bit 10:	Local Receiver Stat. */
+	PHY_B_PES_LOCKED	= 1<<9, /* Bit  9:	Locked */
+	PHY_B_PES_LS	= 1<<8, /* Bit  8:	Link Status */
+	PHY_B_PES_RF	= 1<<7, /* Bit  7:	Remote Fault */
+	PHY_B_PES_CE_ER	= 1<<6, /* Bit  6:	Carrier Ext Error */
+	PHY_B_PES_BAD_SSD	= 1<<5, /* Bit  5:	Bad SSD */
+	PHY_B_PES_BAD_ESD	= 1<<4, /* Bit  4:	Bad ESD */
+	PHY_B_PES_RX_ER	= 1<<3, /* Bit  3:	Receive Error */
+	PHY_B_PES_TX_ER	= 1<<2, /* Bit  2:	Transmit Error */
+	PHY_B_PES_LOCK_ER	= 1<<1, /* Bit  1:	Lock Error */
+	PHY_B_PES_MLT3_ER	= 1<<0, /* Bit  0:	MLT3 code Error */
+};
+
+/*  PHY_BCOM_AUNE_ADV	16 bit r/w	Auto-Negotiation Advertisement *****/
+/*  PHY_BCOM_AUNE_LP	16 bit r/o	Link Partner Ability Reg *****/
+enum {
+	PHY_B_AN_RF	= 1<<13, /* Bit 13:	Remote Fault */
+
+	PHY_B_AN_ASP	= 1<<11, /* Bit 11:	Asymmetric Pause */
+	PHY_B_AN_PC	= 1<<10, /* Bit 10:	Pause Capable */
+};
+
+
+/*****  PHY_BCOM_FC_CTR		16 bit r/w	False Carrier Counter *****/
+enum {
+	PHY_B_FC_CTR	= 0xff, /* Bit  7..0:	False Carrier Counter */
+
+/*****  PHY_BCOM_RNO_CTR	16 bit r/w	Receive NOT_OK Counter *****/
+	PHY_B_RC_LOC_MSK	= 0xff00, /* Bit 15..8:	Local Rx NOT_OK cnt */
+	PHY_B_RC_REM_MSK	= 0x00ff, /* Bit  7..0:	Remote Rx NOT_OK cnt */
+
+/*****  PHY_BCOM_AUX_CTRL	16 bit r/w	Auxiliary Control Reg *****/
+	PHY_B_AC_L_SQE		= 1<<15, /* Bit 15:	Low Squelch */
+	PHY_B_AC_LONG_PACK	= 1<<14, /* Bit 14:	Rx Long Packets */
+	PHY_B_AC_ER_CTRL	= 3<<12,/* Bit 13..12:	Edgerate Control */
+									/* Bit 11:	reserved */
+	PHY_B_AC_TX_TST	= 1<<10, /* Bit 10:	Tx test bit, always 1 */
+									/* Bit  9.. 8:	reserved */
+	PHY_B_AC_DIS_PRF	= 1<<7, /* Bit  7:	dis part resp filter */
+									/* Bit  6:	reserved */
+	PHY_B_AC_DIS_PM	= 1<<5, /* Bit  5:	dis power management */
+									/* Bit  4:	reserved */
+	PHY_B_AC_DIAG	= 1<<3, /* Bit  3:	Diagnostic Mode */
+};
+
+/*****  PHY_BCOM_AUX_STAT	16 bit r/o	Auxiliary Status Reg *****/
+enum {
+	PHY_B_AS_AN_C	= 1<<15, /* Bit 15:	AutoNeg complete */
+	PHY_B_AS_AN_CA	= 1<<14, /* Bit 14:	AN Complete Ack */
+	PHY_B_AS_ANACK_D	= 1<<13, /* Bit 13:	AN Ack Detect */
+	PHY_B_AS_ANAB_D	= 1<<12, /* Bit 12:	AN Ability Detect */
+	PHY_B_AS_NPW	= 1<<11, /* Bit 11:	AN Next Page Wait */
+	PHY_B_AS_AN_RES_MSK	= 7<<8,/* Bit 10..8:	AN HDC */
+	PHY_B_AS_PDF	= 1<<7, /* Bit  7:	Parallel Detect. Fault */
+	PHY_B_AS_RF	= 1<<6, /* Bit  6:	Remote Fault */
+	PHY_B_AS_ANP_R	= 1<<5, /* Bit  5:	AN Page Received */
+	PHY_B_AS_LP_ANAB	= 1<<4, /* Bit  4:	LP AN Ability */
+	PHY_B_AS_LP_NPAB	= 1<<3, /* Bit  3:	LP Next Page Ability */
+	PHY_B_AS_LS	= 1<<2, /* Bit  2:	Link Status */
+	PHY_B_AS_PRR	= 1<<1, /* Bit  1:	Pause Resolution-Rx */
+	PHY_B_AS_PRT	= 1<<0, /* Bit  0:	Pause Resolution-Tx */
+};
+#define PHY_B_AS_PAUSE_MSK	(PHY_B_AS_PRR | PHY_B_AS_PRT)
+
+/*****  PHY_BCOM_INT_STAT	16 bit r/o	Interrupt Status Reg *****/
+/*****  PHY_BCOM_INT_MASK	16 bit r/w	Interrupt Mask Reg *****/
+enum {
+	PHY_B_IS_PSE	= 1<<14, /* Bit 14:	Pair Swap Error */
+	PHY_B_IS_MDXI_SC	= 1<<13, /* Bit 13:	MDIX Status Change */
+	PHY_B_IS_HCT	= 1<<12, /* Bit 12:	counter above 32k */
+	PHY_B_IS_LCT	= 1<<11, /* Bit 11:	counter above 128 */
+	PHY_B_IS_AN_PR	= 1<<10, /* Bit 10:	Page Received */
+	PHY_B_IS_NO_HDCL	= 1<<9, /* Bit  9:	No HCD Link */
+	PHY_B_IS_NO_HDC	= 1<<8, /* Bit  8:	No HCD */
+	PHY_B_IS_NEG_USHDC	= 1<<7, /* Bit  7:	Negotiated Unsup. HCD */
+	PHY_B_IS_SCR_S_ER	= 1<<6, /* Bit  6:	Scrambler Sync Error */
+	PHY_B_IS_RRS_CHANGE	= 1<<5, /* Bit  5:	Remote Rx Stat Change */
+	PHY_B_IS_LRS_CHANGE	= 1<<4, /* Bit  4:	Local Rx Stat Change */
+	PHY_B_IS_DUP_CHANGE	= 1<<3, /* Bit  3:	Duplex Mode Change */
+	PHY_B_IS_LSP_CHANGE	= 1<<2, /* Bit  2:	Link Speed Change */
+	PHY_B_IS_LST_CHANGE	= 1<<1, /* Bit  1:	Link Status Changed */
+	PHY_B_IS_CRC_ER	= 1<<0, /* Bit  0:	CRC Error */
+};
+#define PHY_B_DEF_MSK	\
+	(~(PHY_B_IS_PSE | PHY_B_IS_AN_PR | PHY_B_IS_DUP_CHANGE | \
+	    PHY_B_IS_LSP_CHANGE | PHY_B_IS_LST_CHANGE))
+
+/* Pause Bits (PHY_B_AN_ASP and PHY_B_AN_PC) encoding */
+enum {
+	PHY_B_P_NO_PAUSE	= 0<<10,/* Bit 11..10:	no Pause Mode */
+	PHY_B_P_SYM_MD	= 1<<10, /* Bit 11..10:	symmetric Pause Mode */
+	PHY_B_P_ASYM_MD	= 2<<10,/* Bit 11..10:	asymmetric Pause Mode */
+	PHY_B_P_BOTH_MD	= 3<<10,/* Bit 11..10:	both Pause Mode */
+};
+/*
+ * Resolved Duplex mode and Capabilities (Aux Status Summary Reg)
+ */
+enum {
+	PHY_B_RES_1000FD	= 7<<8,/* Bit 10..8:	1000Base-T Full Dup. */
+	PHY_B_RES_1000HD	= 6<<8,/* Bit 10..8:	1000Base-T Half Dup. */
+};
+
+/** Marvell-Specific */
+enum {
+	PHY_M_AN_NXT_PG	= 1<<15, /* Request Next Page */
+	PHY_M_AN_ACK	= 1<<14, /* (ro)	Acknowledge Received */
+	PHY_M_AN_RF	= 1<<13, /* Remote Fault */
+
+	PHY_M_AN_ASP	= 1<<11, /* Asymmetric Pause */
+	PHY_M_AN_PC	= 1<<10, /* MAC Pause implemented */
+	PHY_M_AN_100_T4	= 1<<9, /* Not cap. 100Base-T4 (always 0) */
+	PHY_M_AN_100_FD	= 1<<8, /* Advertise 100Base-TX Full Duplex */
+	PHY_M_AN_100_HD	= 1<<7, /* Advertise 100Base-TX Half Duplex */
+	PHY_M_AN_10_FD	= 1<<6, /* Advertise 10Base-TX Full Duplex */
+	PHY_M_AN_10_HD	= 1<<5, /* Advertise 10Base-TX Half Duplex */
+	PHY_M_AN_SEL_MSK =0x1f<<4,	/* Bit  4.. 0: Selector Field Mask */
+};
+
+/* special defines for FIBER (88E1011S only) */
+enum {
+	PHY_M_AN_ASP_X		= 1<<8, /* Asymmetric Pause */
+	PHY_M_AN_PC_X		= 1<<7, /* MAC Pause implemented */
+	PHY_M_AN_1000X_AHD	= 1<<6, /* Advertise 10000Base-X Half Duplex */
+	PHY_M_AN_1000X_AFD	= 1<<5, /* Advertise 10000Base-X Full Duplex */
+};
+
+/* Pause Bits (PHY_M_AN_ASP_X and PHY_M_AN_PC_X) encoding */
+enum {
+	PHY_M_P_NO_PAUSE_X	= 0<<7,/* Bit  8.. 7:	no Pause Mode */
+	PHY_M_P_SYM_MD_X	= 1<<7, /* Bit  8.. 7:	symmetric Pause Mode */
+	PHY_M_P_ASYM_MD_X	= 2<<7,/* Bit  8.. 7:	asymmetric Pause Mode */
+	PHY_M_P_BOTH_MD_X	= 3<<7,/* Bit  8.. 7:	both Pause Mode */
+};
+
+/*****  PHY_MARV_1000T_CTRL	16 bit r/w	1000Base-T Control Reg *****/
+enum {
+	PHY_M_1000C_TEST= 7<<13,/* Bit 15..13:	Test Modes */
+	PHY_M_1000C_MSE	= 1<<12, /* Manual Master/Slave Enable */
+	PHY_M_1000C_MSC	= 1<<11, /* M/S Configuration (1=Master) */
+	PHY_M_1000C_MPD	= 1<<10, /* Multi-Port Device */
+	PHY_M_1000C_AFD	= 1<<9, /* Advertise Full Duplex */
+	PHY_M_1000C_AHD	= 1<<8, /* Advertise Half Duplex */
+};
+
+/*****  PHY_MARV_PHY_CTRL	16 bit r/w	PHY Specific Ctrl Reg *****/
+enum {
+	PHY_M_PC_TX_FFD_MSK	= 3<<14,/* Bit 15..14: Tx FIFO Depth Mask */
+	PHY_M_PC_RX_FFD_MSK	= 3<<12,/* Bit 13..12: Rx FIFO Depth Mask */
+	PHY_M_PC_ASS_CRS_TX	= 1<<11, /* Assert CRS on Transmit */
+	PHY_M_PC_FL_GOOD	= 1<<10, /* Force Link Good */
+	PHY_M_PC_EN_DET_MSK	= 3<<8,/* Bit  9.. 8: Energy Detect Mask */
+	PHY_M_PC_ENA_EXT_D	= 1<<7, /* Enable Ext. Distance (10BT) */
+	PHY_M_PC_MDIX_MSK	= 3<<5,/* Bit  6.. 5: MDI/MDIX Config. Mask */
+	PHY_M_PC_DIS_125CLK	= 1<<4, /* Disable 125 CLK */
+	PHY_M_PC_MAC_POW_UP	= 1<<3, /* MAC Power up */
+	PHY_M_PC_SQE_T_ENA	= 1<<2, /* SQE Test Enabled */
+	PHY_M_PC_POL_R_DIS	= 1<<1, /* Polarity Reversal Disabled */
+	PHY_M_PC_DIS_JABBER	= 1<<0, /* Disable Jabber */
+};
+
+enum {
+	PHY_M_PC_EN_DET		= 2<<8,	/* Energy Detect (Mode 1) */
+	PHY_M_PC_EN_DET_PLUS	= 3<<8, /* Energy Detect Plus (Mode 2) */
+};
+
+enum {
+	PHY_M_PC_MAN_MDI	= 0, /* 00 = Manual MDI configuration */
+	PHY_M_PC_MAN_MDIX	= 1, /* 01 = Manual MDIX configuration */
+	PHY_M_PC_ENA_AUTO	= 3, /* 11 = Enable Automatic Crossover */
+};
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+enum {
+	PHY_M_PC_ENA_DTE_DT	= 1<<15, /* Enable Data Terminal Equ. (DTE) Detect */
+	PHY_M_PC_ENA_ENE_DT	= 1<<14, /* Enable Energy Detect (sense & pulse) */
+	PHY_M_PC_DIS_NLP_CK	= 1<<13, /* Disable Normal Link Puls (NLP) Check */
+	PHY_M_PC_ENA_LIP_NP	= 1<<12, /* Enable Link Partner Next Page Reg. */
+	PHY_M_PC_DIS_NLP_GN	= 1<<11, /* Disable Normal Link Puls Generation */
+
+	PHY_M_PC_DIS_SCRAMB	= 1<<9, /* Disable Scrambler */
+	PHY_M_PC_DIS_FEFI	= 1<<8, /* Disable Far End Fault Indic. (FEFI) */
+
+	PHY_M_PC_SH_TP_SEL	= 1<<6, /* Shielded Twisted Pair Select */
+	PHY_M_PC_RX_FD_MSK	= 3<<2,/* Bit  3.. 2: Rx FIFO Depth Mask */
+};
+
+/*****  PHY_MARV_PHY_STAT	16 bit r/o	PHY Specific Status Reg *****/
+enum {
+	PHY_M_PS_SPEED_MSK	= 3<<14, /* Bit 15..14: Speed Mask */
+	PHY_M_PS_SPEED_1000	= 1<<15, /*		10 = 1000 Mbps */
+	PHY_M_PS_SPEED_100	= 1<<14, /*		01 =  100 Mbps */
+	PHY_M_PS_SPEED_10	= 0,	 /*		00 =   10 Mbps */
+	PHY_M_PS_FULL_DUP	= 1<<13, /* Full Duplex */
+	PHY_M_PS_PAGE_REC	= 1<<12, /* Page Received */
+	PHY_M_PS_SPDUP_RES	= 1<<11, /* Speed & Duplex Resolved */
+	PHY_M_PS_LINK_UP	= 1<<10, /* Link Up */
+	PHY_M_PS_CABLE_MSK	= 7<<7,  /* Bit  9.. 7: Cable Length Mask */
+	PHY_M_PS_MDI_X_STAT	= 1<<6,  /* MDI Crossover Stat (1=MDIX) */
+	PHY_M_PS_DOWNS_STAT	= 1<<5,  /* Downshift Status (1=downsh.) */
+	PHY_M_PS_ENDET_STAT	= 1<<4,  /* Energy Detect Status (1=act) */
+	PHY_M_PS_TX_P_EN	= 1<<3,  /* Tx Pause Enabled */
+	PHY_M_PS_RX_P_EN	= 1<<2,  /* Rx Pause Enabled */
+	PHY_M_PS_POL_REV	= 1<<1,  /* Polarity Reversed */
+	PHY_M_PS_JABBER		= 1<<0,  /* Jabber */
+};
+
+#define PHY_M_PS_PAUSE_MSK	(PHY_M_PS_TX_P_EN | PHY_M_PS_RX_P_EN)
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+enum {
+	PHY_M_PS_DTE_DETECT	= 1<<15, /* Data Terminal Equipment (DTE) Detected */
+	PHY_M_PS_RES_SPEED	= 1<<14, /* Resolved Speed (1=100 Mbps, 0=10 Mbps */
+};
+
+enum {
+	PHY_M_IS_AN_ERROR	= 1<<15, /* Auto-Negotiation Error */
+	PHY_M_IS_LSP_CHANGE	= 1<<14, /* Link Speed Changed */
+	PHY_M_IS_DUP_CHANGE	= 1<<13, /* Duplex Mode Changed */
+	PHY_M_IS_AN_PR		= 1<<12, /* Page Received */
+	PHY_M_IS_AN_COMPL	= 1<<11, /* Auto-Negotiation Completed */
+	PHY_M_IS_LST_CHANGE	= 1<<10, /* Link Status Changed */
+	PHY_M_IS_SYMB_ERROR	= 1<<9, /* Symbol Error */
+	PHY_M_IS_FALSE_CARR	= 1<<8, /* False Carrier */
+	PHY_M_IS_FIFO_ERROR	= 1<<7, /* FIFO Overflow/Underrun Error */
+	PHY_M_IS_MDI_CHANGE	= 1<<6, /* MDI Crossover Changed */
+	PHY_M_IS_DOWNSH_DET	= 1<<5, /* Downshift Detected */
+	PHY_M_IS_END_CHANGE	= 1<<4, /* Energy Detect Changed */
+
+	PHY_M_IS_DTE_CHANGE	= 1<<2, /* DTE Power Det. Status Changed */
+	PHY_M_IS_POL_CHANGE	= 1<<1, /* Polarity Changed */
+	PHY_M_IS_JABBER		= 1<<0, /* Jabber */
+
+	PHY_M_IS_DEF_MSK	= PHY_M_IS_AN_ERROR | PHY_M_IS_LSP_CHANGE |
+				  PHY_M_IS_LST_CHANGE | PHY_M_IS_FIFO_ERROR,
+
+	PHY_M_IS_AN_MSK		= PHY_M_IS_AN_ERROR | PHY_M_IS_AN_COMPL,
+};
+
+/*****  PHY_MARV_EXT_CTRL	16 bit r/w	Ext. PHY Specific Ctrl *****/
+enum {
+	PHY_M_EC_ENA_BC_EXT = 1<<15, /* Enable Block Carr. Ext. (88E1111 only) */
+	PHY_M_EC_ENA_LIN_LB = 1<<14, /* Enable Line Loopback (88E1111 only) */
+
+	PHY_M_EC_DIS_LINK_P = 1<<12, /* Disable Link Pulses (88E1111 only) */
+	PHY_M_EC_M_DSC_MSK  = 3<<10, /* Bit 11..10:	Master Downshift Counter */
+					/* (88E1011 only) */
+	PHY_M_EC_S_DSC_MSK  = 3<<8,  /* Bit  9.. 8:	Slave  Downshift Counter */
+				       /* (88E1011 only) */
+	PHY_M_EC_M_DSC_MSK2  = 7<<9, /* Bit 11.. 9:	Master Downshift Counter */
+					/* (88E1111 only) */
+	PHY_M_EC_DOWN_S_ENA  = 1<<8, /* Downshift Enable (88E1111 only) */
+					/* !!! Errata in spec. (1 = disable) */
+	PHY_M_EC_RX_TIM_CT   = 1<<7, /* RGMII Rx Timing Control*/
+	PHY_M_EC_MAC_S_MSK   = 7<<4, /* Bit  6.. 4:	Def. MAC interface speed */
+	PHY_M_EC_FIB_AN_ENA  = 1<<3, /* Fiber Auto-Neg. Enable (88E1011S only) */
+	PHY_M_EC_DTE_D_ENA   = 1<<2, /* DTE Detect Enable (88E1111 only) */
+	PHY_M_EC_TX_TIM_CT   = 1<<1, /* RGMII Tx Timing Control */
+	PHY_M_EC_TRANS_DIS   = 1<<0, /* Transmitter Disable (88E1111 only) */};
+
+#define PHY_M_EC_M_DSC(x)	((u16)(x)<<10) /* 00=1x; 01=2x; 10=3x; 11=4x */
+#define PHY_M_EC_S_DSC(x)	((u16)(x)<<8) /* 00=dis; 01=1x; 10=2x; 11=3x */
+#define PHY_M_EC_MAC_S(x)	((u16)(x)<<4) /* 01X=0; 110=2.5; 111=25 (MHz) */
+
+#define PHY_M_EC_M_DSC_2(x)	((u16)(x)<<9) /* 000=1x; 001=2x; 010=3x; 011=4x */
+											/* 100=5x; 101=6x; 110=7x; 111=8x */
+enum {
+	MAC_TX_CLK_0_MHZ	= 2,
+	MAC_TX_CLK_2_5_MHZ	= 6,
+	MAC_TX_CLK_25_MHZ 	= 7,
+};
+
+/*****  PHY_MARV_LED_CTRL	16 bit r/w	LED Control Reg *****/
+enum {
+	PHY_M_LEDC_DIS_LED	= 1<<15, /* Disable LED */
+	PHY_M_LEDC_PULS_MSK	= 7<<12,/* Bit 14..12: Pulse Stretch Mask */
+	PHY_M_LEDC_F_INT	= 1<<11, /* Force Interrupt */
+	PHY_M_LEDC_BL_R_MSK	= 7<<8,/* Bit 10.. 8: Blink Rate Mask */
+	PHY_M_LEDC_DP_C_LSB	= 1<<7, /* Duplex Control (LSB, 88E1111 only) */
+	PHY_M_LEDC_TX_C_LSB	= 1<<6, /* Tx Control (LSB, 88E1111 only) */
+	PHY_M_LEDC_LK_C_MSK	= 7<<3,/* Bit  5.. 3: Link Control Mask */
+					/* (88E1111 only) */
+};
+#define PHY_M_LED_PULS_DUR(x)	(((u16)(x)<<12) & PHY_M_LEDC_PULS_MSK)
+#define PHY_M_LED_BLINK_RT(x)	(((u16)(x)<<8) & PHY_M_LEDC_BL_R_MSK)
+
+enum {
+	PHY_M_LEDC_LINK_MSK	= 3<<3, /* Bit  4.. 3: Link Control Mask */
+					/* (88E1011 only) */
+	PHY_M_LEDC_DP_CTRL	= 1<<2, /* Duplex Control */
+	PHY_M_LEDC_DP_C_MSB	= 1<<2, /* Duplex Control (MSB, 88E1111 only) */
+	PHY_M_LEDC_RX_CTRL	= 1<<1, /* Rx Activity / Link */
+	PHY_M_LEDC_TX_CTRL	= 1<<0, /* Tx Activity / Link */
+	PHY_M_LEDC_TX_C_MSB	= 1<<0, /* Tx Control (MSB, 88E1111 only) */
+};
+
+enum {
+	PULS_NO_STR	= 0, /* no pulse stretching */
+	PULS_21MS	= 1, /* 21 ms to 42 ms */
+	PULS_42MS	= 2, /* 42 ms to 84 ms */
+	PULS_84MS	= 3, /* 84 ms to 170 ms */
+	PULS_170MS	= 4, /* 170 ms to 340 ms */
+	PULS_340MS	= 5, /* 340 ms to 670 ms */
+	PULS_670MS	= 6, /* 670 ms to 1.3 s */
+	PULS_1300MS	= 7, /* 1.3 s to 2.7 s */
+};
+
+
+enum {
+	BLINK_42MS	= 0, /* 42 ms */
+	BLINK_84MS	= 1, /* 84 ms */
+	BLINK_170MS	= 2, /* 170 ms */
+	BLINK_340MS	= 3, /* 340 ms */
+	BLINK_670MS	= 4, /* 670 ms */
+};
+
+/*****  PHY_MARV_LED_OVER	16 bit r/w	Manual LED Override Reg *****/
+#define PHY_M_LED_MO_SGMII(x)	((x)<<14) /* Bit 15..14:  SGMII AN Timer */
+										/* Bit 13..12:	reserved */
+#define PHY_M_LED_MO_DUP(x)	((x)<<10) /* Bit 11..10:  Duplex */
+#define PHY_M_LED_MO_10(x)	((x)<<8) /* Bit  9.. 8:  Link 10 */
+#define PHY_M_LED_MO_100(x)	((x)<<6) /* Bit  7.. 6:  Link 100 */
+#define PHY_M_LED_MO_1000(x)	((x)<<4) /* Bit  5.. 4:  Link 1000 */
+#define PHY_M_LED_MO_RX(x)	((x)<<2) /* Bit  3.. 2:  Rx */
+#define PHY_M_LED_MO_TX(x)	((x)<<0) /* Bit  1.. 0:  Tx */
+
+enum {
+	MO_LED_NORM	= 0,
+	MO_LED_BLINK	= 1,
+	MO_LED_OFF	= 2,
+	MO_LED_ON	= 3,
+};
+
+/*****  PHY_MARV_EXT_CTRL_2	16 bit r/w	Ext. PHY Specific Ctrl 2 *****/
+enum {
+	PHY_M_EC2_FI_IMPED	= 1<<6, /* Fiber Input  Impedance */
+	PHY_M_EC2_FO_IMPED	= 1<<5, /* Fiber Output Impedance */
+	PHY_M_EC2_FO_M_CLK	= 1<<4, /* Fiber Mode Clock Enable */
+	PHY_M_EC2_FO_BOOST	= 1<<3, /* Fiber Output Boost */
+	PHY_M_EC2_FO_AM_MSK	= 7, /* Bit  2.. 0:	Fiber Output Amplitude */
+};
+
+/*****  PHY_MARV_EXT_P_STAT 16 bit r/w	Ext. PHY Specific Status *****/
+enum {
+	PHY_M_FC_AUTO_SEL	= 1<<15, /* Fiber/Copper Auto Sel. Dis. */
+	PHY_M_FC_AN_REG_ACC	= 1<<14, /* Fiber/Copper AN Reg. Access */
+	PHY_M_FC_RESOLUTION	= 1<<13, /* Fiber/Copper Resolution */
+	PHY_M_SER_IF_AN_BP	= 1<<12, /* Ser. IF AN Bypass Enable */
+	PHY_M_SER_IF_BP_ST	= 1<<11, /* Ser. IF AN Bypass Status */
+	PHY_M_IRQ_POLARITY	= 1<<10, /* IRQ polarity */
+	PHY_M_DIS_AUT_MED	= 1<<9, /* Disable Aut. Medium Reg. Selection */
+									/* (88E1111 only) */
+								/* Bit  9.. 4: reserved (88E1011 only) */
+	PHY_M_UNDOC1	= 1<<7, /* undocumented bit !! */
+	PHY_M_DTE_POW_STAT	= 1<<4, /* DTE Power Status (88E1111 only) */
+	PHY_M_MODE_MASK	= 0xf, /* Bit  3.. 0: copy of HWCFG MODE[3:0] */
+};
+
+/*****  PHY_MARV_CABLE_DIAG	16 bit r/o	Cable Diagnostic Reg *****/
+enum {
+	PHY_M_CABD_ENA_TEST	= 1<<15, /* Enable Test (Page 0) */
+	PHY_M_CABD_DIS_WAIT	= 1<<15, /* Disable Waiting Period (Page 1) */
+					/* (88E1111 only) */
+	PHY_M_CABD_STAT_MSK	= 3<<13, /* Bit 14..13: Status Mask */
+	PHY_M_CABD_AMPL_MSK	= 0x1f<<8, /* Bit 12.. 8: Amplitude Mask */
+					/* (88E1111 only) */
+	PHY_M_CABD_DIST_MSK	= 0xff, /* Bit  7.. 0: Distance Mask */
+};
+
+/* values for Cable Diagnostic Status (11=fail; 00=OK; 10=open; 01=short) */
+enum {
+	CABD_STAT_NORMAL= 0,
+	CABD_STAT_SHORT	= 1,
+	CABD_STAT_OPEN	= 2,
+	CABD_STAT_FAIL	= 3,
+};
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+/*****  PHY_MARV_FE_LED_PAR		16 bit r/w	LED Parallel Select Reg. *****/
+									/* Bit 15..12: reserved (used internally) */
+enum {
+	PHY_M_FELP_LED2_MSK = 0xf<<8,	/* Bit 11.. 8: LED2 Mask (LINK) */
+	PHY_M_FELP_LED1_MSK = 0xf<<4,	/* Bit  7.. 4: LED1 Mask (ACT) */
+	PHY_M_FELP_LED0_MSK = 0xf, /* Bit  3.. 0: LED0 Mask (SPEED) */
+};
+
+#define PHY_M_FELP_LED2_CTRL(x)	(((x)<<8) & PHY_M_FELP_LED2_MSK)
+#define PHY_M_FELP_LED1_CTRL(x)	(((x)<<4) & PHY_M_FELP_LED1_MSK)
+#define PHY_M_FELP_LED0_CTRL(x)	(((x)<<0) & PHY_M_FELP_LED0_MSK)
+
+enum {
+	LED_PAR_CTRL_COLX	= 0x00,
+	LED_PAR_CTRL_ERROR	= 0x01,
+	LED_PAR_CTRL_DUPLEX	= 0x02,
+	LED_PAR_CTRL_DP_COL	= 0x03,
+	LED_PAR_CTRL_SPEED	= 0x04,
+	LED_PAR_CTRL_LINK	= 0x05,
+	LED_PAR_CTRL_TX		= 0x06,
+	LED_PAR_CTRL_RX		= 0x07,
+	LED_PAR_CTRL_ACT	= 0x08,
+	LED_PAR_CTRL_LNK_RX	= 0x09,
+	LED_PAR_CTRL_LNK_AC	= 0x0a,
+	LED_PAR_CTRL_ACT_BL	= 0x0b,
+	LED_PAR_CTRL_TX_BL	= 0x0c,
+	LED_PAR_CTRL_RX_BL	= 0x0d,
+	LED_PAR_CTRL_COL_BL	= 0x0e,
+	LED_PAR_CTRL_INACT	= 0x0f
+};
+
+/*****,PHY_MARV_FE_SPEC_2		16 bit r/w	Specific Control Reg. 2 *****/
+enum {
+	PHY_M_FESC_DIS_WAIT	= 1<<2, /* Disable TDR Waiting Period */
+	PHY_M_FESC_ENA_MCLK	= 1<<1, /* Enable MAC Rx Clock in sleep mode */
+	PHY_M_FESC_SEL_CL_A	= 1<<0, /* Select Class A driver (100B-TX) */
+};
+
+
+/*****  PHY_MARV_PHY_CTRL (page 3)		16 bit r/w	LED Control Reg. *****/
+enum {
+	PHY_M_LEDC_LOS_MSK	= 0xf<<12, /* Bit 15..12: LOS LED Ctrl. Mask */
+	PHY_M_LEDC_INIT_MSK	= 0xf<<8, /* Bit 11.. 8: INIT LED Ctrl. Mask */
+	PHY_M_LEDC_STA1_MSK	= 0xf<<4, /* Bit  7.. 4: STAT1 LED Ctrl. Mask */
+	PHY_M_LEDC_STA0_MSK	= 0xf, /* Bit  3.. 0: STAT0 LED Ctrl. Mask */
+};
+
+#define PHY_M_LEDC_LOS_CTRL(x)	(((x)<<12) & PHY_M_LEDC_LOS_MSK)
+#define PHY_M_LEDC_INIT_CTRL(x)	(((x)<<8) & PHY_M_LEDC_INIT_MSK)
+#define PHY_M_LEDC_STA1_CTRL(x)	(((x)<<4) & PHY_M_LEDC_STA1_MSK)
+#define PHY_M_LEDC_STA0_CTRL(x)	(((x)<<0) & PHY_M_LEDC_STA0_MSK)
+
+/* GMAC registers  */
+/* Port Registers */
+enum {
+	GM_GP_STAT	= 0x0000,	/* 16 bit r/o	General Purpose Status */
+	GM_GP_CTRL	= 0x0004,	/* 16 bit r/w	General Purpose Control */
+	GM_TX_CTRL	= 0x0008,	/* 16 bit r/w	Transmit Control Reg. */
+	GM_RX_CTRL	= 0x000c,	/* 16 bit r/w	Receive Control Reg. */
+	GM_TX_FLOW_CTRL	= 0x0010,	/* 16 bit r/w	Transmit Flow-Control */
+	GM_TX_PARAM	= 0x0014,	/* 16 bit r/w	Transmit Parameter Reg. */
+	GM_SERIAL_MODE	= 0x0018,	/* 16 bit r/w	Serial Mode Register */
+/* Source Address Registers */
+	GM_SRC_ADDR_1L	= 0x001c,	/* 16 bit r/w	Source Address 1 (low) */
+	GM_SRC_ADDR_1M	= 0x0020,	/* 16 bit r/w	Source Address 1 (middle) */
+	GM_SRC_ADDR_1H	= 0x0024,	/* 16 bit r/w	Source Address 1 (high) */
+	GM_SRC_ADDR_2L	= 0x0028,	/* 16 bit r/w	Source Address 2 (low) */
+	GM_SRC_ADDR_2M	= 0x002c,	/* 16 bit r/w	Source Address 2 (middle) */
+	GM_SRC_ADDR_2H	= 0x0030,	/* 16 bit r/w	Source Address 2 (high) */
+
+/* Multicast Address Hash Registers */
+	GM_MC_ADDR_H1	= 0x0034,	/* 16 bit r/w	Multicast Address Hash 1 */
+	GM_MC_ADDR_H2	= 0x0038,	/* 16 bit r/w	Multicast Address Hash 2 */
+	GM_MC_ADDR_H3	= 0x003c,	/* 16 bit r/w	Multicast Address Hash 3 */
+	GM_MC_ADDR_H4	= 0x0040,	/* 16 bit r/w	Multicast Address Hash 4 */
+
+/* Interrupt Source Registers */
+	GM_TX_IRQ_SRC	= 0x0044,	/* 16 bit r/o	Tx Overflow IRQ Source */
+	GM_RX_IRQ_SRC	= 0x0048,	/* 16 bit r/o	Rx Overflow IRQ Source */
+	GM_TR_IRQ_SRC	= 0x004c,	/* 16 bit r/o	Tx/Rx Over. IRQ Source */
+
+/* Interrupt Mask Registers */
+	GM_TX_IRQ_MSK	= 0x0050,	/* 16 bit r/w	Tx Overflow IRQ Mask */
+	GM_RX_IRQ_MSK	= 0x0054,	/* 16 bit r/w	Rx Overflow IRQ Mask */
+	GM_TR_IRQ_MSK	= 0x0058,	/* 16 bit r/w	Tx/Rx Over. IRQ Mask */
+
+/* Serial Management Interface (SMI) Registers */
+	GM_SMI_CTRL	= 0x0080,	/* 16 bit r/w	SMI Control Register */
+	GM_SMI_DATA	= 0x0084,	/* 16 bit r/w	SMI Data Register */
+	GM_PHY_ADDR	= 0x0088,	/* 16 bit r/w	GPHY Address Register */
+};
+
+/* MIB Counters */
+#define GM_MIB_CNT_BASE	0x0100		/* Base Address of MIB Counters */
+#define GM_MIB_CNT_SIZE	44		/* Number of MIB Counters */
+
+/*
+ * MIB Counters base address definitions (low word) -
+ * use offset 4 for access to high word	(32 bit r/o)
+ */
+enum {
+	GM_RXF_UC_OK  = GM_MIB_CNT_BASE + 0,	/* Unicast Frames Received OK */
+	GM_RXF_BC_OK	= GM_MIB_CNT_BASE + 8,	/* Broadcast Frames Received OK */
+	GM_RXF_MPAUSE	= GM_MIB_CNT_BASE + 16,	/* Pause MAC Ctrl Frames Received */
+	GM_RXF_MC_OK	= GM_MIB_CNT_BASE + 24,	/* Multicast Frames Received OK */
+	GM_RXF_FCS_ERR	= GM_MIB_CNT_BASE + 32,	/* Rx Frame Check Seq. Error */
+	/* GM_MIB_CNT_BASE + 40:	reserved */
+	GM_RXO_OK_LO	= GM_MIB_CNT_BASE + 48,	/* Octets Received OK Low */
+	GM_RXO_OK_HI	= GM_MIB_CNT_BASE + 56,	/* Octets Received OK High */
+	GM_RXO_ERR_LO	= GM_MIB_CNT_BASE + 64,	/* Octets Received Invalid Low */
+	GM_RXO_ERR_HI	= GM_MIB_CNT_BASE + 72,	/* Octets Received Invalid High */
+	GM_RXF_SHT	= GM_MIB_CNT_BASE + 80,	/* Frames <64 Byte Received OK */
+	GM_RXE_FRAG	= GM_MIB_CNT_BASE + 88,	/* Frames <64 Byte Received with FCS Err */
+	GM_RXF_64B	= GM_MIB_CNT_BASE + 96,	/* 64 Byte Rx Frame */
+	GM_RXF_127B	= GM_MIB_CNT_BASE + 104,	/* 65-127 Byte Rx Frame */
+	GM_RXF_255B	= GM_MIB_CNT_BASE + 112,	/* 128-255 Byte Rx Frame */
+	GM_RXF_511B	= GM_MIB_CNT_BASE + 120,	/* 256-511 Byte Rx Frame */
+	GM_RXF_1023B	= GM_MIB_CNT_BASE + 128,	/* 512-1023 Byte Rx Frame */
+	GM_RXF_1518B	= GM_MIB_CNT_BASE + 136,	/* 1024-1518 Byte Rx Frame */
+	GM_RXF_MAX_SZ	= GM_MIB_CNT_BASE + 144,	/* 1519-MaxSize Byte Rx Frame */
+	GM_RXF_LNG_ERR	= GM_MIB_CNT_BASE + 152,	/* Rx Frame too Long Error */
+	GM_RXF_JAB_PKT	= GM_MIB_CNT_BASE + 160,	/* Rx Jabber Packet Frame */
+	/* GM_MIB_CNT_BASE + 168:	reserved */
+	GM_RXE_FIFO_OV	= GM_MIB_CNT_BASE + 176,	/* Rx FIFO overflow Event */
+	/* GM_MIB_CNT_BASE + 184:	reserved */
+	GM_TXF_UC_OK	= GM_MIB_CNT_BASE + 192,	/* Unicast Frames Xmitted OK */
+	GM_TXF_BC_OK	= GM_MIB_CNT_BASE + 200,	/* Broadcast Frames Xmitted OK */
+	GM_TXF_MPAUSE	= GM_MIB_CNT_BASE + 208,	/* Pause MAC Ctrl Frames Xmitted */
+	GM_TXF_MC_OK	= GM_MIB_CNT_BASE + 216,	/* Multicast Frames Xmitted OK */
+	GM_TXO_OK_LO	= GM_MIB_CNT_BASE + 224,	/* Octets Transmitted OK Low */
+	GM_TXO_OK_HI	= GM_MIB_CNT_BASE + 232,	/* Octets Transmitted OK High */
+	GM_TXF_64B	= GM_MIB_CNT_BASE + 240,	/* 64 Byte Tx Frame */
+	GM_TXF_127B	= GM_MIB_CNT_BASE + 248,	/* 65-127 Byte Tx Frame */
+	GM_TXF_255B	= GM_MIB_CNT_BASE + 256,	/* 128-255 Byte Tx Frame */
+	GM_TXF_511B	= GM_MIB_CNT_BASE + 264,	/* 256-511 Byte Tx Frame */
+	GM_TXF_1023B	= GM_MIB_CNT_BASE + 272,	/* 512-1023 Byte Tx Frame */
+	GM_TXF_1518B	= GM_MIB_CNT_BASE + 280,	/* 1024-1518 Byte Tx Frame */
+	GM_TXF_MAX_SZ	= GM_MIB_CNT_BASE + 288,	/* 1519-MaxSize Byte Tx Frame */
+
+	GM_TXF_COL	= GM_MIB_CNT_BASE + 304,	/* Tx Collision */
+	GM_TXF_LAT_COL	= GM_MIB_CNT_BASE + 312,	/* Tx Late Collision */
+	GM_TXF_ABO_COL	= GM_MIB_CNT_BASE + 320,	/* Tx aborted due to Exces. Col. */
+	GM_TXF_MUL_COL	= GM_MIB_CNT_BASE + 328,	/* Tx Multiple Collision */
+	GM_TXF_SNG_COL	= GM_MIB_CNT_BASE + 336,	/* Tx Single Collision */
+	GM_TXE_FIFO_UR	= GM_MIB_CNT_BASE + 344,	/* Tx FIFO Underrun Event */
+};
+
+/* GMAC Bit Definitions */
+/*	GM_GP_STAT	16 bit r/o	General Purpose Status Register */
+enum {
+	GM_GPSR_SPEED		= 1<<15, /* Bit 15:	Port Speed (1 = 100 Mbps) */
+	GM_GPSR_DUPLEX		= 1<<14, /* Bit 14:	Duplex Mode (1 = Full) */
+	GM_GPSR_FC_TX_DIS	= 1<<13, /* Bit 13:	Tx Flow-Control Mode Disabled */
+	GM_GPSR_LINK_UP		= 1<<12, /* Bit 12:	Link Up Status */
+	GM_GPSR_PAUSE		= 1<<11, /* Bit 11:	Pause State */
+	GM_GPSR_TX_ACTIVE	= 1<<10, /* Bit 10:	Tx in Progress */
+	GM_GPSR_EXC_COL		= 1<<9,	/* Bit  9:	Excessive Collisions Occured */
+	GM_GPSR_LAT_COL		= 1<<8,	/* Bit  8:	Late Collisions Occured */
+
+	GM_GPSR_PHY_ST_CH	= 1<<5,	/* Bit  5:	PHY Status Change */
+	GM_GPSR_GIG_SPEED	= 1<<4,	/* Bit  4:	Gigabit Speed (1 = 1000 Mbps) */
+	GM_GPSR_PART_MODE	= 1<<3,	/* Bit  3:	Partition mode */
+	GM_GPSR_FC_RX_DIS	= 1<<2,	/* Bit  2:	Rx Flow-Control Mode Disabled */
+	GM_GPSR_PROM_EN		= 1<<1,	/* Bit  1:	Promiscuous Mode Enabled */
+};
+
+/*	GM_GP_CTRL	16 bit r/w	General Purpose Control Register */
+enum {
+	GM_GPCR_PROM_ENA	= 1<<14,	/* Bit 14:	Enable Promiscuous Mode */
+	GM_GPCR_FC_TX_DIS	= 1<<13, /* Bit 13:	Disable Tx Flow-Control Mode */
+	GM_GPCR_TX_ENA		= 1<<12, /* Bit 12:	Enable Transmit */
+	GM_GPCR_RX_ENA		= 1<<11, /* Bit 11:	Enable Receive */
+	GM_GPCR_BURST_ENA	= 1<<10, /* Bit 10:	Enable Burst Mode */
+	GM_GPCR_LOOP_ENA	= 1<<9,	/* Bit  9:	Enable MAC Loopback Mode */
+	GM_GPCR_PART_ENA	= 1<<8,	/* Bit  8:	Enable Partition Mode */
+	GM_GPCR_GIGS_ENA	= 1<<7,	/* Bit  7:	Gigabit Speed (1000 Mbps) */
+	GM_GPCR_FL_PASS		= 1<<6,	/* Bit  6:	Force Link Pass */
+	GM_GPCR_DUP_FULL	= 1<<5,	/* Bit  5:	Full Duplex Mode */
+	GM_GPCR_FC_RX_DIS	= 1<<4,	/* Bit  4:	Disable Rx Flow-Control Mode */
+	GM_GPCR_SPEED_100	= 1<<3,   /* Bit  3:	Port Speed 100 Mbps */
+	GM_GPCR_AU_DUP_DIS	= 1<<2,	/* Bit  2:	Disable Auto-Update Duplex */
+	GM_GPCR_AU_FCT_DIS	= 1<<1,	/* Bit  1:	Disable Auto-Update Flow-C. */
+	GM_GPCR_AU_SPD_DIS	= 1<<0,	/* Bit  0:	Disable Auto-Update Speed */
+};
+
+#define GM_GPCR_SPEED_1000	(GM_GPCR_GIGS_ENA | GM_GPCR_SPEED_100)
+#define GM_GPCR_AU_ALL_DIS	(GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS|GM_GPCR_AU_SPD_DIS)
+
+/*	GM_TX_CTRL			16 bit r/w	Transmit Control Register */
+enum {
+	GM_TXCR_FORCE_JAM	= 1<<15, /* Bit 15:	Force Jam / Flow-Control */
+	GM_TXCR_CRC_DIS		= 1<<14, /* Bit 14:	Disable insertion of CRC */
+	GM_TXCR_PAD_DIS		= 1<<13, /* Bit 13:	Disable padding of packets */
+	GM_TXCR_COL_THR_MSK	= 7<<10, /* Bit 12..10:	Collision Threshold */
+};
+
+#define TX_COL_THR(x)		(((x)<<10) & GM_TXCR_COL_THR_MSK)
+#define TX_COL_DEF		0x04	/* late collision after 64 byte */
+
+/*	GM_RX_CTRL			16 bit r/w	Receive Control Register */
+enum {
+	GM_RXCR_UCF_ENA	= 1<<15, /* Bit 15:	Enable Unicast filtering */
+	GM_RXCR_MCF_ENA	= 1<<14, /* Bit 14:	Enable Multicast filtering */
+	GM_RXCR_CRC_DIS	= 1<<13, /* Bit 13:	Remove 4-byte CRC */
+	GM_RXCR_PASS_FC	= 1<<12, /* Bit 12:	Pass FC packets to FIFO */
+};
+
+/*	GM_TX_PARAM		16 bit r/w	Transmit Parameter Register */
+enum {
+	GM_TXPA_JAMLEN_MSK	= 0x03<<14,	/* Bit 15..14:	Jam Length */
+	GM_TXPA_JAMIPG_MSK	= 0x1f<<9,	/* Bit 13..9:	Jam IPG */
+	GM_TXPA_JAMDAT_MSK	= 0x1f<<4,	/* Bit  8..4:	IPG Jam to Data */
+
+	TX_JAM_LEN_DEF		= 0x03,
+	TX_JAM_IPG_DEF		= 0x0b,
+	TX_IPG_JAM_DEF		= 0x1c,
+};
+
+#define TX_JAM_LEN_VAL(x)	(((x)<<14) & GM_TXPA_JAMLEN_MSK)
+#define TX_JAM_IPG_VAL(x)	(((x)<<9)  & GM_TXPA_JAMIPG_MSK)
+#define TX_IPG_JAM_DATA(x)	(((x)<<4)  & GM_TXPA_JAMDAT_MSK)
+
+
+/*	GM_SERIAL_MODE			16 bit r/w	Serial Mode Register */
+enum {
+	GM_SMOD_DATABL_MSK	= 0x1f<<11, /* Bit 15..11:	Data Blinder (r/o) */
+	GM_SMOD_LIMIT_4		= 1<<10, /* Bit 10:	4 consecutive Tx trials */
+	GM_SMOD_VLAN_ENA	= 1<<9,	/* Bit  9:	Enable VLAN  (Max. Frame Len) */
+	GM_SMOD_JUMBO_ENA	= 1<<8,	/* Bit  8:	Enable Jumbo (Max. Frame Len) */
+	 GM_SMOD_IPG_MSK	= 0x1f	/* Bit 4..0:	Inter-Packet Gap (IPG) */
+};
+
+#define DATA_BLIND_VAL(x)	(((x)<<11) & GM_SMOD_DATABL_MSK)
+#define DATA_BLIND_DEF		0x04
+
+#define IPG_DATA_VAL(x)		(x & GM_SMOD_IPG_MSK)
+#define IPG_DATA_DEF		0x1e
+
+/*	GM_SMI_CTRL			16 bit r/w	SMI Control Register */
+enum {
+	GM_SMI_CT_PHY_A_MSK	= 0x1f<<11, /* Bit 15..11:	PHY Device Address */
+	GM_SMI_CT_REG_A_MSK	= 0x1f<<6, /* Bit 10.. 6:	PHY Register Address */
+	GM_SMI_CT_OP_RD		= 1<<5,	/* Bit  5:	OpCode Read (0=Write)*/
+	GM_SMI_CT_RD_VAL	= 1<<4,	/* Bit  4:	Read Valid (Read completed) */
+	GM_SMI_CT_BUSY		= 1<<3,	/* Bit  3:	Busy (Operation in progress) */
+};
+
+#define GM_SMI_CT_PHY_AD(x)	(((x)<<11) & GM_SMI_CT_PHY_A_MSK)
+#define GM_SMI_CT_REG_AD(x)	(((x)<<6) & GM_SMI_CT_REG_A_MSK)
+
+/*	GM_PHY_ADDR				16 bit r/w	GPHY Address Register */
+enum {
+	GM_PAR_MIB_CLR	= 1<<5,	/* Bit  5:	Set MIB Clear Counter Mode */
+	GM_PAR_MIB_TST	= 1<<4,	/* Bit  4:	MIB Load Counter (Test Mode) */
+};
+
+/* Receive Frame Status Encoding */
+enum {
+	GMR_FS_LEN	= 0xffff<<16, /* Bit 31..16:	Rx Frame Length */
+	GMR_FS_LEN_SHIFT = 16,
+	GMR_FS_VLAN	= 1<<13, /* Bit 13:	VLAN Packet */
+	GMR_FS_JABBER	= 1<<12, /* Bit 12:	Jabber Packet */
+	GMR_FS_UN_SIZE	= 1<<11, /* Bit 11:	Undersize Packet */
+	GMR_FS_MC	= 1<<10, /* Bit 10:	Multicast Packet */
+	GMR_FS_BC	= 1<<9, /* Bit  9:	Broadcast Packet */
+	GMR_FS_RX_OK	= 1<<8, /* Bit  8:	Receive OK (Good Packet) */
+	GMR_FS_GOOD_FC	= 1<<7, /* Bit  7:	Good Flow-Control Packet */
+	GMR_FS_BAD_FC	= 1<<6, /* Bit  6:	Bad  Flow-Control Packet */
+	GMR_FS_MII_ERR	= 1<<5, /* Bit  5:	MII Error */
+	GMR_FS_LONG_ERR	= 1<<4, /* Bit  4:	Too Long Packet */
+	GMR_FS_FRAGMENT	= 1<<3, /* Bit  3:	Fragment */
+
+	GMR_FS_CRC_ERR	= 1<<1, /* Bit  1:	CRC Error */
+	GMR_FS_RX_FF_OV	= 1<<0, /* Bit  0:	Rx FIFO Overflow */
+
+/*
+ * GMR_FS_ANY_ERR (analogous to XMR_FS_ANY_ERR)
+ */
+	GMR_FS_ANY_ERR	= GMR_FS_CRC_ERR | GMR_FS_LONG_ERR |
+			  GMR_FS_MII_ERR | GMR_FS_BAD_FC | GMR_FS_GOOD_FC |
+			  GMR_FS_JABBER,
+/* Rx GMAC FIFO Flush Mask (default) */
+	RX_FF_FL_DEF_MSK = GMR_FS_CRC_ERR | GMR_FS_RX_FF_OV |GMR_FS_MII_ERR |
+			   GMR_FS_BAD_FC |  GMR_FS_UN_SIZE | GMR_FS_JABBER,
+};
+
+/*	RX_GMF_CTRL_T	32 bit	Rx GMAC FIFO Control/Test */
+enum {
+	GMF_WP_TST_ON	= 1<<14,	/* Write Pointer Test On */
+	GMF_WP_TST_OFF	= 1<<13,	/* Write Pointer Test Off */
+	GMF_WP_STEP	= 1<<12,	/* Write Pointer Step/Increment */
+
+	GMF_RP_TST_ON	= 1<<10,	/* Read Pointer Test On */
+	GMF_RP_TST_OFF	= 1<<9,		/* Read Pointer Test Off */
+	GMF_RP_STEP	= 1<<8,		/* Read Pointer Step/Increment */
+	GMF_RX_F_FL_ON	= 1<<7,		/* Rx FIFO Flush Mode On */
+	GMF_RX_F_FL_OFF	= 1<<6,		/* Rx FIFO Flush Mode Off */
+	GMF_CLI_RX_FO	= 1<<5,		/* Clear IRQ Rx FIFO Overrun */
+	GMF_CLI_RX_FC	= 1<<4,		/* Clear IRQ Rx Frame Complete */
+	GMF_OPER_ON	= 1<<3,		/* Operational Mode On */
+	GMF_OPER_OFF	= 1<<2,		/* Operational Mode Off */
+	GMF_RST_CLR	= 1<<1,		/* Clear GMAC FIFO Reset */
+	GMF_RST_SET	= 1<<0,		/* Set   GMAC FIFO Reset */
+
+	RX_GMF_FL_THR_DEF = 0xa,	/* flush threshold (default) */
+};
+
+
+/*	TX_GMF_CTRL_T	32 bit	Tx GMAC FIFO Control/Test */
+enum {
+	GMF_WSP_TST_ON	= 1<<18, /* Write Shadow Pointer Test On */
+	GMF_WSP_TST_OFF	= 1<<17, /* Write Shadow Pointer Test Off */
+	GMF_WSP_STEP	= 1<<16, /* Write Shadow Pointer Step/Increment */
+
+	GMF_CLI_TX_FU	= 1<<6,	/* Clear IRQ Tx FIFO Underrun */
+	GMF_CLI_TX_FC	= 1<<5,	/* Clear IRQ Tx Frame Complete */
+	GMF_CLI_TX_PE	= 1<<4,	/* Clear IRQ Tx Parity Error */
+};
+
+/*	GMAC_TI_ST_CTRL	 8 bit	Time Stamp Timer Ctrl Reg (YUKON only) */
+enum {
+	GMT_ST_START	= 1<<2,	/* Start Time Stamp Timer */
+	GMT_ST_STOP	= 1<<1,	/* Stop  Time Stamp Timer */
+	GMT_ST_CLR_IRQ	= 1<<0,	/* Clear Time Stamp Timer IRQ */
+};
+
+/*	GMAC_CTRL		32 bit	GMAC Control Reg (YUKON only) */
+enum {
+	GMC_H_BURST_ON	= 1<<7,	/* Half Duplex Burst Mode On */
+	GMC_H_BURST_OFF	= 1<<6,	/* Half Duplex Burst Mode Off */
+	GMC_F_LOOPB_ON	= 1<<5,	/* FIFO Loopback On */
+	GMC_F_LOOPB_OFF	= 1<<4,	/* FIFO Loopback Off */
+	GMC_PAUSE_ON	= 1<<3,	/* Pause On */
+	GMC_PAUSE_OFF	= 1<<2,	/* Pause Off */
+	GMC_RST_CLR	= 1<<1,	/* Clear GMAC Reset */
+	GMC_RST_SET	= 1<<0,	/* Set   GMAC Reset */
+};
+
+/*	GPHY_CTRL		32 bit	GPHY Control Reg (YUKON only) */
+enum {
+	GPC_SEL_BDT	= 1<<28, /* Select Bi-Dir. Transfer for MDC/MDIO */
+	GPC_INT_POL_HI	= 1<<27, /* IRQ Polarity is Active HIGH */
+	GPC_75_OHM	= 1<<26, /* Use 75 Ohm Termination instead of 50 */
+	GPC_DIS_FC	= 1<<25, /* Disable Automatic Fiber/Copper Detection */
+	GPC_DIS_SLEEP	= 1<<24, /* Disable Energy Detect */
+	GPC_HWCFG_M_3	= 1<<23, /* HWCFG_MODE[3] */
+	GPC_HWCFG_M_2	= 1<<22, /* HWCFG_MODE[2] */
+	GPC_HWCFG_M_1	= 1<<21, /* HWCFG_MODE[1] */
+	GPC_HWCFG_M_0	= 1<<20, /* HWCFG_MODE[0] */
+	GPC_ANEG_0	= 1<<19, /* ANEG[0] */
+	GPC_ENA_XC	= 1<<18, /* Enable MDI crossover */
+	GPC_DIS_125	= 1<<17, /* Disable 125 MHz clock */
+	GPC_ANEG_3	= 1<<16, /* ANEG[3] */
+	GPC_ANEG_2	= 1<<15, /* ANEG[2] */
+	GPC_ANEG_1	= 1<<14, /* ANEG[1] */
+	GPC_ENA_PAUSE	= 1<<13, /* Enable Pause (SYM_OR_REM) */
+	GPC_PHYADDR_4	= 1<<12, /* Bit 4 of Phy Addr */
+	GPC_PHYADDR_3	= 1<<11, /* Bit 3 of Phy Addr */
+	GPC_PHYADDR_2	= 1<<10, /* Bit 2 of Phy Addr */
+	GPC_PHYADDR_1	= 1<<9,	 /* Bit 1 of Phy Addr */
+	GPC_PHYADDR_0	= 1<<8,	 /* Bit 0 of Phy Addr */
+						/* Bits  7..2:	reserved */
+	GPC_RST_CLR	= 1<<1,	/* Clear GPHY Reset */
+	GPC_RST_SET	= 1<<0,	/* Set   GPHY Reset */
+};
+
+#define GPC_HWCFG_GMII_COP (GPC_HWCFG_M_3|GPC_HWCFG_M_2 | GPC_HWCFG_M_1 | GPC_HWCFG_M_0)
+#define GPC_HWCFG_GMII_FIB (GPC_HWCFG_M_2 | GPC_HWCFG_M_1 | GPC_HWCFG_M_0)
+#define GPC_ANEG_ADV_ALL_M  (GPC_ANEG_3 | GPC_ANEG_2 | GPC_ANEG_1 | GPC_ANEG_0)
+
+/* forced speed and duplex mode (don't mix with other ANEG bits) */
+#define GPC_FRC10MBIT_HALF	0
+#define GPC_FRC10MBIT_FULL	GPC_ANEG_0
+#define GPC_FRC100MBIT_HALF	GPC_ANEG_1
+#define GPC_FRC100MBIT_FULL	(GPC_ANEG_0 | GPC_ANEG_1)
+
+/* auto-negotiation with limited advertised speeds */
+/* mix only with master/slave settings (for copper) */
+#define GPC_ADV_1000_HALF	GPC_ANEG_2
+#define GPC_ADV_1000_FULL	GPC_ANEG_3
+#define GPC_ADV_ALL		(GPC_ANEG_2 | GPC_ANEG_3)
+
+/* master/slave settings */
+/* only for copper with 1000 Mbps */
+#define GPC_FORCE_MASTER	0
+#define GPC_FORCE_SLAVE		GPC_ANEG_0
+#define GPC_PREF_MASTER		GPC_ANEG_1
+#define GPC_PREF_SLAVE		(GPC_ANEG_1 | GPC_ANEG_0)
+
+/*	GMAC_IRQ_SRC	 8 bit	GMAC Interrupt Source Reg (YUKON only) */
+/*	GMAC_IRQ_MSK	 8 bit	GMAC Interrupt Mask   Reg (YUKON only) */
+enum {
+	GM_IS_TX_CO_OV	= 1<<5,	/* Transmit Counter Overflow IRQ */
+	GM_IS_RX_CO_OV	= 1<<4,	/* Receive Counter Overflow IRQ */
+	GM_IS_TX_FF_UR	= 1<<3,	/* Transmit FIFO Underrun */
+	GM_IS_TX_COMPL	= 1<<2,	/* Frame Transmission Complete */
+	GM_IS_RX_FF_OR	= 1<<1,	/* Receive FIFO Overrun */
+	GM_IS_RX_COMPL	= 1<<0,	/* Frame Reception Complete */
+
+#define GMAC_DEF_MSK	(GM_IS_RX_FF_OR | GM_IS_TX_FF_UR)
+
+/*	GMAC_LINK_CTRL	16 bit	GMAC Link Control Reg (YUKON only) */
+						/* Bits 15.. 2:	reserved */
+	GMLC_RST_CLR	= 1<<1,	/* Clear GMAC Link Reset */
+	GMLC_RST_SET	= 1<<0,	/* Set   GMAC Link Reset */
+
+
+/*	WOL_CTRL_STAT	16 bit	WOL Control/Status Reg */
+	WOL_CTL_LINK_CHG_OCC		= 1<<15,
+	WOL_CTL_MAGIC_PKT_OCC		= 1<<14,
+	WOL_CTL_PATTERN_OCC		= 1<<13,
+	WOL_CTL_CLEAR_RESULT		= 1<<12,
+	WOL_CTL_ENA_PME_ON_LINK_CHG	= 1<<11,
+	WOL_CTL_DIS_PME_ON_LINK_CHG	= 1<<10,
+	WOL_CTL_ENA_PME_ON_MAGIC_PKT	= 1<<9,
+	WOL_CTL_DIS_PME_ON_MAGIC_PKT	= 1<<8,
+	WOL_CTL_ENA_PME_ON_PATTERN	= 1<<7,
+	WOL_CTL_DIS_PME_ON_PATTERN	= 1<<6,
+	WOL_CTL_ENA_LINK_CHG_UNIT	= 1<<5,
+	WOL_CTL_DIS_LINK_CHG_UNIT	= 1<<4,
+	WOL_CTL_ENA_MAGIC_PKT_UNIT	= 1<<3,
+	WOL_CTL_DIS_MAGIC_PKT_UNIT	= 1<<2,
+	WOL_CTL_ENA_PATTERN_UNIT	= 1<<1,
+	WOL_CTL_DIS_PATTERN_UNIT	= 1<<0,
+};
+
+#define WOL_CTL_DEFAULT				\
+	(WOL_CTL_DIS_PME_ON_LINK_CHG |	\
+	WOL_CTL_DIS_PME_ON_PATTERN |	\
+	WOL_CTL_DIS_PME_ON_MAGIC_PKT |	\
+	WOL_CTL_DIS_LINK_CHG_UNIT |		\
+	WOL_CTL_DIS_PATTERN_UNIT |		\
+	WOL_CTL_DIS_MAGIC_PKT_UNIT)
+
+/*	WOL_MATCH_CTL	 8 bit	WOL Match Control Reg */
+#define WOL_CTL_PATT_ENA(x)	(1 << (x))
+
+
+/* XMAC II registers				      */
+enum {
+	XM_MMU_CMD	= 0x0000, /* 16 bit r/w	MMU Command Register */
+	XM_POFF		= 0x0008, /* 32 bit r/w	Packet Offset Register */
+	XM_BURST	= 0x000c, /* 32 bit r/w	Burst Register for half duplex*/
+	XM_1L_VLAN_TAG	= 0x0010, /* 16 bit r/w	One Level VLAN Tag ID */
+	XM_2L_VLAN_TAG	= 0x0014, /* 16 bit r/w	Two Level VLAN Tag ID */
+	XM_TX_CMD	= 0x0020, /* 16 bit r/w	Transmit Command Register */
+	XM_TX_RT_LIM	= 0x0024, /* 16 bit r/w	Transmit Retry Limit Register */
+	XM_TX_STIME	= 0x0028, /* 16 bit r/w	Transmit Slottime Register */
+	XM_TX_IPG	= 0x002c, /* 16 bit r/w	Transmit Inter Packet Gap */
+	XM_RX_CMD	= 0x0030, /* 16 bit r/w	Receive Command Register */
+	XM_PHY_ADDR	= 0x0034, /* 16 bit r/w	PHY Address Register */
+	XM_PHY_DATA	= 0x0038, /* 16 bit r/w	PHY Data Register */
+	XM_GP_PORT	= 0x0040, /* 32 bit r/w	General Purpose Port Register */
+	XM_IMSK		= 0x0044, /* 16 bit r/w	Interrupt Mask Register */
+	XM_ISRC		= 0x0048, /* 16 bit r/o	Interrupt Status Register */
+	XM_HW_CFG	= 0x004c, /* 16 bit r/w	Hardware Config Register */
+	XM_TX_LO_WM	= 0x0060, /* 16 bit r/w	Tx FIFO Low Water Mark */
+	XM_TX_HI_WM	= 0x0062, /* 16 bit r/w	Tx FIFO High Water Mark */
+	XM_TX_THR	= 0x0064, /* 16 bit r/w	Tx Request Threshold */
+	XM_HT_THR	= 0x0066, /* 16 bit r/w	Host Request Threshold */
+	XM_PAUSE_DA	= 0x0068, /* NA reg r/w	Pause Destination Address */
+	XM_CTL_PARA	= 0x0070, /* 32 bit r/w	Control Parameter Register */
+	XM_MAC_OPCODE	= 0x0074, /* 16 bit r/w	Opcode for MAC control frames */
+	XM_MAC_PTIME	= 0x0076, /* 16 bit r/w	Pause time for MAC ctrl frames*/
+	XM_TX_STAT	= 0x0078, /* 32 bit r/o	Tx Status LIFO Register */
+
+	XM_EXM_START	= 0x0080, /* r/w	Start Address of the EXM Regs */
+#define XM_EXM(reg)	(XM_EXM_START + ((reg) << 3))
+};
+
+enum {
+	XM_SRC_CHK	= 0x0100, /* NA reg r/w	Source Check Address Register */
+	XM_SA		= 0x0108, /* NA reg r/w	Station Address Register */
+	XM_HSM		= 0x0110, /* 64 bit r/w	Hash Match Address Registers */
+	XM_RX_LO_WM	= 0x0118, /* 16 bit r/w	Receive Low Water Mark */
+	XM_RX_HI_WM	= 0x011a, /* 16 bit r/w	Receive High Water Mark */
+	XM_RX_THR	= 0x011c, /* 32 bit r/w	Receive Request Threshold */
+	XM_DEV_ID	= 0x0120, /* 32 bit r/o	Device ID Register */
+	XM_MODE		= 0x0124, /* 32 bit r/w	Mode Register */
+	XM_LSA		= 0x0128, /* NA reg r/o	Last Source Register */
+	XM_TS_READ	= 0x0130, /* 32 bit r/o	Time Stamp Read Register */
+	XM_TS_LOAD	= 0x0134, /* 32 bit r/o	Time Stamp Load Value */
+	XM_STAT_CMD	= 0x0200, /* 16 bit r/w	Statistics Command Register */
+	XM_RX_CNT_EV	= 0x0204, /* 32 bit r/o	Rx Counter Event Register */
+	XM_TX_CNT_EV	= 0x0208, /* 32 bit r/o	Tx Counter Event Register */
+	XM_RX_EV_MSK	= 0x020c, /* 32 bit r/w	Rx Counter Event Mask */
+	XM_TX_EV_MSK	= 0x0210, /* 32 bit r/w	Tx Counter Event Mask */
+	XM_TXF_OK	= 0x0280, /* 32 bit r/o	Frames Transmitted OK Conuter */
+	XM_TXO_OK_HI	= 0x0284, /* 32 bit r/o	Octets Transmitted OK High Cnt*/
+	XM_TXO_OK_LO	= 0x0288, /* 32 bit r/o	Octets Transmitted OK Low Cnt */
+	XM_TXF_BC_OK	= 0x028c, /* 32 bit r/o	Broadcast Frames Xmitted OK */
+	XM_TXF_MC_OK	= 0x0290, /* 32 bit r/o	Multicast Frames Xmitted OK */
+	XM_TXF_UC_OK	= 0x0294, /* 32 bit r/o	Unicast Frames Xmitted OK */
+	XM_TXF_LONG	= 0x0298, /* 32 bit r/o	Tx Long Frame Counter */
+	XM_TXE_BURST	= 0x029c, /* 32 bit r/o	Tx Burst Event Counter */
+	XM_TXF_MPAUSE	= 0x02a0, /* 32 bit r/o	Tx Pause MAC Ctrl Frame Cnt */
+	XM_TXF_MCTRL	= 0x02a4, /* 32 bit r/o	Tx MAC Ctrl Frame Counter */
+	XM_TXF_SNG_COL	= 0x02a8, /* 32 bit r/o	Tx Single Collision Counter */
+	XM_TXF_MUL_COL	= 0x02ac, /* 32 bit r/o	Tx Multiple Collision Counter */
+	XM_TXF_ABO_COL	= 0x02b0, /* 32 bit r/o	Tx aborted due to Exces. Col. */
+	XM_TXF_LAT_COL	= 0x02b4, /* 32 bit r/o	Tx Late Collision Counter */
+	XM_TXF_DEF	= 0x02b8, /* 32 bit r/o	Tx Deferred Frame Counter */
+	XM_TXF_EX_DEF	= 0x02bc, /* 32 bit r/o	Tx Excessive Deferall Counter */
+	XM_TXE_FIFO_UR	= 0x02c0, /* 32 bit r/o	Tx FIFO Underrun Event Cnt */
+	XM_TXE_CS_ERR	= 0x02c4, /* 32 bit r/o	Tx Carrier Sense Error Cnt */
+	XM_TXP_UTIL	= 0x02c8, /* 32 bit r/o	Tx Utilization in % */
+	XM_TXF_64B	= 0x02d0, /* 32 bit r/o	64 Byte Tx Frame Counter */
+	XM_TXF_127B	= 0x02d4, /* 32 bit r/o	65-127 Byte Tx Frame Counter */
+	XM_TXF_255B	= 0x02d8, /* 32 bit r/o	128-255 Byte Tx Frame Counter */
+	XM_TXF_511B	= 0x02dc, /* 32 bit r/o	256-511 Byte Tx Frame Counter */
+	XM_TXF_1023B	= 0x02e0, /* 32 bit r/o	512-1023 Byte Tx Frame Counter*/
+	XM_TXF_MAX_SZ	= 0x02e4, /* 32 bit r/o	1024-MaxSize Byte Tx Frame Cnt*/
+	XM_RXF_OK	= 0x0300, /* 32 bit r/o	Frames Received OK */
+	XM_RXO_OK_HI	= 0x0304, /* 32 bit r/o	Octets Received OK High Cnt */
+	XM_RXO_OK_LO	= 0x0308, /* 32 bit r/o	Octets Received OK Low Counter*/
+	XM_RXF_BC_OK	= 0x030c, /* 32 bit r/o	Broadcast Frames Received OK */
+	XM_RXF_MC_OK	= 0x0310, /* 32 bit r/o	Multicast Frames Received OK */
+	XM_RXF_UC_OK	= 0x0314, /* 32 bit r/o	Unicast Frames Received OK */
+	XM_RXF_MPAUSE	= 0x0318, /* 32 bit r/o	Rx Pause MAC Ctrl Frame Cnt */
+	XM_RXF_MCTRL	= 0x031c, /* 32 bit r/o	Rx MAC Ctrl Frame Counter */
+	XM_RXF_INV_MP	= 0x0320, /* 32 bit r/o	Rx invalid Pause Frame Cnt */
+	XM_RXF_INV_MOC	= 0x0324, /* 32 bit r/o	Rx Frames with inv. MAC Opcode*/
+	XM_RXE_BURST	= 0x0328, /* 32 bit r/o	Rx Burst Event Counter */
+	XM_RXE_FMISS	= 0x032c, /* 32 bit r/o	Rx Missed Frames Event Cnt */
+	XM_RXF_FRA_ERR	= 0x0330, /* 32 bit r/o	Rx Framing Error Counter */
+	XM_RXE_FIFO_OV	= 0x0334, /* 32 bit r/o	Rx FIFO overflow Event Cnt */
+	XM_RXF_JAB_PKT	= 0x0338, /* 32 bit r/o	Rx Jabber Packet Frame Cnt */
+	XM_RXE_CAR_ERR	= 0x033c, /* 32 bit r/o	Rx Carrier Event Error Cnt */
+	XM_RXF_LEN_ERR	= 0x0340, /* 32 bit r/o	Rx in Range Length Error */
+	XM_RXE_SYM_ERR	= 0x0344, /* 32 bit r/o	Rx Symbol Error Counter */
+	XM_RXE_SHT_ERR	= 0x0348, /* 32 bit r/o	Rx Short Event Error Cnt */
+	XM_RXE_RUNT	= 0x034c, /* 32 bit r/o	Rx Runt Event Counter */
+	XM_RXF_LNG_ERR	= 0x0350, /* 32 bit r/o	Rx Frame too Long Error Cnt */
+	XM_RXF_FCS_ERR	= 0x0354, /* 32 bit r/o	Rx Frame Check Seq. Error Cnt */
+	XM_RXF_CEX_ERR	= 0x035c, /* 32 bit r/o	Rx Carrier Ext Error Frame Cnt*/
+	XM_RXP_UTIL	= 0x0360, /* 32 bit r/o	Rx Utilization in % */
+	XM_RXF_64B	= 0x0368, /* 32 bit r/o	64 Byte Rx Frame Counter */
+	XM_RXF_127B	= 0x036c, /* 32 bit r/o	65-127 Byte Rx Frame Counter */
+	XM_RXF_255B	= 0x0370, /* 32 bit r/o	128-255 Byte Rx Frame Counter */
+	XM_RXF_511B	= 0x0374, /* 32 bit r/o	256-511 Byte Rx Frame Counter */
+	XM_RXF_1023B	= 0x0378, /* 32 bit r/o	512-1023 Byte Rx Frame Counter*/
+	XM_RXF_MAX_SZ	= 0x037c, /* 32 bit r/o	1024-MaxSize Byte Rx Frame Cnt*/
+};
+
+/*	XM_MMU_CMD	16 bit r/w	MMU Command Register */
+enum {
+	XM_MMU_PHY_RDY	= 1<<12, /* Bit 12:	PHY Read Ready */
+	XM_MMU_PHY_BUSY	= 1<<11, /* Bit 11:	PHY Busy */
+	XM_MMU_IGN_PF	= 1<<10, /* Bit 10:	Ignore Pause Frame */
+	XM_MMU_MAC_LB	= 1<<9,	 /* Bit  9:	Enable MAC Loopback */
+	XM_MMU_FRC_COL	= 1<<7,	 /* Bit  7:	Force Collision */
+	XM_MMU_SIM_COL	= 1<<6,	 /* Bit  6:	Simulate Collision */
+	XM_MMU_NO_PRE	= 1<<5,	 /* Bit  5:	No MDIO Preamble */
+	XM_MMU_GMII_FD	= 1<<4,	 /* Bit  4:	GMII uses Full Duplex */
+	XM_MMU_RAT_CTRL	= 1<<3,	 /* Bit  3:	Enable Rate Control */
+	XM_MMU_GMII_LOOP= 1<<2,	 /* Bit  2:	PHY is in Loopback Mode */
+	XM_MMU_ENA_RX	= 1<<1,	 /* Bit  1:	Enable Receiver */
+	XM_MMU_ENA_TX	= 1<<0,	 /* Bit  0:	Enable Transmitter */
+};
+
+
+/*	XM_TX_CMD	16 bit r/w	Transmit Command Register */
+enum {
+	XM_TX_BK2BK	= 1<<6,	/* Bit  6:	Ignor Carrier Sense (Tx Bk2Bk)*/
+	XM_TX_ENC_BYP	= 1<<5,	/* Bit  5:	Set Encoder in Bypass Mode */
+	XM_TX_SAM_LINE	= 1<<4,	/* Bit  4: (sc)	Start utilization calculation */
+	XM_TX_NO_GIG_MD	= 1<<3,	/* Bit  3:	Disable Carrier Extension */
+	XM_TX_NO_PRE	= 1<<2,	/* Bit  2:	Disable Preamble Generation */
+	XM_TX_NO_CRC	= 1<<1,	/* Bit  1:	Disable CRC Generation */
+	XM_TX_AUTO_PAD	= 1<<0,	/* Bit  0:	Enable Automatic Padding */
+};
+
+/*	XM_TX_RT_LIM	16 bit r/w	Transmit Retry Limit Register */
+#define XM_RT_LIM_MSK	0x1f	/* Bit  4..0:	Tx Retry Limit */
+
+
+/*	XM_TX_STIME	16 bit r/w	Transmit Slottime Register */
+#define XM_STIME_MSK	0x7f	/* Bit  6..0:	Tx Slottime bits */
+
+
+/*	XM_TX_IPG	16 bit r/w	Transmit Inter Packet Gap */
+#define XM_IPG_MSK		0xff	/* Bit  7..0:	IPG value bits */
+
+
+/*	XM_RX_CMD	16 bit r/w	Receive Command Register */
+enum {
+	XM_RX_LENERR_OK	= 1<<8,	/* Bit  8	don't set Rx Err bit for */
+				/*		inrange error packets */
+	XM_RX_BIG_PK_OK	= 1<<7,	/* Bit  7	don't set Rx Err bit for */
+				/*		jumbo packets */
+	XM_RX_IPG_CAP	= 1<<6,	/* Bit  6	repl. type field with IPG */
+	XM_RX_TP_MD	= 1<<5,	/* Bit  5:	Enable transparent Mode */
+	XM_RX_STRIP_FCS	= 1<<4,	/* Bit  4:	Enable FCS Stripping */
+	XM_RX_SELF_RX	= 1<<3,	/* Bit  3: 	Enable Rx of own packets */
+	XM_RX_SAM_LINE	= 1<<2,	/* Bit  2: (sc)	Start utilization calculation */
+	XM_RX_STRIP_PAD	= 1<<1,	/* Bit  1:	Strip pad bytes of Rx frames */
+	XM_RX_DIS_CEXT	= 1<<0,	/* Bit  0:	Disable carrier ext. check */
+};
+
+
+/*	XM_GP_PORT	32 bit r/w	General Purpose Port Register */
+enum {
+	XM_GP_ANIP	= 1<<6,	/* Bit  6: (ro)	Auto-Neg. in progress */
+	XM_GP_FRC_INT	= 1<<5,	/* Bit  5: (sc)	Force Interrupt */
+	XM_GP_RES_MAC	= 1<<3,	/* Bit  3: (sc)	Reset MAC and FIFOs */
+	XM_GP_RES_STAT	= 1<<2,	/* Bit  2: (sc)	Reset the statistics module */
+	XM_GP_INP_ASS	= 1<<0,	/* Bit  0: (ro) GP Input Pin asserted */
+};
+
+
+/*	XM_IMSK		16 bit r/w	Interrupt Mask Register */
+/*	XM_ISRC		16 bit r/o	Interrupt Status Register */
+enum {
+	XM_IS_LNK_AE	= 1<<14, /* Bit 14:	Link Asynchronous Event */
+	XM_IS_TX_ABORT	= 1<<13, /* Bit 13:	Transmit Abort, late Col. etc */
+	XM_IS_FRC_INT	= 1<<12, /* Bit 12:	Force INT bit set in GP */
+	XM_IS_INP_ASS	= 1<<11, /* Bit 11:	Input Asserted, GP bit 0 set */
+	XM_IS_LIPA_RC	= 1<<10, /* Bit 10:	Link Partner requests config */
+	XM_IS_RX_PAGE	= 1<<9,	/* Bit  9:	Page Received */
+	XM_IS_TX_PAGE	= 1<<8,	/* Bit  8:	Next Page Loaded for Transmit */
+	XM_IS_AND	= 1<<7,	/* Bit  7:	Auto-Negotiation Done */
+	XM_IS_TSC_OV	= 1<<6,	/* Bit  6:	Time Stamp Counter Overflow */
+	XM_IS_RXC_OV	= 1<<5,	/* Bit  5:	Rx Counter Event Overflow */
+	XM_IS_TXC_OV	= 1<<4,	/* Bit  4:	Tx Counter Event Overflow */
+	XM_IS_RXF_OV	= 1<<3,	/* Bit  3:	Receive FIFO Overflow */
+	XM_IS_TXF_UR	= 1<<2,	/* Bit  2:	Transmit FIFO Underrun */
+	XM_IS_TX_COMP	= 1<<1,	/* Bit  1:	Frame Tx Complete */
+	XM_IS_RX_COMP	= 1<<0,	/* Bit  0:	Frame Rx Complete */
+
+	XM_IMSK_DISABLE	= 0xffff,
+};
+
+/*	XM_HW_CFG	16 bit r/w	Hardware Config Register */
+enum {
+	XM_HW_GEN_EOP	= 1<<3,	/* Bit  3:	generate End of Packet pulse */
+	XM_HW_COM4SIG	= 1<<2,	/* Bit  2:	use Comma Detect for Sig. Det.*/
+	XM_HW_GMII_MD	= 1<<0,	/* Bit  0:	GMII Interface selected */
+};
+
+
+/*	XM_TX_LO_WM	16 bit r/w	Tx FIFO Low Water Mark */
+/*	XM_TX_HI_WM	16 bit r/w	Tx FIFO High Water Mark */
+#define XM_TX_WM_MSK	0x01ff	/* Bit  9.. 0	Tx FIFO Watermark bits */
+
+/*	XM_TX_THR	16 bit r/w	Tx Request Threshold */
+/*	XM_HT_THR	16 bit r/w	Host Request Threshold */
+/*	XM_RX_THR	16 bit r/w	Rx Request Threshold */
+#define XM_THR_MSK		0x03ff	/* Bit 10.. 0	Rx/Tx Request Threshold bits */
+
+
+/*	XM_TX_STAT	32 bit r/o	Tx Status LIFO Register */
+enum {
+	XM_ST_VALID	= (1UL<<31),	/* Bit 31:	Status Valid */
+	XM_ST_BYTE_CNT	= (0x3fffL<<17),	/* Bit 30..17:	Tx frame Length */
+	XM_ST_RETRY_CNT	= (0x1fL<<12),	/* Bit 16..12:	Retry Count */
+	XM_ST_EX_COL	= 1<<11,	/* Bit 11:	Excessive Collisions */
+	XM_ST_EX_DEF	= 1<<10,	/* Bit 10:	Excessive Deferral */
+	XM_ST_BURST	= 1<<9,		/* Bit  9:	p. xmitted in burst md*/
+	XM_ST_DEFER	= 1<<8,		/* Bit  8:	packet was defered */
+	XM_ST_BC	= 1<<7,		/* Bit  7:	Broadcast packet */
+	XM_ST_MC	= 1<<6,		/* Bit  6:	Multicast packet */
+	XM_ST_UC	= 1<<5,		/* Bit  5:	Unicast packet */
+	XM_ST_TX_UR	= 1<<4,		/* Bit  4:	FIFO Underrun occured */
+	XM_ST_CS_ERR	= 1<<3,		/* Bit  3:	Carrier Sense Error */
+	XM_ST_LAT_COL	= 1<<2,		/* Bit  2:	Late Collision Error */
+	XM_ST_MUL_COL	= 1<<1,		/* Bit  1:	Multiple Collisions */
+	XM_ST_SGN_COL	= 1<<0,		/* Bit  0:	Single Collision */
+};
+
+/*	XM_RX_LO_WM	16 bit r/w	Receive Low Water Mark */
+/*	XM_RX_HI_WM	16 bit r/w	Receive High Water Mark */
+#define XM_RX_WM_MSK	0x03ff		/* Bit 11.. 0:	Rx FIFO Watermark bits */
+
+
+/*	XM_DEV_ID	32 bit r/o	Device ID Register */
+#define XM_DEV_OUI	(0x00ffffffUL<<8)	/* Bit 31..8:	Device OUI */
+#define XM_DEV_REV	(0x07L << 5)		/* Bit  7..5:	Chip Rev Num */
+
+
+/*	XM_MODE		32 bit r/w	Mode Register */
+enum {
+	XM_MD_ENA_REJ	= 1<<26, /* Bit 26:	Enable Frame Reject */
+	XM_MD_SPOE_E	= 1<<25, /* Bit 25:	Send Pause on Edge */
+									/* 		extern generated */
+	XM_MD_TX_REP	= 1<<24, /* Bit 24:	Transmit Repeater Mode */
+	XM_MD_SPOFF_I	= 1<<23, /* Bit 23:	Send Pause on FIFO full */
+									/*		intern generated */
+	XM_MD_LE_STW	= 1<<22, /* Bit 22:	Rx Stat Word in Little Endian */
+	XM_MD_TX_CONT	= 1<<21, /* Bit 21:	Send Continuous */
+	XM_MD_TX_PAUSE	= 1<<20, /* Bit 20: (sc)	Send Pause Frame */
+	XM_MD_ATS	= 1<<19, /* Bit 19:	Append Time Stamp */
+	XM_MD_SPOL_I	= 1<<18, /* Bit 18:	Send Pause on Low */
+									/*		intern generated */
+	XM_MD_SPOH_I	= 1<<17, /* Bit 17:	Send Pause on High */
+									/*		intern generated */
+	XM_MD_CAP	= 1<<16, /* Bit 16:	Check Address Pair */
+	XM_MD_ENA_HASH	= 1<<15, /* Bit 15:	Enable Hashing */
+	XM_MD_CSA	= 1<<14, /* Bit 14:	Check Station Address */
+	XM_MD_CAA	= 1<<13, /* Bit 13:	Check Address Array */
+	XM_MD_RX_MCTRL	= 1<<12, /* Bit 12:	Rx MAC Control Frame */
+	XM_MD_RX_RUNT	= 1<<11, /* Bit 11:	Rx Runt Frames */
+	XM_MD_RX_IRLE	= 1<<10, /* Bit 10:	Rx in Range Len Err Frame */
+	XM_MD_RX_LONG	= 1<<9,  /* Bit  9:	Rx Long Frame */
+	XM_MD_RX_CRCE	= 1<<8,  /* Bit  8:	Rx CRC Error Frame */
+	XM_MD_RX_ERR	= 1<<7,  /* Bit  7:	Rx Error Frame */
+	XM_MD_DIS_UC	= 1<<6,  /* Bit  6:	Disable Rx Unicast */
+	XM_MD_DIS_MC	= 1<<5,  /* Bit  5:	Disable Rx Multicast */
+	XM_MD_DIS_BC	= 1<<4,  /* Bit  4:	Disable Rx Broadcast */
+	XM_MD_ENA_PROM	= 1<<3,  /* Bit  3:	Enable Promiscuous */
+	XM_MD_ENA_BE	= 1<<2,  /* Bit  2:	Enable Big Endian */
+	XM_MD_FTF	= 1<<1,  /* Bit  1: (sc)	Flush Tx FIFO */
+	XM_MD_FRF	= 1<<0,  /* Bit  0: (sc)	Flush Rx FIFO */
+};
+
+#define XM_PAUSE_MODE	(XM_MD_SPOE_E | XM_MD_SPOL_I | XM_MD_SPOH_I)
+#define XM_DEF_MODE	(XM_MD_RX_RUNT | XM_MD_RX_IRLE | XM_MD_RX_LONG |\
+			 XM_MD_RX_CRCE | XM_MD_RX_ERR | XM_MD_CSA)
+
+/*	XM_STAT_CMD	16 bit r/w	Statistics Command Register */
+enum {
+	XM_SC_SNP_RXC	= 1<<5,	/* Bit  5: (sc)	Snap Rx Counters */
+	XM_SC_SNP_TXC	= 1<<4,	/* Bit  4: (sc)	Snap Tx Counters */
+	XM_SC_CP_RXC	= 1<<3,	/* Bit  3: 	Copy Rx Counters Continuously */
+	XM_SC_CP_TXC	= 1<<2,	/* Bit  2:	Copy Tx Counters Continuously */
+	XM_SC_CLR_RXC	= 1<<1,	/* Bit  1: (sc)	Clear Rx Counters */
+	XM_SC_CLR_TXC	= 1<<0,	/* Bit  0: (sc) Clear Tx Counters */
+};
+
+
+/*	XM_RX_CNT_EV	32 bit r/o	Rx Counter Event Register */
+/*	XM_RX_EV_MSK	32 bit r/w	Rx Counter Event Mask */
+enum {
+	XMR_MAX_SZ_OV	= 1<<31, /* Bit 31:	1024-MaxSize Rx Cnt Ov*/
+	XMR_1023B_OV	= 1<<30, /* Bit 30:	512-1023Byte Rx Cnt Ov*/
+	XMR_511B_OV	= 1<<29, /* Bit 29:	256-511 Byte Rx Cnt Ov*/
+	XMR_255B_OV	= 1<<28, /* Bit 28:	128-255 Byte Rx Cnt Ov*/
+	XMR_127B_OV	= 1<<27, /* Bit 27:	65-127 Byte Rx Cnt Ov */
+	XMR_64B_OV	= 1<<26, /* Bit 26:	64 Byte Rx Cnt Ov */
+	XMR_UTIL_OV	= 1<<25, /* Bit 25:	Rx Util Cnt Overflow */
+	XMR_UTIL_UR	= 1<<24, /* Bit 24:	Rx Util Cnt Underrun */
+	XMR_CEX_ERR_OV	= 1<<23, /* Bit 23:	CEXT Err Cnt Ov */
+	XMR_FCS_ERR_OV	= 1<<21, /* Bit 21:	Rx FCS Error Cnt Ov */
+	XMR_LNG_ERR_OV	= 1<<20, /* Bit 20:	Rx too Long Err Cnt Ov*/
+	XMR_RUNT_OV	= 1<<19, /* Bit 19:	Runt Event Cnt Ov */
+	XMR_SHT_ERR_OV	= 1<<18, /* Bit 18:	Rx Short Ev Err Cnt Ov*/
+	XMR_SYM_ERR_OV	= 1<<17, /* Bit 17:	Rx Sym Err Cnt Ov */
+	XMR_CAR_ERR_OV	= 1<<15, /* Bit 15:	Rx Carr Ev Err Cnt Ov */
+	XMR_JAB_PKT_OV	= 1<<14, /* Bit 14:	Rx Jabb Packet Cnt Ov */
+	XMR_FIFO_OV	= 1<<13, /* Bit 13:	Rx FIFO Ov Ev Cnt Ov */
+	XMR_FRA_ERR_OV	= 1<<12, /* Bit 12:	Rx Framing Err Cnt Ov */
+	XMR_FMISS_OV	= 1<<11, /* Bit 11:	Rx Missed Ev Cnt Ov */
+	XMR_BURST	= 1<<10, /* Bit 10:	Rx Burst Event Cnt Ov */
+	XMR_INV_MOC	= 1<<9,  /* Bit  9:	Rx with inv. MAC OC Ov*/
+	XMR_INV_MP	= 1<<8,  /* Bit  8:	Rx inv Pause Frame Ov */
+	XMR_MCTRL_OV	= 1<<7,  /* Bit  7:	Rx MAC Ctrl-F Cnt Ov */
+	XMR_MPAUSE_OV	= 1<<6,  /* Bit  6:	Rx Pause MAC Ctrl-F Ov*/
+	XMR_UC_OK_OV	= 1<<5,  /* Bit  5:	Rx Unicast Frame CntOv*/
+	XMR_MC_OK_OV	= 1<<4,  /* Bit  4:	Rx Multicast Cnt Ov */
+	XMR_BC_OK_OV	= 1<<3,  /* Bit  3:	Rx Broadcast Cnt Ov */
+	XMR_OK_LO_OV	= 1<<2,  /* Bit  2:	Octets Rx OK Low CntOv*/
+	XMR_OK_HI_OV	= 1<<1,  /* Bit  1:	Octets Rx OK Hi Cnt Ov*/
+	XMR_OK_OV	= 1<<0,  /* Bit  0:	Frames Received Ok Ov */
+};
+
+#define XMR_DEF_MSK		(XMR_OK_LO_OV | XMR_OK_HI_OV)
+
+/*	XM_TX_CNT_EV	32 bit r/o	Tx Counter Event Register */
+/*	XM_TX_EV_MSK	32 bit r/w	Tx Counter Event Mask */
+enum {
+	XMT_MAX_SZ_OV	= 1<<25,	/* Bit 25:	1024-MaxSize Tx Cnt Ov*/
+	XMT_1023B_OV	= 1<<24,	/* Bit 24:	512-1023Byte Tx Cnt Ov*/
+	XMT_511B_OV	= 1<<23,	/* Bit 23:	256-511 Byte Tx Cnt Ov*/
+	XMT_255B_OV	= 1<<22,	/* Bit 22:	128-255 Byte Tx Cnt Ov*/
+	XMT_127B_OV	= 1<<21,	/* Bit 21:	65-127 Byte Tx Cnt Ov */
+	XMT_64B_OV	= 1<<20,	/* Bit 20:	64 Byte Tx Cnt Ov */
+	XMT_UTIL_OV	= 1<<19,	/* Bit 19:	Tx Util Cnt Overflow */
+	XMT_UTIL_UR	= 1<<18,	/* Bit 18:	Tx Util Cnt Underrun */
+	XMT_CS_ERR_OV	= 1<<17,	/* Bit 17:	Tx Carr Sen Err Cnt Ov*/
+	XMT_FIFO_UR_OV	= 1<<16,	/* Bit 16:	Tx FIFO Ur Ev Cnt Ov */
+	XMT_EX_DEF_OV	= 1<<15,	/* Bit 15:	Tx Ex Deferall Cnt Ov */
+	XMT_DEF	= 1<<14,	/* Bit 14:	Tx Deferred Cnt Ov */
+	XMT_LAT_COL_OV	= 1<<13,	/* Bit 13:	Tx Late Col Cnt Ov */
+	XMT_ABO_COL_OV	= 1<<12,	/* Bit 12:	Tx abo dueto Ex Col Ov*/
+	XMT_MUL_COL_OV	= 1<<11,	/* Bit 11:	Tx Mult Col Cnt Ov */
+	XMT_SNG_COL	= 1<<10,	/* Bit 10:	Tx Single Col Cnt Ov */
+	XMT_MCTRL_OV	= 1<<9,		/* Bit  9:	Tx MAC Ctrl Counter Ov*/
+	XMT_MPAUSE	= 1<<8,		/* Bit  8:	Tx Pause MAC Ctrl-F Ov*/
+	XMT_BURST	= 1<<7,		/* Bit  7:	Tx Burst Event Cnt Ov */
+	XMT_LONG	= 1<<6,		/* Bit  6:	Tx Long Frame Cnt Ov */
+	XMT_UC_OK_OV	= 1<<5,		/* Bit  5:	Tx Unicast Cnt Ov */
+	XMT_MC_OK_OV	= 1<<4,		/* Bit  4:	Tx Multicast Cnt Ov */
+	XMT_BC_OK_OV	= 1<<3,		/* Bit  3:	Tx Broadcast Cnt Ov */
+	XMT_OK_LO_OV	= 1<<2,		/* Bit  2:	Octets Tx OK Low CntOv*/
+	XMT_OK_HI_OV	= 1<<1,		/* Bit  1:	Octets Tx OK Hi Cnt Ov*/
+	XMT_OK_OV	= 1<<0,		/* Bit  0:	Frames Tx Ok Ov */
+};
+
+
+#define XMT_DEF_MSK		(XMT_OK_LO_OV | XMT_OK_HI_OV)
+
+struct skge_rx_desc {
+	u32		control;
+	u32		next_offset;
+	u32		dma_lo;
+	u32		dma_hi;
+	u32		status;
+	u32		timestamp;
+	u16		csum2;
+	u16		csum1;
+	u16		csum2_start;
+	u16		csum1_start;
+};
+
+struct skge_tx_desc {
+	u32		control;
+	u32		next_offset;
+	u32		dma_lo;
+	u32		dma_hi;
+	u32		status;
+	u32		csum_offs;
+	u16		csum_write;
+	u16		csum_start;
+	u32		rsvd;
+};
+
+struct skge_element {
+	struct skge_element	*next;
+	void			*desc;
+	struct io_buffer	*iob;
+};
+
+struct skge_ring {
+	struct skge_element *to_clean;
+	struct skge_element *to_use;
+	struct skge_element *start;
+};
+
+
+struct skge_hw {
+	u32	     regs;
+	struct pci_device    *pdev;
+	u32		     intr_mask;
+	struct net_device    *dev[2];
+
+	u8	     	     chip_id;
+	u8		     chip_rev;
+	u8		     copper;
+	u8		     ports;
+	u8		     phy_type;
+
+	u32	     	     ram_size;
+	u32	     	     ram_offset;
+	u16		     phy_addr;
+};
+
+enum pause_control {
+	FLOW_MODE_NONE 		= 1, /* No Flow-Control */
+	FLOW_MODE_LOC_SEND	= 2, /* Local station sends PAUSE */
+	FLOW_MODE_SYMMETRIC	= 3, /* Both stations may send PAUSE */
+	FLOW_MODE_SYM_OR_REM	= 4, /* Both stations may send PAUSE or
+				      * just the remote station may send PAUSE
+				      */
+};
+
+enum pause_status {
+	FLOW_STAT_INDETERMINATED=0,	/* indeterminated */
+	FLOW_STAT_NONE,			/* No Flow Control */
+	FLOW_STAT_REM_SEND,		/* Remote Station sends PAUSE */
+	FLOW_STAT_LOC_SEND,		/* Local station sends PAUSE */
+	FLOW_STAT_SYMMETRIC,		/* Both station may send PAUSE */
+};
+
+
+struct skge_port {
+	struct skge_hw	     *hw;
+	struct net_device    *netdev;
+	int		     port;
+
+	struct skge_ring     tx_ring;
+	struct skge_ring     rx_ring;
+
+	enum pause_control   flow_control;
+	enum pause_status    flow_status;
+	u8		     autoneg;	/* AUTONEG_ENABLE, AUTONEG_DISABLE */
+	u8		     duplex;	/* DUPLEX_HALF, DUPLEX_FULL */
+	u16		     speed;	/* SPEED_1000, SPEED_100, ... */
+	u32		     advertising;
+
+	void		     *mem;	/* PCI memory for rings */
+	u32		     dma;
+	int		     use_xm_link_timer;
+};
+
+
+/* Register accessor for memory mapped device */
+static inline u32 skge_read32(const struct skge_hw *hw, int reg)
+{
+	return readl(hw->regs + reg);
+}
+
+static inline u16 skge_read16(const struct skge_hw *hw, int reg)
+{
+	return readw(hw->regs + reg);
+}
+
+static inline u8 skge_read8(const struct skge_hw *hw, int reg)
+{
+	return readb(hw->regs + reg);
+}
+
+static inline void skge_write32(const struct skge_hw *hw, int reg, u32 val)
+{
+	writel(val, hw->regs + reg);
+}
+
+static inline void skge_write16(const struct skge_hw *hw, int reg, u16 val)
+{
+	writew(val, hw->regs + reg);
+}
+
+static inline void skge_write8(const struct skge_hw *hw, int reg, u8 val)
+{
+	writeb(val, hw->regs + reg);
+}
+
+/* MAC Related Registers inside the device. */
+#define SK_REG(port,reg)	(((port)<<7)+(u16)(reg))
+#define SK_XMAC_REG(port, reg) \
+	((BASE_XMAC_1 + (port) * (BASE_XMAC_2 - BASE_XMAC_1)) | (reg) << 1)
+
+static inline u32 xm_read32(const struct skge_hw *hw, int port, int reg)
+{
+	u32 v;
+	v = skge_read16(hw, SK_XMAC_REG(port, reg));
+	v |= (u32)skge_read16(hw, SK_XMAC_REG(port, reg+2)) << 16;
+	return v;
+}
+
+static inline u16 xm_read16(const struct skge_hw *hw, int port, int reg)
+{
+	return skge_read16(hw, SK_XMAC_REG(port,reg));
+}
+
+static inline void xm_write32(const struct skge_hw *hw, int port, int r, u32 v)
+{
+	skge_write16(hw, SK_XMAC_REG(port,r), v & 0xffff);
+	skge_write16(hw, SK_XMAC_REG(port,r+2), v >> 16);
+}
+
+static inline void xm_write16(const struct skge_hw *hw, int port, int r, u16 v)
+{
+	skge_write16(hw, SK_XMAC_REG(port,r), v);
+}
+
+static inline void xm_outhash(const struct skge_hw *hw, int port, int reg,
+				   const u8 *hash)
+{
+	xm_write16(hw, port, reg,   (u16)hash[0] | ((u16)hash[1] << 8));
+	xm_write16(hw, port, reg+2, (u16)hash[2] | ((u16)hash[3] << 8));
+	xm_write16(hw, port, reg+4, (u16)hash[4] | ((u16)hash[5] << 8));
+	xm_write16(hw, port, reg+6, (u16)hash[6] | ((u16)hash[7] << 8));
+}
+
+static inline void xm_outaddr(const struct skge_hw *hw, int port, int reg,
+				   const u8 *addr)
+{
+	xm_write16(hw, port, reg,   (u16)addr[0] | ((u16)addr[1] << 8));
+	xm_write16(hw, port, reg+2, (u16)addr[2] | ((u16)addr[3] << 8));
+	xm_write16(hw, port, reg+4, (u16)addr[4] | ((u16)addr[5] << 8));
+}
+
+#define SK_GMAC_REG(port,reg) \
+	(BASE_GMAC_1 + (port) * (BASE_GMAC_2-BASE_GMAC_1) + (reg))
+
+static inline u16 gma_read16(const struct skge_hw *hw, int port, int reg)
+{
+	return skge_read16(hw, SK_GMAC_REG(port,reg));
+}
+
+static inline u32 gma_read32(const struct skge_hw *hw, int port, int reg)
+{
+	return (u32) skge_read16(hw, SK_GMAC_REG(port,reg))
+		| ((u32)skge_read16(hw, SK_GMAC_REG(port,reg+4)) << 16);
+}
+
+static inline void gma_write16(const struct skge_hw *hw, int port, int r, u16 v)
+{
+	skge_write16(hw, SK_GMAC_REG(port,r), v);
+}
+
+static inline void gma_set_addr(struct skge_hw *hw, int port, int reg,
+				    const u8 *addr)
+{
+	gma_write16(hw, port, reg,  (u16) addr[0] | ((u16) addr[1] << 8));
+	gma_write16(hw, port, reg+4,(u16) addr[2] | ((u16) addr[3] << 8));
+	gma_write16(hw, port, reg+8,(u16) addr[4] | ((u16) addr[5] << 8));
+}
+
+#endif
diff --git a/gpxe/src/drivers/net/sky2.c b/gpxe/src/drivers/net/sky2.c
new file mode 100644
index 0000000..00940af
--- /dev/null
+++ b/gpxe/src/drivers/net/sky2.c
@@ -0,0 +1,2399 @@
+/*
+ * gPXE driver for Marvell Yukon 2 chipset. Derived from Linux sky2 driver
+ * (v1.22), which was based on earlier sk98lin and skge drivers.
+ *
+ * This driver intentionally does not support all the features
+ * of the original driver such as link fail-over and link management because
+ * those should be done at higher levels.
+ *
+ * Copyright (C) 2005 Stephen Hemminger <shemminger at osdl.org>
+ *
+ * Modified for gPXE, April 2009 by Joshua Oreman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_ONLY );
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/pci.h>
+#include <byteswap.h>
+#include <mii.h>
+
+#include "sky2.h"
+
+#define DRV_NAME		"sky2"
+#define DRV_VERSION		"1.22"
+#define PFX			DRV_NAME " "
+
+/*
+ * The Yukon II chipset takes 64 bit command blocks (called list elements)
+ * that are organized into three (receive, transmit, status) different rings
+ * similar to Tigon3.
+ *
+ * Each ring start must be aligned to a 4k boundary. You will get mysterious
+ * "invalid LE" errors if they're not.
+ *
+ * The card silently forces each ring size to be at least 128. If you
+ * act as though one of them is smaller (by setting the below
+ * #defines) you'll get bad bugs.
+ */
+
+#define RX_LE_SIZE	    	128
+#define RX_LE_BYTES		(RX_LE_SIZE*sizeof(struct sky2_rx_le))
+#define RX_RING_ALIGN		4096
+#define RX_PENDING		(RX_LE_SIZE/6 - 2)
+
+#define TX_RING_SIZE		128
+#define TX_PENDING		(TX_RING_SIZE - 1)
+#define TX_RING_ALIGN		4096
+#define MAX_SKB_TX_LE		4
+
+#define STATUS_RING_SIZE	512	/* 2 ports * (TX + RX) */
+#define STATUS_LE_BYTES		(STATUS_RING_SIZE*sizeof(struct sky2_status_le))
+#define STATUS_RING_ALIGN       4096
+#define PHY_RETRIES		1000
+
+#define SKY2_EEPROM_MAGIC	0x9955aabb
+
+
+#define RING_NEXT(x,s)	(((x)+1) & ((s)-1))
+
+static struct pci_device_id sky2_id_table[] = {
+	PCI_ROM(0x1148, 0x9000, "sk9sxx", "Syskonnect SK-9Sxx", 0),
+	PCI_ROM(0x1148, 0x9e00, "sk9exx", "Syskonnect SK-9Exx", 0),
+	PCI_ROM(0x1186, 0x4b00, "dge560t", "D-Link DGE-560T", 0),
+	PCI_ROM(0x1186, 0x4001, "dge550sx", "D-Link DGE-550SX", 0),
+	PCI_ROM(0x1186, 0x4b02, "dge560sx", "D-Link DGE-560SX", 0),
+	PCI_ROM(0x1186, 0x4b03, "dge550t", "D-Link DGE-550T", 0),
+	PCI_ROM(0x11ab, 0x4340, "m88e8021", "Marvell 88E8021", 0),
+	PCI_ROM(0x11ab, 0x4341, "m88e8022", "Marvell 88E8022", 0),
+	PCI_ROM(0x11ab, 0x4342, "m88e8061", "Marvell 88E8061", 0),
+	PCI_ROM(0x11ab, 0x4343, "m88e8062", "Marvell 88E8062", 0),
+	PCI_ROM(0x11ab, 0x4344, "m88e8021b", "Marvell 88E8021", 0),
+	PCI_ROM(0x11ab, 0x4345, "m88e8022b", "Marvell 88E8022", 0),
+	PCI_ROM(0x11ab, 0x4346, "m88e8061b", "Marvell 88E8061", 0),
+	PCI_ROM(0x11ab, 0x4347, "m88e8062b", "Marvell 88E8062", 0),
+	PCI_ROM(0x11ab, 0x4350, "m88e8035", "Marvell 88E8035", 0),
+	PCI_ROM(0x11ab, 0x4351, "m88e8036", "Marvell 88E8036", 0),
+	PCI_ROM(0x11ab, 0x4352, "m88e8038", "Marvell 88E8038", 0),
+	PCI_ROM(0x11ab, 0x4353, "m88e8039", "Marvell 88E8039", 0),
+	PCI_ROM(0x11ab, 0x4354, "m88e8040", "Marvell 88E8040", 0),
+	PCI_ROM(0x11ab, 0x4355, "m88e8040t", "Marvell 88E8040T", 0),
+	PCI_ROM(0x11ab, 0x4356, "m88ec033", "Marvel 88EC033", 0),
+	PCI_ROM(0x11ab, 0x4357, "m88e8042", "Marvell 88E8042", 0),
+	PCI_ROM(0x11ab, 0x435a, "m88e8048", "Marvell 88E8048", 0),
+	PCI_ROM(0x11ab, 0x4360, "m88e8052", "Marvell 88E8052", 0),
+	PCI_ROM(0x11ab, 0x4361, "m88e8050", "Marvell 88E8050", 0),
+	PCI_ROM(0x11ab, 0x4362, "m88e8053", "Marvell 88E8053", 0),
+	PCI_ROM(0x11ab, 0x4363, "m88e8055", "Marvell 88E8055", 0),
+	PCI_ROM(0x11ab, 0x4364, "m88e8056", "Marvell 88E8056", 0),
+	PCI_ROM(0x11ab, 0x4365, "m88e8070", "Marvell 88E8070", 0),
+	PCI_ROM(0x11ab, 0x4366, "m88ec036", "Marvell 88EC036", 0),
+	PCI_ROM(0x11ab, 0x4367, "m88ec032", "Marvell 88EC032", 0),
+	PCI_ROM(0x11ab, 0x4368, "m88ec034", "Marvell 88EC034", 0),
+	PCI_ROM(0x11ab, 0x4369, "m88ec042", "Marvell 88EC042", 0),
+	PCI_ROM(0x11ab, 0x436a, "m88e8058", "Marvell 88E8058", 0),
+	PCI_ROM(0x11ab, 0x436b, "m88e8071", "Marvell 88E8071", 0),
+	PCI_ROM(0x11ab, 0x436c, "m88e8072", "Marvell 88E8072", 0),
+	PCI_ROM(0x11ab, 0x436d, "m88e8055b", "Marvell 88E8055", 0),
+	PCI_ROM(0x11ab, 0x4370, "m88e8075", "Marvell 88E8075", 0),
+	PCI_ROM(0x11ab, 0x4380, "m88e8057", "Marvell 88E8057", 0)
+};
+
+/* Avoid conditionals by using array */
+static const unsigned txqaddr[] = { Q_XA1, Q_XA2 };
+static const unsigned rxqaddr[] = { Q_R1, Q_R2 };
+static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 };
+
+static void sky2_set_multicast(struct net_device *dev);
+
+/* Access to PHY via serial interconnect */
+static int gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val)
+{
+	int i;
+
+	gma_write16(hw, port, GM_SMI_DATA, val);
+	gma_write16(hw, port, GM_SMI_CTRL,
+		    GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) | GM_SMI_CT_REG_AD(reg));
+
+	for (i = 0; i < PHY_RETRIES; i++) {
+		u16 ctrl = gma_read16(hw, port, GM_SMI_CTRL);
+		if (ctrl == 0xffff)
+			goto io_error;
+
+		if (!(ctrl & GM_SMI_CT_BUSY))
+			return 0;
+
+		udelay(10);
+	}
+
+	DBG(PFX "%s: phy write timeout\n", hw->dev[port]->name);
+	return -ETIMEDOUT;
+
+io_error:
+	DBG(PFX "%s: phy I/O error\n", hw->dev[port]->name);
+	return -EIO;
+}
+
+static int __gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg, u16 *val)
+{
+	int i;
+
+	gma_write16(hw, port, GM_SMI_CTRL, GM_SMI_CT_PHY_AD(PHY_ADDR_MARV)
+		    | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD);
+
+	for (i = 0; i < PHY_RETRIES; i++) {
+		u16 ctrl = gma_read16(hw, port, GM_SMI_CTRL);
+		if (ctrl == 0xffff)
+			goto io_error;
+
+		if (ctrl & GM_SMI_CT_RD_VAL) {
+			*val = gma_read16(hw, port, GM_SMI_DATA);
+			return 0;
+		}
+
+		udelay(10);
+	}
+
+	DBG(PFX "%s: phy read timeout\n", hw->dev[port]->name);
+	return -ETIMEDOUT;
+io_error:
+	DBG(PFX "%s: phy I/O error\n", hw->dev[port]->name);
+	return -EIO;
+}
+
+static inline u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg)
+{
+	u16 v = 0;
+	__gm_phy_read(hw, port, reg, &v);
+	return v;
+}
+
+
+static void sky2_power_on(struct sky2_hw *hw)
+{
+	/* switch power to VCC (WA for VAUX problem) */
+	sky2_write8(hw, B0_POWER_CTRL,
+		    PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON);
+
+	/* disable Core Clock Division, */
+	sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS);
+
+	if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
+		/* enable bits are inverted */
+		sky2_write8(hw, B2_Y2_CLK_GATE,
+			    Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
+			    Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
+			    Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS);
+	else
+		sky2_write8(hw, B2_Y2_CLK_GATE, 0);
+
+	if (hw->flags & SKY2_HW_ADV_POWER_CTL) {
+		u32 reg;
+
+		sky2_pci_write32(hw, PCI_DEV_REG3, 0);
+
+		reg = sky2_pci_read32(hw, PCI_DEV_REG4);
+		/* set all bits to 0 except bits 15..12 and 8 */
+		reg &= P_ASPM_CONTROL_MSK;
+		sky2_pci_write32(hw, PCI_DEV_REG4, reg);
+
+		reg = sky2_pci_read32(hw, PCI_DEV_REG5);
+		/* set all bits to 0 except bits 28 & 27 */
+		reg &= P_CTL_TIM_VMAIN_AV_MSK;
+		sky2_pci_write32(hw, PCI_DEV_REG5, reg);
+
+		sky2_pci_write32(hw, PCI_CFG_REG_1, 0);
+
+		/* Enable workaround for dev 4.107 on Yukon-Ultra & Extreme */
+		reg = sky2_read32(hw, B2_GP_IO);
+		reg |= GLB_GPIO_STAT_RACE_DIS;
+		sky2_write32(hw, B2_GP_IO, reg);
+
+		sky2_read32(hw, B2_GP_IO);
+	}
+}
+
+static void sky2_power_aux(struct sky2_hw *hw)
+{
+	if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
+		sky2_write8(hw, B2_Y2_CLK_GATE, 0);
+	else
+		/* enable bits are inverted */
+		sky2_write8(hw, B2_Y2_CLK_GATE,
+			    Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
+			    Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
+			    Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS);
+
+	/* switch power to VAUX */
+	if (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL)
+		sky2_write8(hw, B0_POWER_CTRL,
+			    (PC_VAUX_ENA | PC_VCC_ENA |
+			     PC_VAUX_ON | PC_VCC_OFF));
+}
+
+static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port)
+{
+	u16 reg;
+
+	/* disable all GMAC IRQ's */
+	sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+
+	gma_write16(hw, port, GM_MC_ADDR_H1, 0);	/* clear MC hash */
+	gma_write16(hw, port, GM_MC_ADDR_H2, 0);
+	gma_write16(hw, port, GM_MC_ADDR_H3, 0);
+	gma_write16(hw, port, GM_MC_ADDR_H4, 0);
+
+	reg = gma_read16(hw, port, GM_RX_CTRL);
+	reg |= GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA;
+	gma_write16(hw, port, GM_RX_CTRL, reg);
+}
+
+/* flow control to advertise bits */
+static const u16 copper_fc_adv[] = {
+	[FC_NONE]	= 0,
+	[FC_TX]		= PHY_M_AN_ASP,
+	[FC_RX]		= PHY_M_AN_PC,
+	[FC_BOTH]	= PHY_M_AN_PC | PHY_M_AN_ASP,
+};
+
+/* flow control to advertise bits when using 1000BaseX */
+static const u16 fiber_fc_adv[] = {
+	[FC_NONE] = PHY_M_P_NO_PAUSE_X,
+	[FC_TX]   = PHY_M_P_ASYM_MD_X,
+	[FC_RX]	  = PHY_M_P_SYM_MD_X,
+	[FC_BOTH] = PHY_M_P_BOTH_MD_X,
+};
+
+/* flow control to GMA disable bits */
+static const u16 gm_fc_disable[] = {
+	[FC_NONE] = GM_GPCR_FC_RX_DIS | GM_GPCR_FC_TX_DIS,
+	[FC_TX]	  = GM_GPCR_FC_RX_DIS,
+	[FC_RX]	  = GM_GPCR_FC_TX_DIS,
+	[FC_BOTH] = 0,
+};
+
+
+static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
+{
+	struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
+	u16 ctrl, ct1000, adv, pg, ledctrl, ledover, reg;
+
+	if (sky2->autoneg == AUTONEG_ENABLE &&
+	    !(hw->flags & SKY2_HW_NEWER_PHY)) {
+		u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
+
+		ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK |
+			   PHY_M_EC_MAC_S_MSK);
+		ectrl |= PHY_M_EC_MAC_S(MAC_TX_CLK_25_MHZ);
+
+		/* on PHY 88E1040 Rev.D0 (and newer) downshift control changed */
+		if (hw->chip_id == CHIP_ID_YUKON_EC)
+			/* set downshift counter to 3x and enable downshift */
+			ectrl |= PHY_M_EC_DSC_2(2) | PHY_M_EC_DOWN_S_ENA;
+		else
+			/* set master & slave downshift counter to 1x */
+			ectrl |= PHY_M_EC_M_DSC(0) | PHY_M_EC_S_DSC(1);
+
+		gm_phy_write(hw, port, PHY_MARV_EXT_CTRL, ectrl);
+	}
+
+	ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+	if (sky2_is_copper(hw)) {
+		if (!(hw->flags & SKY2_HW_GIGABIT)) {
+			/* enable automatic crossover */
+			ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO) >> 1;
+
+			if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+			    hw->chip_rev == CHIP_REV_YU_FE2_A0) {
+				u16 spec;
+
+				/* Enable Class A driver for FE+ A0 */
+				spec = gm_phy_read(hw, port, PHY_MARV_FE_SPEC_2);
+				spec |= PHY_M_FESC_SEL_CL_A;
+				gm_phy_write(hw, port, PHY_MARV_FE_SPEC_2, spec);
+			}
+		} else {
+			/* disable energy detect */
+			ctrl &= ~PHY_M_PC_EN_DET_MSK;
+
+			/* enable automatic crossover */
+			ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO);
+
+			/* downshift on PHY 88E1112 and 88E1149 is changed */
+			if (sky2->autoneg == AUTONEG_ENABLE
+			    && (hw->flags & SKY2_HW_NEWER_PHY)) {
+				/* set downshift counter to 3x and enable downshift */
+				ctrl &= ~PHY_M_PC_DSC_MSK;
+				ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA;
+			}
+		}
+	} else {
+		/* workaround for deviation #4.88 (CRC errors) */
+		/* disable Automatic Crossover */
+
+		ctrl &= ~PHY_M_PC_MDIX_MSK;
+	}
+
+	gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+	/* special setup for PHY 88E1112 Fiber */
+	if (hw->chip_id == CHIP_ID_YUKON_XL && (hw->flags & SKY2_HW_FIBRE_PHY)) {
+		pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+
+		/* Fiber: select 1000BASE-X only mode MAC Specific Ctrl Reg. */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+		ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+		ctrl &= ~PHY_M_MAC_MD_MSK;
+		ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX);
+		gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+		if (hw->pmd_type  == 'P') {
+			/* select page 1 to access Fiber registers */
+			gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 1);
+
+			/* for SFP-module set SIGDET polarity to low */
+			ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+			ctrl |= PHY_M_FIB_SIGD_POL;
+			gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+		}
+
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+	}
+
+	ctrl = PHY_CT_RESET;
+	ct1000 = 0;
+	adv = PHY_AN_CSMA;
+	reg = 0;
+
+	if (sky2->autoneg == AUTONEG_ENABLE) {
+		if (sky2_is_copper(hw)) {
+			if (sky2->advertising & ADVERTISED_1000baseT_Full)
+				ct1000 |= PHY_M_1000C_AFD;
+			if (sky2->advertising & ADVERTISED_1000baseT_Half)
+				ct1000 |= PHY_M_1000C_AHD;
+			if (sky2->advertising & ADVERTISED_100baseT_Full)
+				adv |= PHY_M_AN_100_FD;
+			if (sky2->advertising & ADVERTISED_100baseT_Half)
+				adv |= PHY_M_AN_100_HD;
+			if (sky2->advertising & ADVERTISED_10baseT_Full)
+				adv |= PHY_M_AN_10_FD;
+			if (sky2->advertising & ADVERTISED_10baseT_Half)
+				adv |= PHY_M_AN_10_HD;
+
+			adv |= copper_fc_adv[sky2->flow_mode];
+		} else {	/* special defines for FIBER (88E1040S only) */
+			if (sky2->advertising & ADVERTISED_1000baseT_Full)
+				adv |= PHY_M_AN_1000X_AFD;
+			if (sky2->advertising & ADVERTISED_1000baseT_Half)
+				adv |= PHY_M_AN_1000X_AHD;
+
+			adv |= fiber_fc_adv[sky2->flow_mode];
+		}
+
+		/* Restart Auto-negotiation */
+		ctrl |= PHY_CT_ANE | PHY_CT_RE_CFG;
+	} else {
+		/* forced speed/duplex settings */
+		ct1000 = PHY_M_1000C_MSE;
+
+		/* Disable auto update for duplex flow control and speed */
+		reg |= GM_GPCR_AU_ALL_DIS;
+
+		switch (sky2->speed) {
+		case SPEED_1000:
+			ctrl |= PHY_CT_SP1000;
+			reg |= GM_GPCR_SPEED_1000;
+			break;
+		case SPEED_100:
+			ctrl |= PHY_CT_SP100;
+			reg |= GM_GPCR_SPEED_100;
+			break;
+		}
+
+		if (sky2->duplex == DUPLEX_FULL) {
+			reg |= GM_GPCR_DUP_FULL;
+			ctrl |= PHY_CT_DUP_MD;
+		} else if (sky2->speed < SPEED_1000)
+			sky2->flow_mode = FC_NONE;
+
+
+		reg |= gm_fc_disable[sky2->flow_mode];
+
+		/* Forward pause packets to GMAC? */
+		if (sky2->flow_mode & FC_RX)
+			sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
+		else
+			sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+	}
+
+	gma_write16(hw, port, GM_GP_CTRL, reg);
+
+	if (hw->flags & SKY2_HW_GIGABIT)
+		gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, ct1000);
+
+	gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, adv);
+	gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+
+	/* Setup Phy LED's */
+	ledctrl = PHY_M_LED_PULS_DUR(PULS_170MS);
+	ledover = 0;
+
+	switch (hw->chip_id) {
+	case CHIP_ID_YUKON_FE:
+		/* on 88E3082 these bits are at 11..9 (shifted left) */
+		ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) << 1;
+
+		ctrl = gm_phy_read(hw, port, PHY_MARV_FE_LED_PAR);
+
+		/* delete ACT LED control bits */
+		ctrl &= ~PHY_M_FELP_LED1_MSK;
+		/* change ACT LED control to blink mode */
+		ctrl |= PHY_M_FELP_LED1_CTRL(LED_PAR_CTRL_ACT_BL);
+		gm_phy_write(hw, port, PHY_MARV_FE_LED_PAR, ctrl);
+		break;
+
+	case CHIP_ID_YUKON_FE_P:
+		/* Enable Link Partner Next Page */
+		ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+		ctrl |= PHY_M_PC_ENA_LIP_NP;
+
+		/* disable Energy Detect and enable scrambler */
+		ctrl &= ~(PHY_M_PC_ENA_ENE_DT | PHY_M_PC_DIS_SCRAMB);
+		gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+		/* set LED2 -> ACT, LED1 -> LINK, LED0 -> SPEED */
+		ctrl = PHY_M_FELP_LED2_CTRL(LED_PAR_CTRL_ACT_BL) |
+			PHY_M_FELP_LED1_CTRL(LED_PAR_CTRL_LINK) |
+			PHY_M_FELP_LED0_CTRL(LED_PAR_CTRL_SPEED);
+
+		gm_phy_write(hw, port, PHY_MARV_FE_LED_PAR, ctrl);
+		break;
+
+	case CHIP_ID_YUKON_XL:
+		pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+
+		/* select page 3 to access LED control register */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+
+		/* set LED Function Control register */
+		gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+			     (PHY_M_LEDC_LOS_CTRL(1) |	/* LINK/ACT */
+			      PHY_M_LEDC_INIT_CTRL(7) |	/* 10 Mbps */
+			      PHY_M_LEDC_STA1_CTRL(7) |	/* 100 Mbps */
+			      PHY_M_LEDC_STA0_CTRL(7)));	/* 1000 Mbps */
+
+		/* set Polarity Control register */
+		gm_phy_write(hw, port, PHY_MARV_PHY_STAT,
+			     (PHY_M_POLC_LS1_P_MIX(4) |
+			      PHY_M_POLC_IS0_P_MIX(4) |
+			      PHY_M_POLC_LOS_CTRL(2) |
+			      PHY_M_POLC_INIT_CTRL(2) |
+			      PHY_M_POLC_STA1_CTRL(2) |
+			      PHY_M_POLC_STA0_CTRL(2)));
+
+		/* restore page register */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+		break;
+
+	case CHIP_ID_YUKON_EC_U:
+	case CHIP_ID_YUKON_EX:
+	case CHIP_ID_YUKON_SUPR:
+		pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+
+		/* select page 3 to access LED control register */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+
+		/* set LED Function Control register */
+		gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+			     (PHY_M_LEDC_LOS_CTRL(1) |	/* LINK/ACT */
+			      PHY_M_LEDC_INIT_CTRL(8) |	/* 10 Mbps */
+			      PHY_M_LEDC_STA1_CTRL(7) |	/* 100 Mbps */
+			      PHY_M_LEDC_STA0_CTRL(7)));/* 1000 Mbps */
+
+		/* set Blink Rate in LED Timer Control Register */
+		gm_phy_write(hw, port, PHY_MARV_INT_MASK,
+			     ledctrl | PHY_M_LED_BLINK_RT(BLINK_84MS));
+		/* restore page register */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+		break;
+
+	default:
+		/* set Tx LED (LED_TX) to blink mode on Rx OR Tx activity */
+		ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) | PHY_M_LEDC_TX_CTRL;
+
+		/* turn off the Rx LED (LED_RX) */
+		ledover |= PHY_M_LED_MO_RX(MO_LED_OFF);
+	}
+
+	if (hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_UL_2) {
+		/* apply fixes in PHY AFE */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 255);
+
+		/* increase differential signal amplitude in 10BASE-T */
+		gm_phy_write(hw, port, 0x18, 0xaa99);
+		gm_phy_write(hw, port, 0x17, 0x2011);
+
+		if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
+			/* fix for IEEE A/B Symmetry failure in 1000BASE-T */
+			gm_phy_write(hw, port, 0x18, 0xa204);
+			gm_phy_write(hw, port, 0x17, 0x2002);
+		}
+
+		/* set page register to 0 */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+	} else if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+		   hw->chip_rev == CHIP_REV_YU_FE2_A0) {
+		/* apply workaround for integrated resistors calibration */
+		gm_phy_write(hw, port, PHY_MARV_PAGE_ADDR, 17);
+		gm_phy_write(hw, port, PHY_MARV_PAGE_DATA, 0x3f60);
+	} else if (hw->chip_id != CHIP_ID_YUKON_EX &&
+		   hw->chip_id < CHIP_ID_YUKON_SUPR) {
+		/* no effect on Yukon-XL */
+		gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
+
+		if (sky2->autoneg == AUTONEG_DISABLE || sky2->speed == SPEED_100) {
+			/* turn on 100 Mbps LED (LED_LINK100) */
+			ledover |= PHY_M_LED_MO_100(MO_LED_ON);
+		}
+
+		if (ledover)
+			gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
+
+	}
+
+	/* Enable phy interrupt on auto-negotiation complete (or link up) */
+	if (sky2->autoneg == AUTONEG_ENABLE)
+		gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL);
+	else
+		gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
+}
+
+static const u32 phy_power[] = { PCI_Y2_PHY1_POWD, PCI_Y2_PHY2_POWD };
+static const u32 coma_mode[] = { PCI_Y2_PHY1_COMA, PCI_Y2_PHY2_COMA };
+
+static void sky2_phy_power_up(struct sky2_hw *hw, unsigned port)
+{
+	u32 reg1;
+
+	sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+	reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
+	reg1 &= ~phy_power[port];
+
+	if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
+		reg1 |= coma_mode[port];
+
+	sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
+	sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+	sky2_pci_read32(hw, PCI_DEV_REG1);
+
+	if (hw->chip_id == CHIP_ID_YUKON_FE)
+		gm_phy_write(hw, port, PHY_MARV_CTRL, PHY_CT_ANE);
+	else if (hw->flags & SKY2_HW_ADV_POWER_CTL)
+		sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+}
+
+static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port)
+{
+	u32 reg1;
+	u16 ctrl;
+
+	/* release GPHY Control reset */
+	sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+
+	/* release GMAC reset */
+	sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR);
+
+	if (hw->flags & SKY2_HW_NEWER_PHY) {
+		/* select page 2 to access MAC control register */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+
+		ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+		/* allow GMII Power Down */
+		ctrl &= ~PHY_M_MAC_GMIF_PUP;
+		gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+		/* set page register back to 0 */
+		gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+	}
+
+	/* setup General Purpose Control Register */
+	gma_write16(hw, port, GM_GP_CTRL,
+		    GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 | GM_GPCR_AU_ALL_DIS);
+
+	if (hw->chip_id != CHIP_ID_YUKON_EC) {
+		if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
+			/* select page 2 to access MAC control register */
+			gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+
+			ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+			/* enable Power Down */
+			ctrl |= PHY_M_PC_POW_D_ENA;
+			gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+			/* set page register back to 0 */
+			gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+		}
+
+		/* set IEEE compatible Power Down Mode (dev. #4.99) */
+		gm_phy_write(hw, port, PHY_MARV_CTRL, PHY_CT_PDOWN);
+	}
+
+	sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+	reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
+	reg1 |= phy_power[port];		/* set PHY to PowerDown/COMA Mode */
+	sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
+	sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+}
+
+static void sky2_set_tx_stfwd(struct sky2_hw *hw, unsigned port)
+{
+	if ( (hw->chip_id == CHIP_ID_YUKON_EX &&
+	      hw->chip_rev != CHIP_REV_YU_EX_A0) ||
+	     hw->chip_id == CHIP_ID_YUKON_FE_P ||
+	     hw->chip_id == CHIP_ID_YUKON_SUPR) {
+		/* disable jumbo frames on devices that support them */
+		sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
+			     TX_JUMBO_DIS | TX_STFW_ENA);
+	} else {
+		sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_STFW_ENA);
+	}
+}
+
+static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
+{
+	u16 reg;
+	u32 rx_reg;
+	int i;
+	const u8 *addr = hw->dev[port]->ll_addr;
+
+	sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+	sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+
+	sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR);
+
+	if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 && port == 1) {
+		/* WA DEV_472 -- looks like crossed wires on port 2 */
+		/* clear GMAC 1 Control reset */
+		sky2_write8(hw, SK_REG(0, GMAC_CTRL), GMC_RST_CLR);
+		do {
+			sky2_write8(hw, SK_REG(1, GMAC_CTRL), GMC_RST_SET);
+			sky2_write8(hw, SK_REG(1, GMAC_CTRL), GMC_RST_CLR);
+		} while (gm_phy_read(hw, 1, PHY_MARV_ID0) != PHY_MARV_ID0_VAL ||
+			 gm_phy_read(hw, 1, PHY_MARV_ID1) != PHY_MARV_ID1_Y2 ||
+			 gm_phy_read(hw, 1, PHY_MARV_INT_MASK) != 0);
+	}
+
+	sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
+
+	/* Enable Transmit FIFO Underrun */
+	sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);
+
+	sky2_phy_power_up(hw, port);
+	sky2_phy_init(hw, port);
+
+	/* MIB clear */
+	reg = gma_read16(hw, port, GM_PHY_ADDR);
+	gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR);
+
+	for (i = GM_MIB_CNT_BASE; i <= GM_MIB_CNT_END; i += 4)
+		gma_read16(hw, port, i);
+	gma_write16(hw, port, GM_PHY_ADDR, reg);
+
+	/* transmit control */
+	gma_write16(hw, port, GM_TX_CTRL, TX_COL_THR(TX_COL_DEF));
+
+	/* receive control reg: unicast + multicast + no FCS  */
+	gma_write16(hw, port, GM_RX_CTRL,
+		    GM_RXCR_UCF_ENA | GM_RXCR_CRC_DIS | GM_RXCR_MCF_ENA);
+
+	/* transmit flow control */
+	gma_write16(hw, port, GM_TX_FLOW_CTRL, 0xffff);
+
+	/* transmit parameter */
+	gma_write16(hw, port, GM_TX_PARAM,
+		    TX_JAM_LEN_VAL(TX_JAM_LEN_DEF) |
+		    TX_JAM_IPG_VAL(TX_JAM_IPG_DEF) |
+		    TX_IPG_JAM_DATA(TX_IPG_JAM_DEF) |
+		    TX_BACK_OFF_LIM(TX_BOF_LIM_DEF));
+
+	/* serial mode register */
+	reg = DATA_BLIND_VAL(DATA_BLIND_DEF) |
+		GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+
+	gma_write16(hw, port, GM_SERIAL_MODE, reg);
+
+	/* virtual address for data */
+	gma_set_addr(hw, port, GM_SRC_ADDR_2L, addr);
+
+	/* physical address: used for pause frames */
+	gma_set_addr(hw, port, GM_SRC_ADDR_1L, addr);
+
+	/* ignore counter overflows */
+	gma_write16(hw, port, GM_TX_IRQ_MSK, 0);
+	gma_write16(hw, port, GM_RX_IRQ_MSK, 0);
+	gma_write16(hw, port, GM_TR_IRQ_MSK, 0);
+
+	/* Configure Rx MAC FIFO */
+	sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR);
+	rx_reg = GMF_OPER_ON | GMF_RX_F_FL_ON;
+	if (hw->chip_id == CHIP_ID_YUKON_EX ||
+	    hw->chip_id == CHIP_ID_YUKON_FE_P)
+		rx_reg |= GMF_RX_OVER_ON;
+
+	sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), rx_reg);
+
+	if (hw->chip_id == CHIP_ID_YUKON_XL) {
+		/* Hardware errata - clear flush mask */
+		sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), 0);
+	} else {
+		/* Flush Rx MAC FIFO on any flow control or error */
+		sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), GMR_FS_ANY_ERR);
+	}
+
+	/* Set threshold to 0xa (64 bytes) + 1 to workaround pause bug  */
+	reg = RX_GMF_FL_THR_DEF + 1;
+	/* Another magic mystery workaround from sk98lin */
+	if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+	    hw->chip_rev == CHIP_REV_YU_FE2_A0)
+		reg = 0x178;
+	sky2_write16(hw, SK_REG(port, RX_GMF_FL_THR), reg);
+
+	/* Configure Tx MAC FIFO */
+	sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR);
+	sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON);
+
+	/* On chips without ram buffer, pause is controled by MAC level */
+	if (!(hw->flags & SKY2_HW_RAM_BUFFER)) {
+		sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8);
+		sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8);
+
+		sky2_set_tx_stfwd(hw, port);
+	}
+
+	if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+	    hw->chip_rev == CHIP_REV_YU_FE2_A0) {
+		/* disable dynamic watermark */
+		reg = sky2_read16(hw, SK_REG(port, TX_GMF_EA));
+		reg &= ~TX_DYN_WM_ENA;
+		sky2_write16(hw, SK_REG(port, TX_GMF_EA), reg);
+	}
+}
+
+/* Assign Ram Buffer allocation to queue */
+static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, u32 space)
+{
+	u32 end;
+
+	/* convert from K bytes to qwords used for hw register */
+	start *= 1024/8;
+	space *= 1024/8;
+	end = start + space - 1;
+
+	sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR);
+	sky2_write32(hw, RB_ADDR(q, RB_START), start);
+	sky2_write32(hw, RB_ADDR(q, RB_END), end);
+	sky2_write32(hw, RB_ADDR(q, RB_WP), start);
+	sky2_write32(hw, RB_ADDR(q, RB_RP), start);
+
+	if (q == Q_R1 || q == Q_R2) {
+		u32 tp = space - space/4;
+
+		/* On receive queue's set the thresholds
+		 * give receiver priority when > 3/4 full
+		 * send pause when down to 2K
+		 */
+		sky2_write32(hw, RB_ADDR(q, RB_RX_UTHP), tp);
+		sky2_write32(hw, RB_ADDR(q, RB_RX_LTHP), space/2);
+
+		tp = space - 2048/8;
+		sky2_write32(hw, RB_ADDR(q, RB_RX_UTPP), tp);
+		sky2_write32(hw, RB_ADDR(q, RB_RX_LTPP), space/4);
+	} else {
+		/* Enable store & forward on Tx queue's because
+		 * Tx FIFO is only 1K on Yukon
+		 */
+		sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_STFWD);
+	}
+
+	sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD);
+	sky2_read8(hw, RB_ADDR(q, RB_CTRL));
+}
+
+/* Setup Bus Memory Interface */
+static void sky2_qset(struct sky2_hw *hw, u16 q)
+{
+	sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_RESET);
+	sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_OPER_INIT);
+	sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_FIFO_OP_ON);
+	sky2_write32(hw, Q_ADDR(q, Q_WM),  BMU_WM_DEFAULT);
+}
+
+/* Setup prefetch unit registers. This is the interface between
+ * hardware and driver list elements
+ */
+static void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr,
+				      u64 addr, u32 last)
+{
+	sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+	sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_CLR);
+	sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_HI), addr >> 32);
+	sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_LO), (u32) addr);
+	sky2_write16(hw, Y2_QADDR(qaddr, PREF_UNIT_LAST_IDX), last);
+	sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_OP_ON);
+
+	sky2_read32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL));
+}
+
+static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2)
+{
+	struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod;
+
+	sky2->tx_prod = RING_NEXT(sky2->tx_prod, TX_RING_SIZE);
+	le->ctrl = 0;
+	return le;
+}
+
+static void tx_init(struct sky2_port *sky2)
+{
+	struct sky2_tx_le *le;
+
+	sky2->tx_prod = sky2->tx_cons = 0;
+
+	le = get_tx_le(sky2);
+	le->addr = 0;
+	le->opcode = OP_ADDR64 | HW_OWNER;
+}
+
+static inline struct tx_ring_info *tx_le_re(struct sky2_port *sky2,
+					    struct sky2_tx_le *le)
+{
+	return sky2->tx_ring + (le - sky2->tx_le);
+}
+
+/* Update chip's next pointer */
+static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx)
+{
+	/* Make sure write' to descriptors are complete before we tell hardware */
+	wmb();
+	sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), idx);
+	DBGIO(PFX "queue %#x idx <- %d\n", q, idx);
+}
+
+
+static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2)
+{
+	struct sky2_rx_le *le = sky2->rx_le + sky2->rx_put;
+
+	sky2->rx_put = RING_NEXT(sky2->rx_put, RX_LE_SIZE);
+	le->ctrl = 0;
+	return le;
+}
+
+/* Build description to hardware for one receive segment */
+static void sky2_rx_add(struct sky2_port *sky2,  u8 op,
+			u32 map, unsigned len)
+{
+	struct sky2_rx_le *le;
+
+	le = sky2_next_rx(sky2);
+	le->addr = cpu_to_le32(map);
+	le->length = cpu_to_le16(len);
+	le->opcode = op | HW_OWNER;
+}
+
+/* Build description to hardware for one possibly fragmented skb */
+static void sky2_rx_submit(struct sky2_port *sky2,
+			   const struct rx_ring_info *re)
+{
+	sky2_rx_add(sky2, OP_PACKET, re->data_addr, sky2->rx_data_size);
+}
+
+
+static void sky2_rx_map_iob(struct pci_device *pdev __unused,
+			    struct rx_ring_info *re,
+			    unsigned size __unused)
+{
+	struct io_buffer *iob = re->iob;
+	re->data_addr = virt_to_bus(iob->data);
+}
+
+/* Diable the checksum offloading.
+ */
+static void rx_set_checksum(struct sky2_port *sky2)
+{
+	struct sky2_rx_le *le = sky2_next_rx(sky2);
+
+	le->addr = cpu_to_le32((ETH_HLEN << 16) | ETH_HLEN);
+	le->ctrl = 0;
+	le->opcode = OP_TCPSTART | HW_OWNER;
+
+	sky2_write32(sky2->hw,
+		     Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+		     BMU_DIS_RX_CHKSUM);
+}
+
+/*
+ * The RX Stop command will not work for Yukon-2 if the BMU does not
+ * reach the end of packet and since we can't make sure that we have
+ * incoming data, we must reset the BMU while it is not doing a DMA
+ * transfer. Since it is possible that the RX path is still active,
+ * the RX RAM buffer will be stopped first, so any possible incoming
+ * data will not trigger a DMA. After the RAM buffer is stopped, the
+ * BMU is polled until any DMA in progress is ended and only then it
+ * will be reset.
+ */
+static void sky2_rx_stop(struct sky2_port *sky2)
+{
+	struct sky2_hw *hw = sky2->hw;
+	unsigned rxq = rxqaddr[sky2->port];
+	int i;
+
+	/* disable the RAM Buffer receive queue */
+	sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_DIS_OP_MD);
+
+	for (i = 0; i < 0xffff; i++)
+		if (sky2_read8(hw, RB_ADDR(rxq, Q_RSL))
+		    == sky2_read8(hw, RB_ADDR(rxq, Q_RL)))
+			goto stopped;
+
+	DBG(PFX "%s: receiver stop failed\n", sky2->netdev->name);
+stopped:
+	sky2_write32(hw, Q_ADDR(rxq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST);
+
+	/* reset the Rx prefetch unit */
+	sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+	wmb();
+}
+
+/* Clean out receive buffer area, assumes receiver hardware stopped */
+static void sky2_rx_clean(struct sky2_port *sky2)
+{
+	unsigned i;
+
+	memset(sky2->rx_le, 0, RX_LE_BYTES);
+	for (i = 0; i < RX_PENDING; i++) {
+		struct rx_ring_info *re = sky2->rx_ring + i;
+
+		if (re->iob) {
+			free_iob(re->iob);
+			re->iob = NULL;
+		}
+	}
+}
+
+/*
+ * Allocate an iob for receiving.
+ */
+static struct io_buffer *sky2_rx_alloc(struct sky2_port *sky2)
+{
+	struct io_buffer *iob;
+
+	iob = alloc_iob(sky2->rx_data_size + ETH_DATA_ALIGN);
+	if (!iob)
+		return NULL;
+
+	/*
+	 * Cards with a RAM buffer hang in the rx FIFO if the
+	 * receive buffer isn't aligned to (Linux module comments say
+	 * 64 bytes, Linux module code says 8 bytes). Since io_buffers
+	 * are always 2kb-aligned under gPXE, just leave it be
+	 * without ETH_DATA_ALIGN in those cases.
+	 *
+	 * XXX This causes unaligned access to the IP header,
+	 * which is undesirable, but it's less undesirable than the
+	 * card hanging.
+	 */
+	if (!(sky2->hw->flags & SKY2_HW_RAM_BUFFER)) {
+		iob_reserve(iob, ETH_DATA_ALIGN);
+	}
+
+	return iob;
+}
+
+static inline void sky2_rx_update(struct sky2_port *sky2, unsigned rxq)
+{
+	sky2_put_idx(sky2->hw, rxq, sky2->rx_put);
+}
+
+/*
+ * Allocate and setup receiver buffer pool.
+ * Normal case this ends up creating one list element for skb
+ * in the receive ring. One element is used for checksum
+ * enable/disable, and one extra to avoid wrap.
+ */
+static int sky2_rx_start(struct sky2_port *sky2)
+{
+	struct sky2_hw *hw = sky2->hw;
+	struct rx_ring_info *re;
+	unsigned rxq = rxqaddr[sky2->port];
+	unsigned i, size, thresh;
+
+	sky2->rx_put = sky2->rx_next = 0;
+	sky2_qset(hw, rxq);
+
+	/* On PCI express lowering the watermark gives better performance */
+	if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP))
+		sky2_write32(hw, Q_ADDR(rxq, Q_WM), BMU_WM_PEX);
+
+	/* These chips have no ram buffer?
+	 * MAC Rx RAM Read is controlled by hardware */
+	if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
+	    (hw->chip_rev == CHIP_REV_YU_EC_U_A1
+	     || hw->chip_rev == CHIP_REV_YU_EC_U_B0))
+		sky2_write32(hw, Q_ADDR(rxq, Q_TEST), F_M_RX_RAM_DIS);
+
+	sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1);
+
+	if (!(hw->flags & SKY2_HW_NEW_LE))
+		rx_set_checksum(sky2);
+
+	/* Space needed for frame data + headers rounded up */
+	size = (ETH_FRAME_LEN + 8) & ~7;
+
+	/* Stopping point for hardware truncation */
+	thresh = (size - 8) / sizeof(u32);
+
+	sky2->rx_data_size = size;
+
+	/* Fill Rx ring */
+	for (i = 0; i < RX_PENDING; i++) {
+		re = sky2->rx_ring + i;
+
+		re->iob = sky2_rx_alloc(sky2);
+		if (!re->iob)
+			goto nomem;
+
+		sky2_rx_map_iob(hw->pdev, re, sky2->rx_data_size);
+		sky2_rx_submit(sky2, re);
+	}
+
+	/*
+	 * The receiver hangs if it receives frames larger than the
+	 * packet buffer. As a workaround, truncate oversize frames, but
+	 * the register is limited to 9 bits, so if you do frames > 2052
+	 * you better get the MTU right!
+	 */
+	if (thresh > 0x1ff)
+		sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_OFF);
+	else {
+		sky2_write16(hw, SK_REG(sky2->port, RX_GMF_TR_THR), thresh);
+		sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON);
+	}
+
+	/* Tell chip about available buffers */
+	sky2_rx_update(sky2, rxq);
+	return 0;
+nomem:
+	sky2_rx_clean(sky2);
+	return -ENOMEM;
+}
+
+/* Free the le and ring buffers */
+static void sky2_free_rings(struct sky2_port *sky2)
+{
+	free_dma(sky2->rx_le, RX_LE_BYTES);
+	free(sky2->rx_ring);
+
+	free_dma(sky2->tx_le, TX_RING_SIZE * sizeof(struct sky2_tx_le));
+	free(sky2->tx_ring);
+
+	sky2->tx_le = NULL;
+	sky2->rx_le = NULL;
+
+	sky2->rx_ring = NULL;
+	sky2->tx_ring = NULL;
+}
+
+/* Bring up network interface. */
+static int sky2_up(struct net_device *dev)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+	struct sky2_hw *hw = sky2->hw;
+	unsigned port = sky2->port;
+	u32 imask, ramsize;
+	int err = -ENOMEM;
+
+	netdev_link_down(dev);
+
+	/* must be power of 2 */
+	sky2->tx_le = malloc_dma(TX_RING_SIZE * sizeof(struct sky2_tx_le), TX_RING_ALIGN);
+	sky2->tx_le_map = virt_to_bus(sky2->tx_le);
+	if (!sky2->tx_le)
+		goto err_out;
+	memset(sky2->tx_le, 0, TX_RING_SIZE * sizeof(struct sky2_tx_le));
+
+	sky2->tx_ring = zalloc(TX_RING_SIZE * sizeof(struct tx_ring_info));
+	if (!sky2->tx_ring)
+		goto err_out;
+
+	tx_init(sky2);
+
+	sky2->rx_le = malloc_dma(RX_LE_BYTES, RX_RING_ALIGN);
+	sky2->rx_le_map = virt_to_bus(sky2->rx_le);
+	if (!sky2->rx_le)
+		goto err_out;
+	memset(sky2->rx_le, 0, RX_LE_BYTES);
+
+	sky2->rx_ring = zalloc(RX_PENDING * sizeof(struct rx_ring_info));
+	if (!sky2->rx_ring)
+		goto err_out;
+
+	sky2_mac_init(hw, port);
+
+	/* Register is number of 4K blocks on internal RAM buffer. */
+	ramsize = sky2_read8(hw, B2_E_0) * 4;
+	if (ramsize > 0) {
+		u32 rxspace;
+
+		hw->flags |= SKY2_HW_RAM_BUFFER;
+		DBG2(PFX "%s: ram buffer %dK\n", dev->name, ramsize);
+		if (ramsize < 16)
+			rxspace = ramsize / 2;
+		else
+			rxspace = 8 + (2*(ramsize - 16))/3;
+
+		sky2_ramset(hw, rxqaddr[port], 0, rxspace);
+		sky2_ramset(hw, txqaddr[port], rxspace, ramsize - rxspace);
+
+		/* Make sure SyncQ is disabled */
+		sky2_write8(hw, RB_ADDR(port == 0 ? Q_XS1 : Q_XS2, RB_CTRL),
+			    RB_RST_SET);
+	}
+
+	sky2_qset(hw, txqaddr[port]);
+
+	/* This is copied from sk98lin 10.0.5.3; no one tells me about erratta's */
+	if (hw->chip_id == CHIP_ID_YUKON_EX && hw->chip_rev == CHIP_REV_YU_EX_B0)
+		sky2_write32(hw, Q_ADDR(txqaddr[port], Q_TEST), F_TX_CHK_AUTO_OFF);
+
+	/* Set almost empty threshold */
+	if (hw->chip_id == CHIP_ID_YUKON_EC_U
+	    && hw->chip_rev == CHIP_REV_YU_EC_U_A0)
+		sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), ECU_TXFF_LEV);
+
+	sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
+			   TX_RING_SIZE - 1);
+
+	err = sky2_rx_start(sky2);
+	if (err)
+		goto err_out;
+
+	/* Enable interrupts from phy/mac for port */
+	imask = sky2_read32(hw, B0_IMSK);
+	imask |= portirq_msk[port];
+	sky2_write32(hw, B0_IMSK, imask);
+
+	DBGIO(PFX "%s: le bases: st %p [%x], rx %p [%x], tx %p [%x]\n",
+	      dev->name, hw->st_le, hw->st_dma, sky2->rx_le, sky2->rx_le_map,
+	      sky2->tx_le, sky2->tx_le_map);
+
+	sky2_set_multicast(dev);
+	return 0;
+
+err_out:
+	sky2_free_rings(sky2);
+	return err;
+}
+
+/* Modular subtraction in ring */
+static inline int tx_dist(unsigned tail, unsigned head)
+{
+	return (head - tail) & (TX_RING_SIZE - 1);
+}
+
+/* Number of list elements available for next tx */
+static inline int tx_avail(const struct sky2_port *sky2)
+{
+	return TX_PENDING - tx_dist(sky2->tx_cons, sky2->tx_prod);
+}
+
+
+/*
+ * Put one packet in ring for transmit.
+ * A single packet can generate multiple list elements, and
+ * the number of ring elements will probably be less than the number
+ * of list elements used.
+ */
+static int sky2_xmit_frame(struct net_device *dev, struct io_buffer *iob)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+	struct sky2_hw *hw = sky2->hw;
+	struct sky2_tx_le *le = NULL;
+	struct tx_ring_info *re;
+	unsigned len;
+	u32 mapping;
+	u8 ctrl;
+
+	if (tx_avail(sky2) < 1)
+		return -EBUSY;
+
+	len = iob_len(iob);
+	mapping = virt_to_bus(iob->data);
+
+	DBGIO(PFX "%s: tx queued, slot %d, len %d\n", dev->name,
+	      sky2->tx_prod, len);
+
+	ctrl = 0;
+
+	le = get_tx_le(sky2);
+	le->addr = cpu_to_le32((u32) mapping);
+	le->length = cpu_to_le16(len);
+	le->ctrl = ctrl;
+	le->opcode = (OP_PACKET | HW_OWNER);
+
+	re = tx_le_re(sky2, le);
+	re->iob = iob;
+
+	le->ctrl |= EOP;
+
+	sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod);
+
+	return 0;
+}
+
+/*
+ * Free ring elements from starting at tx_cons until "done"
+ *
+ * NB: the hardware will tell us about partial completion of multi-part
+ *     buffers so make sure not to free iob too early.
+ */
+static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
+{
+	struct net_device *dev = sky2->netdev;
+	unsigned idx;
+
+	assert(done < TX_RING_SIZE);
+
+	for (idx = sky2->tx_cons; idx != done;
+	     idx = RING_NEXT(idx, TX_RING_SIZE)) {
+		struct sky2_tx_le *le = sky2->tx_le + idx;
+		struct tx_ring_info *re = sky2->tx_ring + idx;
+
+		if (le->ctrl & EOP) {
+			DBGIO(PFX "%s: tx done %d\n", dev->name, idx);
+			netdev_tx_complete(dev, re->iob);
+		}
+	}
+
+	sky2->tx_cons = idx;
+	mb();
+}
+
+/* Cleanup all untransmitted buffers, assume transmitter not running */
+static void sky2_tx_clean(struct net_device *dev)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+
+	sky2_tx_complete(sky2, sky2->tx_prod);
+}
+
+/* Network shutdown */
+static void sky2_down(struct net_device *dev)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+	struct sky2_hw *hw = sky2->hw;
+	unsigned port = sky2->port;
+	u16 ctrl;
+	u32 imask;
+
+	/* Never really got started! */
+	if (!sky2->tx_le)
+		return;
+
+	DBG2(PFX "%s: disabling interface\n", dev->name);
+
+	/* Disable port IRQ */
+	imask = sky2_read32(hw, B0_IMSK);
+	imask &= ~portirq_msk[port];
+	sky2_write32(hw, B0_IMSK, imask);
+
+	sky2_gmac_reset(hw, port);
+
+	/* Stop transmitter */
+	sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_STOP);
+	sky2_read32(hw, Q_ADDR(txqaddr[port], Q_CSR));
+
+	sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL),
+		     RB_RST_SET | RB_DIS_OP_MD);
+
+	ctrl = gma_read16(hw, port, GM_GP_CTRL);
+	ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA);
+	gma_write16(hw, port, GM_GP_CTRL, ctrl);
+
+	sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+
+	/* Workaround shared GMAC reset */
+	if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0
+	      && port == 0 && hw->dev[1]))
+		sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
+
+	/* Disable Force Sync bit and Enable Alloc bit */
+	sky2_write8(hw, SK_REG(port, TXA_CTRL),
+		    TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC);
+
+	/* Stop Interval Timer and Limit Counter of Tx Arbiter */
+	sky2_write32(hw, SK_REG(port, TXA_ITI_INI), 0L);
+	sky2_write32(hw, SK_REG(port, TXA_LIM_INI), 0L);
+
+	/* Reset the PCI FIFO of the async Tx queue */
+	sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR),
+		     BMU_RST_SET | BMU_FIFO_RST);
+
+	/* Reset the Tx prefetch units */
+	sky2_write32(hw, Y2_QADDR(txqaddr[port], PREF_UNIT_CTRL),
+		     PREF_UNIT_RST_SET);
+
+	sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
+
+	sky2_rx_stop(sky2);
+
+	sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
+	sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
+
+	sky2_phy_power_down(hw, port);
+
+	/* turn off LED's */
+	sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
+
+	sky2_tx_clean(dev);
+	sky2_rx_clean(sky2);
+
+	sky2_free_rings(sky2);
+
+	return;
+}
+
+static u16 sky2_phy_speed(const struct sky2_hw *hw, u16 aux)
+{
+	if (hw->flags & SKY2_HW_FIBRE_PHY)
+		return SPEED_1000;
+
+	if (!(hw->flags & SKY2_HW_GIGABIT)) {
+		if (aux & PHY_M_PS_SPEED_100)
+			return SPEED_100;
+		else
+			return SPEED_10;
+	}
+
+	switch (aux & PHY_M_PS_SPEED_MSK) {
+	case PHY_M_PS_SPEED_1000:
+		return SPEED_1000;
+	case PHY_M_PS_SPEED_100:
+		return SPEED_100;
+	default:
+		return SPEED_10;
+	}
+}
+
+static void sky2_link_up(struct sky2_port *sky2)
+{
+	struct sky2_hw *hw = sky2->hw;
+	unsigned port = sky2->port;
+	u16 reg;
+	static const char *fc_name[] = {
+		[FC_NONE]	= "none",
+		[FC_TX]		= "tx",
+		[FC_RX]		= "rx",
+		[FC_BOTH]	= "both",
+	};
+
+	/* enable Rx/Tx */
+	reg = gma_read16(hw, port, GM_GP_CTRL);
+	reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA;
+	gma_write16(hw, port, GM_GP_CTRL, reg);
+
+	gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
+
+	netdev_link_up(sky2->netdev);
+
+	/* Turn on link LED */
+	sky2_write8(hw, SK_REG(port, LNK_LED_REG),
+		    LINKLED_ON | LINKLED_BLINK_OFF | LINKLED_LINKSYNC_OFF);
+
+	DBG(PFX "%s: Link is up at %d Mbps, %s duplex, flow control %s\n",
+	    sky2->netdev->name, sky2->speed,
+	    sky2->duplex == DUPLEX_FULL ? "full" : "half",
+	    fc_name[sky2->flow_status]);
+}
+
+static void sky2_link_down(struct sky2_port *sky2)
+{
+	struct sky2_hw *hw = sky2->hw;
+	unsigned port = sky2->port;
+	u16 reg;
+
+	gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);
+
+	reg = gma_read16(hw, port, GM_GP_CTRL);
+	reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
+	gma_write16(hw, port, GM_GP_CTRL, reg);
+
+	netdev_link_down(sky2->netdev);
+
+	/* Turn on link LED */
+	sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
+
+	DBG(PFX "%s: Link is down.\n", sky2->netdev->name);
+
+	sky2_phy_init(hw, port);
+}
+
+static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
+{
+	struct sky2_hw *hw = sky2->hw;
+	unsigned port = sky2->port;
+	u16 advert, lpa;
+
+	advert = gm_phy_read(hw, port, PHY_MARV_AUNE_ADV);
+	lpa = gm_phy_read(hw, port, PHY_MARV_AUNE_LP);
+	if (lpa & PHY_M_AN_RF) {
+		DBG(PFX "%s: remote fault\n", sky2->netdev->name);
+		return -1;
+	}
+
+	if (!(aux & PHY_M_PS_SPDUP_RES)) {
+		DBG(PFX "%s: speed/duplex mismatch\n", sky2->netdev->name);
+		return -1;
+	}
+
+	sky2->speed = sky2_phy_speed(hw, aux);
+	sky2->duplex = (aux & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF;
+
+	/* Since the pause result bits seem to in different positions on
+	 * different chips. look at registers.
+	 */
+
+	sky2->flow_status = FC_NONE;
+	if (advert & ADVERTISE_PAUSE_CAP) {
+		if (lpa & LPA_PAUSE_CAP)
+			sky2->flow_status = FC_BOTH;
+		else if (advert & ADVERTISE_PAUSE_ASYM)
+			sky2->flow_status = FC_RX;
+	} else if (advert & ADVERTISE_PAUSE_ASYM) {
+		if ((lpa & LPA_PAUSE_CAP) && (lpa & LPA_PAUSE_ASYM))
+			sky2->flow_status = FC_TX;
+	}
+
+	if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000
+	    && !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX))
+		sky2->flow_status = FC_NONE;
+
+	if (sky2->flow_status & FC_TX)
+		sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
+	else
+		sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+
+	return 0;
+}
+
+/* Interrupt from PHY */
+static void sky2_phy_intr(struct sky2_hw *hw, unsigned port)
+{
+	struct net_device *dev = hw->dev[port];
+	struct sky2_port *sky2 = netdev_priv(dev);
+	u16 istatus, phystat;
+
+	istatus = gm_phy_read(hw, port, PHY_MARV_INT_STAT);
+	phystat = gm_phy_read(hw, port, PHY_MARV_PHY_STAT);
+
+	DBGIO(PFX "%s: phy interrupt status 0x%x 0x%x\n",
+	      sky2->netdev->name, istatus, phystat);
+
+	if (sky2->autoneg == AUTONEG_ENABLE && (istatus & PHY_M_IS_AN_COMPL)) {
+		if (sky2_autoneg_done(sky2, phystat) == 0)
+			sky2_link_up(sky2);
+		return;
+	}
+
+	if (istatus & PHY_M_IS_LSP_CHANGE)
+		sky2->speed = sky2_phy_speed(hw, phystat);
+
+	if (istatus & PHY_M_IS_DUP_CHANGE)
+		sky2->duplex =
+		    (phystat & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF;
+
+	if (istatus & PHY_M_IS_LST_CHANGE) {
+		if (phystat & PHY_M_PS_LINK_UP)
+			sky2_link_up(sky2);
+		else
+			sky2_link_down(sky2);
+	}
+}
+
+/* Normal packet - take iob from ring element and put in a new one  */
+static struct io_buffer *receive_new(struct sky2_port *sky2,
+				     struct rx_ring_info *re,
+				     unsigned int length)
+{
+	struct io_buffer *iob, *niob;
+	unsigned hdr_space = sky2->rx_data_size;
+
+	/* Don't be tricky about reusing pages (yet) */
+	niob = sky2_rx_alloc(sky2);
+	if (!niob)
+		return NULL;
+
+	iob = re->iob;
+
+	re->iob = niob;
+	sky2_rx_map_iob(sky2->hw->pdev, re, hdr_space);
+
+	iob_put(iob, length);
+	return iob;
+}
+
+/*
+ * Receive one packet.
+ * For larger packets, get new buffer.
+ */
+static struct io_buffer *sky2_receive(struct net_device *dev,
+				      u16 length, u32 status)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+	struct rx_ring_info *re = sky2->rx_ring + sky2->rx_next;
+	struct io_buffer *iob = NULL;
+	u16 count = (status & GMR_FS_LEN) >> 16;
+
+	DBGIO(PFX "%s: rx slot %d status 0x%x len %d\n",
+	      dev->name, sky2->rx_next, status, length);
+
+	sky2->rx_next = (sky2->rx_next + 1) % RX_PENDING;
+
+	/* This chip has hardware problems that generates bogus status.
+	 * So do only marginal checking and expect higher level protocols
+	 * to handle crap frames.
+	 */
+	if (sky2->hw->chip_id == CHIP_ID_YUKON_FE_P &&
+	    sky2->hw->chip_rev == CHIP_REV_YU_FE2_A0 &&
+	    length == count)
+		goto okay;
+
+	if (status & GMR_FS_ANY_ERR)
+		goto error;
+
+	if (!(status & GMR_FS_RX_OK))
+		goto resubmit;
+
+	/* if length reported by DMA does not match PHY, packet was truncated */
+	if (length != count)
+		goto len_error;
+
+okay:
+	iob = receive_new(sky2, re, length);
+resubmit:
+	sky2_rx_submit(sky2, re);
+
+	return iob;
+
+len_error:
+	/* Truncation of overlength packets
+	   causes PHY length to not match MAC length */
+	DBG2(PFX "%s: rx length error: status %#x length %d\n",
+	     dev->name, status, length);
+
+	/* Pass NULL as iob because we want to keep our iob in the
+	   ring for the next packet. */
+	netdev_rx_err(dev, NULL, -EINVAL);
+	goto resubmit;
+
+error:
+	if (status & GMR_FS_RX_FF_OV) {
+		DBG2(PFX "%s: FIFO overflow error\n", dev->name);
+		netdev_rx_err(dev, NULL, -EBUSY);
+		goto resubmit;
+	}
+
+	DBG2(PFX "%s: rx error, status 0x%x length %d\n",
+	     dev->name, status, length);
+	netdev_rx_err(dev, NULL, -EIO);
+
+	goto resubmit;
+}
+
+/* Transmit complete */
+static inline void sky2_tx_done(struct net_device *dev, u16 last)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+
+	sky2_tx_complete(sky2, last);
+}
+
+/* Process status response ring */
+static void sky2_status_intr(struct sky2_hw *hw, u16 idx)
+{
+	unsigned rx[2] = { 0, 0 };
+
+	rmb();
+	do {
+		struct sky2_port *sky2;
+		struct sky2_status_le *le  = hw->st_le + hw->st_idx;
+		unsigned port;
+		struct net_device *dev;
+		struct io_buffer *iob;
+		u32 status;
+		u16 length;
+		u8 opcode = le->opcode;
+
+		if (!(opcode & HW_OWNER))
+			break;
+
+		port = le->css & CSS_LINK_BIT;
+		dev = hw->dev[port];
+		sky2 = netdev_priv(dev);
+		length = le16_to_cpu(le->length);
+		status = le32_to_cpu(le->status);
+
+		hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE);
+
+		le->opcode = 0;
+		switch (opcode & ~HW_OWNER) {
+		case OP_RXSTAT:
+			++rx[port];
+			iob = sky2_receive(dev, length, status);
+			if (!iob) {
+				netdev_rx_err(dev, NULL, -ENOMEM);
+				break;
+			}
+
+			netdev_rx(dev, iob);
+			break;
+
+		case OP_RXCHKS:
+			DBG2(PFX "status OP_RXCHKS but checksum offloading disabled\n");
+			break;
+
+		case OP_TXINDEXLE:
+			/* TX index reports status for both ports */
+			assert(TX_RING_SIZE <= 0x1000);
+			sky2_tx_done(hw->dev[0], status & 0xfff);
+			if (hw->dev[1])
+				sky2_tx_done(hw->dev[1],
+				     ((status >> 24) & 0xff)
+					     | (u16)(length & 0xf) << 8);
+			break;
+
+		default:
+			DBG(PFX "unknown status opcode 0x%x\n", opcode);
+		}
+	} while (hw->st_idx != idx);
+
+	/* Fully processed status ring so clear irq */
+	sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
+
+	if (rx[0])
+		sky2_rx_update(netdev_priv(hw->dev[0]), Q_R1);
+
+	if (rx[1])
+		sky2_rx_update(netdev_priv(hw->dev[1]), Q_R2);
+}
+
+static void sky2_hw_error(struct sky2_hw *hw, unsigned port, u32 status)
+{
+	struct net_device *dev = hw->dev[port];
+
+	DBGIO(PFX "%s: hw error interrupt status 0x%x\n", dev->name, status);
+
+	if (status & Y2_IS_PAR_RD1) {
+		DBG(PFX "%s: ram data read parity error\n", dev->name);
+		/* Clear IRQ */
+		sky2_write16(hw, RAM_BUFFER(port, B3_RI_CTRL), RI_CLR_RD_PERR);
+	}
+
+	if (status & Y2_IS_PAR_WR1) {
+		DBG(PFX "%s: ram data write parity error\n", dev->name);
+		sky2_write16(hw, RAM_BUFFER(port, B3_RI_CTRL), RI_CLR_WR_PERR);
+	}
+
+	if (status & Y2_IS_PAR_MAC1) {
+		DBG(PFX "%s: MAC parity error\n", dev->name);
+		sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_PE);
+	}
+
+	if (status & Y2_IS_PAR_RX1) {
+		DBG(PFX "%s: RX parity error\n", dev->name);
+		sky2_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), BMU_CLR_IRQ_PAR);
+	}
+
+	if (status & Y2_IS_TCP_TXA1) {
+		DBG(PFX "%s: TCP segmentation error\n", dev->name);
+		sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_CLR_IRQ_TCP);
+	}
+}
+
+static void sky2_hw_intr(struct sky2_hw *hw)
+{
+	u32 status = sky2_read32(hw, B0_HWE_ISRC);
+	u32 hwmsk = sky2_read32(hw, B0_HWE_IMSK);
+
+	status &= hwmsk;
+
+	if (status & Y2_IS_TIST_OV)
+		sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
+
+	if (status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) {
+		u16 pci_err;
+
+		sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+		pci_err = sky2_pci_read16(hw, PCI_STATUS);
+		DBG(PFX "PCI hardware error (0x%x)\n", pci_err);
+
+		sky2_pci_write16(hw, PCI_STATUS,
+				 pci_err | PCI_STATUS_ERROR_BITS);
+		sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+	}
+
+	if (status & Y2_IS_PCI_EXP) {
+		/* PCI-Express uncorrectable Error occurred */
+		u32 err;
+
+		sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+		err = sky2_read32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS);
+		sky2_write32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS,
+			     0xfffffffful);
+		DBG(PFX "PCI-Express error (0x%x)\n", err);
+
+		sky2_read32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS);
+		sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+	}
+
+	if (status & Y2_HWE_L1_MASK)
+		sky2_hw_error(hw, 0, status);
+	status >>= 8;
+	if (status & Y2_HWE_L1_MASK)
+		sky2_hw_error(hw, 1, status);
+}
+
+static void sky2_mac_intr(struct sky2_hw *hw, unsigned port)
+{
+	struct net_device *dev = hw->dev[port];
+	u8 status = sky2_read8(hw, SK_REG(port, GMAC_IRQ_SRC));
+
+	DBGIO(PFX "%s: mac interrupt status 0x%x\n", dev->name, status);
+
+	if (status & GM_IS_RX_CO_OV)
+		gma_read16(hw, port, GM_RX_IRQ_SRC);
+
+	if (status & GM_IS_TX_CO_OV)
+		gma_read16(hw, port, GM_TX_IRQ_SRC);
+
+	if (status & GM_IS_RX_FF_OR) {
+		sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_CLI_RX_FO);
+	}
+
+	if (status & GM_IS_TX_FF_UR) {
+		sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_FU);
+	}
+}
+
+/* This should never happen it is a bug. */
+static void sky2_le_error(struct sky2_hw *hw, unsigned port,
+			  u16 q, unsigned ring_size __unused)
+{
+	struct net_device *dev = hw->dev[port];
+	struct sky2_port *sky2 = netdev_priv(dev);
+	int idx;
+	const u64 *le = (q == Q_R1 || q == Q_R2)
+		? (u64 *) sky2->rx_le : (u64 *) sky2->tx_le;
+
+	idx = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX));
+	DBG(PFX "%s: descriptor error q=%#x get=%d [%llx] last=%d put=%d should be %d\n",
+	    dev->name, (unsigned) q, idx, (unsigned long long) le[idx],
+	    (int) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_LAST_IDX)),
+	    (int) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX)),
+	    le == (u64 *)sky2->rx_le? sky2->rx_put : sky2->tx_prod);
+
+	sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK);
+}
+
+/* Hardware/software error handling */
+static void sky2_err_intr(struct sky2_hw *hw, u32 status)
+{
+	DBG(PFX "error interrupt status=%#x\n", status);
+
+	if (status & Y2_IS_HW_ERR)
+		sky2_hw_intr(hw);
+
+	if (status & Y2_IS_IRQ_MAC1)
+		sky2_mac_intr(hw, 0);
+
+	if (status & Y2_IS_IRQ_MAC2)
+		sky2_mac_intr(hw, 1);
+
+	if (status & Y2_IS_CHK_RX1)
+		sky2_le_error(hw, 0, Q_R1, RX_LE_SIZE);
+
+	if (status & Y2_IS_CHK_RX2)
+		sky2_le_error(hw, 1, Q_R2, RX_LE_SIZE);
+
+	if (status & Y2_IS_CHK_TXA1)
+		sky2_le_error(hw, 0, Q_XA1, TX_RING_SIZE);
+
+	if (status & Y2_IS_CHK_TXA2)
+		sky2_le_error(hw, 1, Q_XA2, TX_RING_SIZE);
+}
+
+static void sky2_poll(struct net_device *dev)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+	struct sky2_hw *hw = sky2->hw;
+	u32 status = sky2_read32(hw, B0_Y2_SP_EISR);
+	u16 idx;
+
+	if (status & Y2_IS_ERROR)
+		sky2_err_intr(hw, status);
+
+	if (status & Y2_IS_IRQ_PHY1)
+		sky2_phy_intr(hw, 0);
+
+	if (status & Y2_IS_IRQ_PHY2)
+		sky2_phy_intr(hw, 1);
+
+	while ((idx = sky2_read16(hw, STAT_PUT_IDX)) != hw->st_idx) {
+		sky2_status_intr(hw, idx);
+	}
+
+	/* Bug/Errata workaround?
+	 * Need to kick the TX irq moderation timer.
+	 */
+	if (sky2_read8(hw, STAT_TX_TIMER_CTRL) == TIM_START) {
+		sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP);
+		sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START);
+	}
+	sky2_read32(hw, B0_Y2_SP_LISR);
+}
+
+/* Chip internal frequency for clock calculations */
+static u32 sky2_mhz(const struct sky2_hw *hw)
+{
+	switch (hw->chip_id) {
+	case CHIP_ID_YUKON_EC:
+	case CHIP_ID_YUKON_EC_U:
+	case CHIP_ID_YUKON_EX:
+	case CHIP_ID_YUKON_SUPR:
+	case CHIP_ID_YUKON_UL_2:
+		return 125;
+
+	case CHIP_ID_YUKON_FE:
+		return 100;
+
+	case CHIP_ID_YUKON_FE_P:
+		return 50;
+
+	case CHIP_ID_YUKON_XL:
+		return 156;
+
+	default:
+		DBG(PFX "unknown chip ID!\n");
+		return 100;	/* bogus */
+	}
+}
+
+static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us)
+{
+	return sky2_mhz(hw) * us;
+}
+
+static inline u32 sky2_clk2us(const struct sky2_hw *hw, u32 clk)
+{
+	return clk / sky2_mhz(hw);
+}
+
+static int sky2_init(struct sky2_hw *hw)
+{
+	u8 t8;
+
+	/* Enable all clocks and check for bad PCI access */
+	sky2_pci_write32(hw, PCI_DEV_REG3, 0);
+
+	sky2_write8(hw, B0_CTST, CS_RST_CLR);
+
+	hw->chip_id = sky2_read8(hw, B2_CHIP_ID);
+	hw->chip_rev = (sky2_read8(hw, B2_MAC_CFG) & CFG_CHIP_R_MSK) >> 4;
+
+	switch(hw->chip_id) {
+	case CHIP_ID_YUKON_XL:
+		hw->flags = SKY2_HW_GIGABIT | SKY2_HW_NEWER_PHY;
+		break;
+
+	case CHIP_ID_YUKON_EC_U:
+		hw->flags = SKY2_HW_GIGABIT
+			| SKY2_HW_NEWER_PHY
+			| SKY2_HW_ADV_POWER_CTL;
+		break;
+
+	case CHIP_ID_YUKON_EX:
+		hw->flags = SKY2_HW_GIGABIT
+			| SKY2_HW_NEWER_PHY
+			| SKY2_HW_NEW_LE
+			| SKY2_HW_ADV_POWER_CTL;
+		break;
+
+	case CHIP_ID_YUKON_EC:
+		/* This rev is really old, and requires untested workarounds */
+		if (hw->chip_rev == CHIP_REV_YU_EC_A1) {
+			DBG(PFX "unsupported revision Yukon-EC rev A1\n");
+			return -EOPNOTSUPP;
+		}
+		hw->flags = SKY2_HW_GIGABIT;
+		break;
+
+	case CHIP_ID_YUKON_FE:
+		break;
+
+	case CHIP_ID_YUKON_FE_P:
+		hw->flags = SKY2_HW_NEWER_PHY
+			| SKY2_HW_NEW_LE
+			| SKY2_HW_AUTO_TX_SUM
+			| SKY2_HW_ADV_POWER_CTL;
+		break;
+
+	case CHIP_ID_YUKON_SUPR:
+		hw->flags = SKY2_HW_GIGABIT
+			| SKY2_HW_NEWER_PHY
+			| SKY2_HW_NEW_LE
+			| SKY2_HW_AUTO_TX_SUM
+			| SKY2_HW_ADV_POWER_CTL;
+		break;
+
+	case CHIP_ID_YUKON_UL_2:
+		hw->flags = SKY2_HW_GIGABIT
+			| SKY2_HW_ADV_POWER_CTL;
+		break;
+
+	default:
+		DBG(PFX "unsupported chip type 0x%x\n", hw->chip_id);
+		return -EOPNOTSUPP;
+	}
+
+	hw->pmd_type = sky2_read8(hw, B2_PMD_TYP);
+	if (hw->pmd_type == 'L' || hw->pmd_type == 'S' || hw->pmd_type == 'P')
+		hw->flags |= SKY2_HW_FIBRE_PHY;
+
+	hw->ports = 1;
+	t8 = sky2_read8(hw, B2_Y2_HW_RES);
+	if ((t8 & CFG_DUAL_MAC_MSK) == CFG_DUAL_MAC_MSK) {
+		if (!(sky2_read8(hw, B2_Y2_CLK_GATE) & Y2_STATUS_LNK2_INAC))
+			++hw->ports;
+	}
+
+	return 0;
+}
+
+static void sky2_reset(struct sky2_hw *hw)
+{
+	u16 status;
+	int i, cap;
+	u32 hwe_mask = Y2_HWE_ALL_MASK;
+
+	/* disable ASF */
+	if (hw->chip_id == CHIP_ID_YUKON_EX) {
+		status = sky2_read16(hw, HCU_CCSR);
+		status &= ~(HCU_CCSR_AHB_RST | HCU_CCSR_CPU_RST_MODE |
+			    HCU_CCSR_UC_STATE_MSK);
+		sky2_write16(hw, HCU_CCSR, status);
+	} else
+		sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET);
+	sky2_write16(hw, B0_CTST, Y2_ASF_DISABLE);
+
+	/* do a SW reset */
+	sky2_write8(hw, B0_CTST, CS_RST_SET);
+	sky2_write8(hw, B0_CTST, CS_RST_CLR);
+
+	/* allow writes to PCI config */
+	sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+
+	/* clear PCI errors, if any */
+	status = sky2_pci_read16(hw, PCI_STATUS);
+	status |= PCI_STATUS_ERROR_BITS;
+	sky2_pci_write16(hw, PCI_STATUS, status);
+
+	sky2_write8(hw, B0_CTST, CS_MRST_CLR);
+
+	cap = pci_find_capability(hw->pdev, PCI_CAP_ID_EXP);
+	if (cap) {
+		sky2_write32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS,
+			     0xfffffffful);
+
+		/* If an error bit is stuck on ignore it */
+		if (sky2_read32(hw, B0_HWE_ISRC) & Y2_IS_PCI_EXP)
+			DBG(PFX "ignoring stuck error report bit\n");
+		else
+			hwe_mask |= Y2_IS_PCI_EXP;
+	}
+
+	sky2_power_on(hw);
+	sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+	for (i = 0; i < hw->ports; i++) {
+		sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET);
+		sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_CLR);
+
+		if (hw->chip_id == CHIP_ID_YUKON_EX ||
+		    hw->chip_id == CHIP_ID_YUKON_SUPR)
+			sky2_write16(hw, SK_REG(i, GMAC_CTRL),
+				     GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON
+				     | GMC_BYP_RETR_ON);
+	}
+
+	/* Clear I2C IRQ noise */
+	sky2_write32(hw, B2_I2C_IRQ, 1);
+
+	/* turn off hardware timer (unused) */
+	sky2_write8(hw, B2_TI_CTRL, TIM_STOP);
+	sky2_write8(hw, B2_TI_CTRL, TIM_CLR_IRQ);
+
+	sky2_write8(hw, B0_Y2LED, LED_STAT_ON);
+
+	/* Turn off descriptor polling */
+	sky2_write32(hw, B28_DPT_CTRL, DPT_STOP);
+
+	/* Turn off receive timestamp */
+	sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_STOP);
+	sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
+
+	/* enable the Tx Arbiters */
+	for (i = 0; i < hw->ports; i++)
+		sky2_write8(hw, SK_REG(i, TXA_CTRL), TXA_ENA_ARB);
+
+	/* Initialize ram interface */
+	for (i = 0; i < hw->ports; i++) {
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_CTRL), RI_RST_CLR);
+
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R1), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA1), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XS1), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_R1), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XA1), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS1), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R2), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA2), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XS2), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_R2), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XA2), SK_RI_TO_53);
+		sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS2), SK_RI_TO_53);
+	}
+
+	sky2_write32(hw, B0_HWE_IMSK, hwe_mask);
+
+	for (i = 0; i < hw->ports; i++)
+		sky2_gmac_reset(hw, i);
+
+	memset(hw->st_le, 0, STATUS_LE_BYTES);
+	hw->st_idx = 0;
+
+	sky2_write32(hw, STAT_CTRL, SC_STAT_RST_SET);
+	sky2_write32(hw, STAT_CTRL, SC_STAT_RST_CLR);
+
+	sky2_write32(hw, STAT_LIST_ADDR_LO, hw->st_dma);
+	sky2_write32(hw, STAT_LIST_ADDR_HI, (u64) hw->st_dma >> 32);
+
+	/* Set the list last index */
+	sky2_write16(hw, STAT_LAST_IDX, STATUS_RING_SIZE - 1);
+
+	sky2_write16(hw, STAT_TX_IDX_TH, 10);
+	sky2_write8(hw, STAT_FIFO_WM, 16);
+
+	/* set Status-FIFO ISR watermark */
+	if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0)
+		sky2_write8(hw, STAT_FIFO_ISR_WM, 4);
+	else
+		sky2_write8(hw, STAT_FIFO_ISR_WM, 16);
+
+	sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 1000));
+	sky2_write32(hw, STAT_ISR_TIMER_INI, sky2_us2clk(hw, 20));
+	sky2_write32(hw, STAT_LEV_TIMER_INI, sky2_us2clk(hw, 100));
+
+	/* enable status unit */
+	sky2_write32(hw, STAT_CTRL, SC_STAT_OP_ON);
+
+	sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START);
+	sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_START);
+	sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
+}
+
+static u32 sky2_supported_modes(const struct sky2_hw *hw)
+{
+	if (sky2_is_copper(hw)) {
+		u32 modes = SUPPORTED_10baseT_Half
+			| SUPPORTED_10baseT_Full
+			| SUPPORTED_100baseT_Half
+			| SUPPORTED_100baseT_Full
+			| SUPPORTED_Autoneg | SUPPORTED_TP;
+
+		if (hw->flags & SKY2_HW_GIGABIT)
+			modes |= SUPPORTED_1000baseT_Half
+				| SUPPORTED_1000baseT_Full;
+		return modes;
+	} else
+		return  SUPPORTED_1000baseT_Half
+			| SUPPORTED_1000baseT_Full
+			| SUPPORTED_Autoneg
+			| SUPPORTED_FIBRE;
+}
+
+static void sky2_set_multicast(struct net_device *dev)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+	struct sky2_hw *hw = sky2->hw;
+	unsigned port = sky2->port;
+	u16 reg;
+	u8 filter[8];
+	int rx_pause;
+
+	rx_pause = (sky2->flow_status == FC_RX || sky2->flow_status == FC_BOTH);
+
+	reg = gma_read16(hw, port, GM_RX_CTRL);
+	reg |= GM_RXCR_UCF_ENA;
+
+	memset(filter, 0xff, sizeof(filter));
+
+	gma_write16(hw, port, GM_MC_ADDR_H1,
+		    (u16) filter[0] | ((u16) filter[1] << 8));
+	gma_write16(hw, port, GM_MC_ADDR_H2,
+		    (u16) filter[2] | ((u16) filter[3] << 8));
+	gma_write16(hw, port, GM_MC_ADDR_H3,
+		    (u16) filter[4] | ((u16) filter[5] << 8));
+	gma_write16(hw, port, GM_MC_ADDR_H4,
+		    (u16) filter[6] | ((u16) filter[7] << 8));
+
+	gma_write16(hw, port, GM_RX_CTRL, reg);
+}
+
+/* Initialize network device */
+static struct net_device *sky2_init_netdev(struct sky2_hw *hw,
+						     unsigned port)
+{
+	struct sky2_port *sky2;
+	struct net_device *dev = alloc_etherdev(sizeof(*sky2));
+
+	if (!dev) {
+		DBG(PFX "etherdev alloc failed\n");
+		return NULL;
+	}
+
+	dev->dev = &hw->pdev->dev;
+
+	sky2 = netdev_priv(dev);
+	sky2->netdev = dev;
+	sky2->hw = hw;
+
+	/* Auto speed and flow control */
+	sky2->autoneg = AUTONEG_ENABLE;
+	sky2->flow_mode = FC_BOTH;
+
+	sky2->duplex = -1;
+	sky2->speed = -1;
+	sky2->advertising = sky2_supported_modes(hw);
+
+	hw->dev[port] = dev;
+
+	sky2->port = port;
+
+	/* read the mac address */
+	memcpy(dev->hw_addr, (void *)(hw->regs + B2_MAC_1 + port * 8), ETH_ALEN);
+
+	return dev;
+}
+
+static void sky2_show_addr(struct net_device *dev)
+{
+	DBG2(PFX "%s: addr %s\n", dev->name, netdev_addr(dev));
+}
+
+#if DBGLVL_MAX
+/* This driver supports yukon2 chipset only */
+static const char *sky2_name(u8 chipid, char *buf, int sz)
+{
+	const char *name[] = {
+		"XL",		/* 0xb3 */
+		"EC Ultra", 	/* 0xb4 */
+		"Extreme",	/* 0xb5 */
+		"EC",		/* 0xb6 */
+		"FE",		/* 0xb7 */
+		"FE+",		/* 0xb8 */
+		"Supreme",	/* 0xb9 */
+		"UL 2",		/* 0xba */
+	};
+
+	if (chipid >= CHIP_ID_YUKON_XL && chipid <= CHIP_ID_YUKON_UL_2)
+		strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
+	else
+		snprintf(buf, sz, "(chip %#x)", chipid);
+	return buf;
+}
+#endif
+
+static void sky2_net_irq(struct net_device *dev, int enable)
+{
+	struct sky2_port *sky2 = netdev_priv(dev);
+	struct sky2_hw *hw = sky2->hw;
+
+	u32 imask = sky2_read32(hw, B0_IMSK);
+	if (enable)
+		imask |= portirq_msk[sky2->port];
+	else
+		imask &= ~portirq_msk[sky2->port];
+	sky2_write32(hw, B0_IMSK, imask);
+}
+
+static struct net_device_operations sky2_operations = {
+	.open     = sky2_up,
+	.close    = sky2_down,
+	.transmit = sky2_xmit_frame,
+	.poll     = sky2_poll,
+	.irq      = sky2_net_irq
+};
+
+static int sky2_probe(struct pci_device *pdev,
+		      const struct pci_device_id *ent __unused)
+{
+	struct net_device *dev;
+	struct sky2_hw *hw;
+	int err;
+	char buf1[16] __unused;	/* only for debugging */
+
+	adjust_pci_device(pdev);
+
+	err = -ENOMEM;
+	hw = zalloc(sizeof(*hw));
+	if (!hw) {
+		DBG(PFX "cannot allocate hardware struct\n");
+		goto err_out;
+	}
+
+	hw->pdev = pdev;
+
+	hw->regs = (unsigned long)ioremap(pci_bar_start(pdev, PCI_BASE_ADDRESS_0), 0x4000);
+	if (!hw->regs) {
+		DBG(PFX "cannot map device registers\n");
+		goto err_out_free_hw;
+	}
+
+	/* ring for status responses */
+	hw->st_le = malloc_dma(STATUS_LE_BYTES, STATUS_RING_ALIGN);
+	if (!hw->st_le)
+		goto err_out_iounmap;
+	hw->st_dma = virt_to_bus(hw->st_le);
+	memset(hw->st_le, 0, STATUS_LE_BYTES);
+
+	err = sky2_init(hw);
+	if (err)
+		goto err_out_iounmap;
+
+#if DBGLVL_MAX
+	DBG2(PFX "Yukon-2 %s chip revision %d\n",
+	     sky2_name(hw->chip_id, buf1, sizeof(buf1)), hw->chip_rev);
+#endif
+
+	sky2_reset(hw);
+
+	dev = sky2_init_netdev(hw, 0);
+	if (!dev) {
+		err = -ENOMEM;
+		goto err_out_free_pci;
+	}
+
+	netdev_init(dev, &sky2_operations);
+
+	err = register_netdev(dev);
+	if (err) {
+		DBG(PFX "cannot register net device\n");
+		goto err_out_free_netdev;
+	}
+
+	sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
+
+	sky2_show_addr(dev);
+
+	if (hw->ports > 1) {
+		struct net_device *dev1;
+
+		dev1 = sky2_init_netdev(hw, 1);
+		if (!dev1)
+			DBG(PFX "allocation for second device failed\n");
+		else if ((err = register_netdev(dev1))) {
+			DBG(PFX "register of second port failed (%d)\n", err);
+			hw->dev[1] = NULL;
+			netdev_nullify(dev1);
+			netdev_put(dev1);
+		} else
+			sky2_show_addr(dev1);
+	}
+
+	pci_set_drvdata(pdev, dev);
+
+	return 0;
+
+err_out_free_netdev:
+	netdev_nullify(dev);
+	netdev_put(dev);
+err_out_free_pci:
+	sky2_write8(hw, B0_CTST, CS_RST_SET);
+	free_dma(hw->st_le, STATUS_LE_BYTES);
+err_out_iounmap:
+	iounmap((void *)hw->regs);
+err_out_free_hw:
+	free(hw);
+err_out:
+	pci_set_drvdata(pdev, NULL);
+	return err;
+}
+
+static void sky2_remove(struct pci_device *pdev)
+{
+	struct sky2_hw *hw = pci_get_drvdata(pdev);
+	int i;
+
+	if (!hw)
+		return;
+
+	for (i = hw->ports-1; i >= 0; --i)
+		unregister_netdev(hw->dev[i]);
+
+	sky2_write32(hw, B0_IMSK, 0);
+
+	sky2_power_aux(hw);
+
+	sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
+	sky2_write8(hw, B0_CTST, CS_RST_SET);
+	sky2_read8(hw, B0_CTST);
+
+	free_dma(hw->st_le, STATUS_LE_BYTES);
+
+	for (i = hw->ports-1; i >= 0; --i) {
+		netdev_nullify(hw->dev[i]);
+		netdev_put(hw->dev[i]);
+	}
+
+	iounmap((void *)hw->regs);
+	free(hw);
+
+	pci_set_drvdata(pdev, NULL);
+}
+
+struct pci_driver sky2_driver __pci_driver = {
+	.ids      = sky2_id_table,
+	.id_count = (sizeof (sky2_id_table) / sizeof (sky2_id_table[0])),
+	.probe    = sky2_probe,
+	.remove   = sky2_remove
+};
diff --git a/gpxe/src/drivers/net/sky2.h b/gpxe/src/drivers/net/sky2.h
new file mode 100644
index 0000000..7963fbe
--- /dev/null
+++ b/gpxe/src/drivers/net/sky2.h
@@ -0,0 +1,2176 @@
+/*
+ * Definitions for the new Marvell Yukon 2 driver.
+ */
+#ifndef _SKY2_H
+#define _SKY2_H
+
+FILE_LICENCE ( GPL2_ONLY );
+
+/* Added for gPXE ------------------ */
+
+/* These were defined in Linux ethtool.h. Their values are arbitrary;
+   they aid only in bookkeeping for the driver. */
+
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+#define DUPLEX_HALF    0x00
+#define DUPLEX_FULL    0x01
+
+#define SPEED_10       10
+#define SPEED_100      100
+#define SPEED_1000     1000
+
+#define ADVERTISED_10baseT_Half        (1 << 0)
+#define ADVERTISED_10baseT_Full        (1 << 1)
+#define ADVERTISED_100baseT_Half       (1 << 2)
+#define ADVERTISED_100baseT_Full       (1 << 3)
+#define ADVERTISED_1000baseT_Half      (1 << 4)
+#define ADVERTISED_1000baseT_Full      (1 << 5)
+
+#define SUPPORTED_10baseT_Half         (1 << 0)
+#define SUPPORTED_10baseT_Full         (1 << 1)
+#define SUPPORTED_100baseT_Half        (1 << 2)
+#define SUPPORTED_100baseT_Full        (1 << 3)
+#define SUPPORTED_1000baseT_Half       (1 << 4)
+#define SUPPORTED_1000baseT_Full       (1 << 5)
+#define SUPPORTED_Autoneg              (1 << 6)
+#define SUPPORTED_TP                   (1 << 7)
+#define SUPPORTED_FIBRE                (1 << 10)
+
+/* ----------------------------------- */
+
+/* PCI config registers */
+enum {
+	PCI_DEV_REG1	= 0x40,
+	PCI_DEV_REG2	= 0x44,
+	PCI_DEV_STATUS  = 0x7c,
+	PCI_DEV_REG3	= 0x80,
+	PCI_DEV_REG4	= 0x84,
+	PCI_DEV_REG5    = 0x88,
+	PCI_CFG_REG_0	= 0x90,
+	PCI_CFG_REG_1	= 0x94,
+};
+
+/* Yukon-2 */
+enum pci_dev_reg_1 {
+	PCI_Y2_PIG_ENA	 = 1<<31, /* Enable Plug-in-Go (YUKON-2) */
+	PCI_Y2_DLL_DIS	 = 1<<30, /* Disable PCI DLL (YUKON-2) */
+	PCI_SW_PWR_ON_RST= 1<<30, /* SW Power on Reset (Yukon-EX) */
+	PCI_Y2_PHY2_COMA = 1<<29, /* Set PHY 2 to Coma Mode (YUKON-2) */
+	PCI_Y2_PHY1_COMA = 1<<28, /* Set PHY 1 to Coma Mode (YUKON-2) */
+	PCI_Y2_PHY2_POWD = 1<<27, /* Set PHY 2 to Power Down (YUKON-2) */
+	PCI_Y2_PHY1_POWD = 1<<26, /* Set PHY 1 to Power Down (YUKON-2) */
+	PCI_Y2_PME_LEGACY= 1<<15, /* PCI Express legacy power management mode */
+
+	PCI_PHY_LNK_TIM_MSK= 3L<<8,/* Bit  9.. 8:	GPHY Link Trigger Timer */
+	PCI_ENA_L1_EVENT = 1<<7, /* Enable PEX L1 Event */
+	PCI_ENA_GPHY_LNK = 1<<6, /* Enable PEX L1 on GPHY Link down */
+	PCI_FORCE_PEX_L1 = 1<<5, /* Force to PEX L1 */
+};
+
+enum pci_dev_reg_2 {
+	PCI_VPD_WR_THR	= 0xffL<<24,	/* Bit 31..24:	VPD Write Threshold */
+	PCI_DEV_SEL	= 0x7fL<<17,	/* Bit 23..17:	EEPROM Device Select */
+	PCI_VPD_ROM_SZ	= 7L<<14,	/* Bit 16..14:	VPD ROM Size	*/
+
+	PCI_PATCH_DIR	= 0xfL<<8,	/* Bit 11.. 8:	Ext Patches dir 3..0 */
+	PCI_EXT_PATCHS	= 0xfL<<4,	/* Bit	7.. 4:	Extended Patches 3..0 */
+	PCI_EN_DUMMY_RD	= 1<<3,		/* Enable Dummy Read */
+	PCI_REV_DESC	= 1<<2,		/* Reverse Desc. Bytes */
+
+	PCI_USEDATA64	= 1<<0,		/* Use 64Bit Data bus ext */
+};
+
+/*	PCI_OUR_REG_4		32 bit	Our Register 4 (Yukon-ECU only) */
+enum pci_dev_reg_4 {
+				/* (Link Training & Status State Machine) */
+	P_PEX_LTSSM_STAT_MSK	= 0x7fL<<25,	/* Bit 31..25:	PEX LTSSM Mask */
+#define P_PEX_LTSSM_STAT(x)	((x << 25) & P_PEX_LTSSM_STAT_MSK)
+	P_PEX_LTSSM_L1_STAT	= 0x34,
+	P_PEX_LTSSM_DET_STAT	= 0x01,
+	P_TIMER_VALUE_MSK	= 0xffL<<16,	/* Bit 23..16:	Timer Value Mask */
+					/* (Active State Power Management) */
+	P_FORCE_ASPM_REQUEST	= 1<<15, /* Force ASPM Request (A1 only) */
+	P_ASPM_GPHY_LINK_DOWN	= 1<<14, /* GPHY Link Down (A1 only) */
+	P_ASPM_INT_FIFO_EMPTY	= 1<<13, /* Internal FIFO Empty (A1 only) */
+	P_ASPM_CLKRUN_REQUEST	= 1<<12, /* CLKRUN Request (A1 only) */
+
+	P_ASPM_FORCE_CLKREQ_ENA	= 1<<4,	/* Force CLKREQ Enable (A1b only) */
+	P_ASPM_CLKREQ_PAD_CTL	= 1<<3,	/* CLKREQ PAD Control (A1 only) */
+	P_ASPM_A1_MODE_SELECT	= 1<<2,	/* A1 Mode Select (A1 only) */
+	P_CLK_GATE_PEX_UNIT_ENA	= 1<<1,	/* Enable Gate PEX Unit Clock */
+	P_CLK_GATE_ROOT_COR_ENA	= 1<<0,	/* Enable Gate Root Core Clock */
+	P_ASPM_CONTROL_MSK	= P_FORCE_ASPM_REQUEST | P_ASPM_GPHY_LINK_DOWN
+				  | P_ASPM_CLKRUN_REQUEST | P_ASPM_INT_FIFO_EMPTY,
+};
+
+/*	PCI_OUR_REG_5		32 bit	Our Register 5 (Yukon-ECU only) */
+enum pci_dev_reg_5 {
+					/* Bit 31..27:	for A3 & later */
+	P_CTL_DIV_CORE_CLK_ENA	= 1<<31, /* Divide Core Clock Enable */
+	P_CTL_SRESET_VMAIN_AV	= 1<<30, /* Soft Reset for Vmain_av De-Glitch */
+	P_CTL_BYPASS_VMAIN_AV	= 1<<29, /* Bypass En. for Vmain_av De-Glitch */
+	P_CTL_TIM_VMAIN_AV_MSK	= 3<<27, /* Bit 28..27: Timer Vmain_av Mask */
+					 /* Bit 26..16: Release Clock on Event */
+	P_REL_PCIE_RST_DE_ASS	= 1<<26, /* PCIe Reset De-Asserted */
+	P_REL_GPHY_REC_PACKET	= 1<<25, /* GPHY Received Packet */
+	P_REL_INT_FIFO_N_EMPTY	= 1<<24, /* Internal FIFO Not Empty */
+	P_REL_MAIN_PWR_AVAIL	= 1<<23, /* Main Power Available */
+	P_REL_CLKRUN_REQ_REL	= 1<<22, /* CLKRUN Request Release */
+	P_REL_PCIE_RESET_ASS	= 1<<21, /* PCIe Reset Asserted */
+	P_REL_PME_ASSERTED	= 1<<20, /* PME Asserted */
+	P_REL_PCIE_EXIT_L1_ST	= 1<<19, /* PCIe Exit L1 State */
+	P_REL_LOADER_NOT_FIN	= 1<<18, /* EPROM Loader Not Finished */
+	P_REL_PCIE_RX_EX_IDLE	= 1<<17, /* PCIe Rx Exit Electrical Idle State */
+	P_REL_GPHY_LINK_UP	= 1<<16, /* GPHY Link Up */
+
+					/* Bit 10.. 0: Mask for Gate Clock */
+	P_GAT_PCIE_RST_ASSERTED	= 1<<10,/* PCIe Reset Asserted */
+	P_GAT_GPHY_N_REC_PACKET	= 1<<9, /* GPHY Not Received Packet */
+	P_GAT_INT_FIFO_EMPTY	= 1<<8, /* Internal FIFO Empty */
+	P_GAT_MAIN_PWR_N_AVAIL	= 1<<7, /* Main Power Not Available */
+	P_GAT_CLKRUN_REQ_REL	= 1<<6, /* CLKRUN Not Requested */
+	P_GAT_PCIE_RESET_ASS	= 1<<5, /* PCIe Reset Asserted */
+	P_GAT_PME_DE_ASSERTED	= 1<<4, /* PME De-Asserted */
+	P_GAT_PCIE_ENTER_L1_ST	= 1<<3, /* PCIe Enter L1 State */
+	P_GAT_LOADER_FINISHED	= 1<<2, /* EPROM Loader Finished */
+	P_GAT_PCIE_RX_EL_IDLE	= 1<<1, /* PCIe Rx Electrical Idle State */
+	P_GAT_GPHY_LINK_DOWN	= 1<<0,	/* GPHY Link Down */
+
+	PCIE_OUR5_EVENT_CLK_D3_SET = P_REL_GPHY_REC_PACKET |
+				     P_REL_INT_FIFO_N_EMPTY |
+				     P_REL_PCIE_EXIT_L1_ST |
+				     P_REL_PCIE_RX_EX_IDLE |
+				     P_GAT_GPHY_N_REC_PACKET |
+				     P_GAT_INT_FIFO_EMPTY |
+				     P_GAT_PCIE_ENTER_L1_ST |
+				     P_GAT_PCIE_RX_EL_IDLE,
+};
+
+#/*	PCI_CFG_REG_1			32 bit	Config Register 1 (Yukon-Ext only) */
+enum pci_cfg_reg1 {
+	P_CF1_DIS_REL_EVT_RST	= 1<<24, /* Dis. Rel. Event during PCIE reset */
+										/* Bit 23..21: Release Clock on Event */
+	P_CF1_REL_LDR_NOT_FIN	= 1<<23, /* EEPROM Loader Not Finished */
+	P_CF1_REL_VMAIN_AVLBL	= 1<<22, /* Vmain available */
+	P_CF1_REL_PCIE_RESET	= 1<<21, /* PCI-E reset */
+										/* Bit 20..18: Gate Clock on Event */
+	P_CF1_GAT_LDR_NOT_FIN	= 1<<20, /* EEPROM Loader Finished */
+	P_CF1_GAT_PCIE_RX_IDLE	= 1<<19, /* PCI-E Rx Electrical idle */
+	P_CF1_GAT_PCIE_RESET	= 1<<18, /* PCI-E Reset */
+	P_CF1_PRST_PHY_CLKREQ	= 1<<17, /* Enable PCI-E rst & PM2PHY gen. CLKREQ */
+	P_CF1_PCIE_RST_CLKREQ	= 1<<16, /* Enable PCI-E rst generate CLKREQ */
+
+	P_CF1_ENA_CFG_LDR_DONE	= 1<<8, /* Enable core level Config loader done */
+
+	P_CF1_ENA_TXBMU_RD_IDLE	= 1<<1, /* Enable TX BMU Read  IDLE for ASPM */
+	P_CF1_ENA_TXBMU_WR_IDLE	= 1<<0, /* Enable TX BMU Write IDLE for ASPM */
+
+	PCIE_CFG1_EVENT_CLK_D3_SET = P_CF1_DIS_REL_EVT_RST |
+					P_CF1_REL_LDR_NOT_FIN |
+					P_CF1_REL_VMAIN_AVLBL |
+					P_CF1_REL_PCIE_RESET |
+					P_CF1_GAT_LDR_NOT_FIN |
+					P_CF1_GAT_PCIE_RESET |
+					P_CF1_PRST_PHY_CLKREQ |
+					P_CF1_ENA_CFG_LDR_DONE |
+					P_CF1_ENA_TXBMU_RD_IDLE |
+					P_CF1_ENA_TXBMU_WR_IDLE,
+};
+
+
+#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \
+			       PCI_STATUS_SIG_SYSTEM_ERROR | \
+			       PCI_STATUS_REC_MASTER_ABORT | \
+			       PCI_STATUS_REC_TARGET_ABORT | \
+			       PCI_STATUS_PARITY)
+
+enum csr_regs {
+	B0_RAP		= 0x0000,
+	B0_CTST		= 0x0004,
+	B0_Y2LED	= 0x0005,
+	B0_POWER_CTRL	= 0x0007,
+	B0_ISRC		= 0x0008,
+	B0_IMSK		= 0x000c,
+	B0_HWE_ISRC	= 0x0010,
+	B0_HWE_IMSK	= 0x0014,
+
+	/* Special ISR registers (Yukon-2 only) */
+	B0_Y2_SP_ISRC2	= 0x001c,
+	B0_Y2_SP_ISRC3	= 0x0020,
+	B0_Y2_SP_EISR	= 0x0024,
+	B0_Y2_SP_LISR	= 0x0028,
+	B0_Y2_SP_ICR	= 0x002c,
+
+	B2_MAC_1	= 0x0100,
+	B2_MAC_2	= 0x0108,
+	B2_MAC_3	= 0x0110,
+	B2_CONN_TYP	= 0x0118,
+	B2_PMD_TYP	= 0x0119,
+	B2_MAC_CFG	= 0x011a,
+	B2_CHIP_ID	= 0x011b,
+	B2_E_0		= 0x011c,
+
+	B2_Y2_CLK_GATE  = 0x011d,
+	B2_Y2_HW_RES	= 0x011e,
+	B2_E_3		= 0x011f,
+	B2_Y2_CLK_CTRL	= 0x0120,
+
+	B2_TI_INI	= 0x0130,
+	B2_TI_VAL	= 0x0134,
+	B2_TI_CTRL	= 0x0138,
+	B2_TI_TEST	= 0x0139,
+
+	B2_TST_CTRL1	= 0x0158,
+	B2_TST_CTRL2	= 0x0159,
+	B2_GP_IO	= 0x015c,
+
+	B2_I2C_CTRL	= 0x0160,
+	B2_I2C_DATA	= 0x0164,
+	B2_I2C_IRQ	= 0x0168,
+	B2_I2C_SW	= 0x016c,
+
+	B3_RAM_ADDR	= 0x0180,
+	B3_RAM_DATA_LO	= 0x0184,
+	B3_RAM_DATA_HI	= 0x0188,
+
+/* RAM Interface Registers */
+/* Yukon-2: use RAM_BUFFER() to access the RAM buffer */
+/*
+ * The HW-Spec. calls this registers Timeout Value 0..11. But this names are
+ * not usable in SW. Please notice these are NOT real timeouts, these are
+ * the number of qWords transferred continuously.
+ */
+#define RAM_BUFFER(port, reg)	(reg | (port <<6))
+
+	B3_RI_WTO_R1	= 0x0190,
+	B3_RI_WTO_XA1	= 0x0191,
+	B3_RI_WTO_XS1	= 0x0192,
+	B3_RI_RTO_R1	= 0x0193,
+	B3_RI_RTO_XA1	= 0x0194,
+	B3_RI_RTO_XS1	= 0x0195,
+	B3_RI_WTO_R2	= 0x0196,
+	B3_RI_WTO_XA2	= 0x0197,
+	B3_RI_WTO_XS2	= 0x0198,
+	B3_RI_RTO_R2	= 0x0199,
+	B3_RI_RTO_XA2	= 0x019a,
+	B3_RI_RTO_XS2	= 0x019b,
+	B3_RI_TO_VAL	= 0x019c,
+	B3_RI_CTRL	= 0x01a0,
+	B3_RI_TEST	= 0x01a2,
+	B3_MA_TOINI_RX1	= 0x01b0,
+	B3_MA_TOINI_RX2	= 0x01b1,
+	B3_MA_TOINI_TX1	= 0x01b2,
+	B3_MA_TOINI_TX2	= 0x01b3,
+	B3_MA_TOVAL_RX1	= 0x01b4,
+	B3_MA_TOVAL_RX2	= 0x01b5,
+	B3_MA_TOVAL_TX1	= 0x01b6,
+	B3_MA_TOVAL_TX2	= 0x01b7,
+	B3_MA_TO_CTRL	= 0x01b8,
+	B3_MA_TO_TEST	= 0x01ba,
+	B3_MA_RCINI_RX1	= 0x01c0,
+	B3_MA_RCINI_RX2	= 0x01c1,
+	B3_MA_RCINI_TX1	= 0x01c2,
+	B3_MA_RCINI_TX2	= 0x01c3,
+	B3_MA_RCVAL_RX1	= 0x01c4,
+	B3_MA_RCVAL_RX2	= 0x01c5,
+	B3_MA_RCVAL_TX1	= 0x01c6,
+	B3_MA_RCVAL_TX2	= 0x01c7,
+	B3_MA_RC_CTRL	= 0x01c8,
+	B3_MA_RC_TEST	= 0x01ca,
+	B3_PA_TOINI_RX1	= 0x01d0,
+	B3_PA_TOINI_RX2	= 0x01d4,
+	B3_PA_TOINI_TX1	= 0x01d8,
+	B3_PA_TOINI_TX2	= 0x01dc,
+	B3_PA_TOVAL_RX1	= 0x01e0,
+	B3_PA_TOVAL_RX2	= 0x01e4,
+	B3_PA_TOVAL_TX1	= 0x01e8,
+	B3_PA_TOVAL_TX2	= 0x01ec,
+	B3_PA_CTRL	= 0x01f0,
+	B3_PA_TEST	= 0x01f2,
+
+	Y2_CFG_SPC	= 0x1c00,	/* PCI config space region */
+	Y2_CFG_AER      = 0x1d00,	/* PCI Advanced Error Report region */
+};
+
+/*	B0_CTST			16 bit	Control/Status register */
+enum {
+	Y2_VMAIN_AVAIL	= 1<<17,/* VMAIN available (YUKON-2 only) */
+	Y2_VAUX_AVAIL	= 1<<16,/* VAUX available (YUKON-2 only) */
+	Y2_HW_WOL_ON	= 1<<15,/* HW WOL On  (Yukon-EC Ultra A1 only) */
+	Y2_HW_WOL_OFF	= 1<<14,/* HW WOL On  (Yukon-EC Ultra A1 only) */
+	Y2_ASF_ENABLE	= 1<<13,/* ASF Unit Enable (YUKON-2 only) */
+	Y2_ASF_DISABLE	= 1<<12,/* ASF Unit Disable (YUKON-2 only) */
+	Y2_CLK_RUN_ENA	= 1<<11,/* CLK_RUN Enable  (YUKON-2 only) */
+	Y2_CLK_RUN_DIS	= 1<<10,/* CLK_RUN Disable (YUKON-2 only) */
+	Y2_LED_STAT_ON	= 1<<9, /* Status LED On  (YUKON-2 only) */
+	Y2_LED_STAT_OFF	= 1<<8, /* Status LED Off (YUKON-2 only) */
+
+	CS_ST_SW_IRQ	= 1<<7,	/* Set IRQ SW Request */
+	CS_CL_SW_IRQ	= 1<<6,	/* Clear IRQ SW Request */
+	CS_STOP_DONE	= 1<<5,	/* Stop Master is finished */
+	CS_STOP_MAST	= 1<<4,	/* Command Bit to stop the master */
+	CS_MRST_CLR	= 1<<3,	/* Clear Master reset	*/
+	CS_MRST_SET	= 1<<2,	/* Set Master reset	*/
+	CS_RST_CLR	= 1<<1,	/* Clear Software reset	*/
+	CS_RST_SET	= 1,	/* Set   Software reset	*/
+};
+
+/*	B0_LED			 8 Bit	LED register */
+enum {
+/* Bit  7.. 2:	reserved */
+	LED_STAT_ON	= 1<<1,	/* Status LED on	*/
+	LED_STAT_OFF	= 1,	/* Status LED off	*/
+};
+
+/*	B0_POWER_CTRL	 8 Bit	Power Control reg (YUKON only) */
+enum {
+	PC_VAUX_ENA	= 1<<7,	/* Switch VAUX Enable  */
+	PC_VAUX_DIS	= 1<<6,	/* Switch VAUX Disable */
+	PC_VCC_ENA	= 1<<5,	/* Switch VCC Enable  */
+	PC_VCC_DIS	= 1<<4,	/* Switch VCC Disable */
+	PC_VAUX_ON	= 1<<3,	/* Switch VAUX On  */
+	PC_VAUX_OFF	= 1<<2,	/* Switch VAUX Off */
+	PC_VCC_ON	= 1<<1,	/* Switch VCC On  */
+	PC_VCC_OFF	= 1<<0,	/* Switch VCC Off */
+};
+
+/*	B2_IRQM_MSK 	32 bit	IRQ Moderation Mask */
+
+/*	B0_Y2_SP_ISRC2	32 bit	Special Interrupt Source Reg 2 */
+/*	B0_Y2_SP_ISRC3	32 bit	Special Interrupt Source Reg 3 */
+/*	B0_Y2_SP_EISR	32 bit	Enter ISR Reg */
+/*	B0_Y2_SP_LISR	32 bit	Leave ISR Reg */
+enum {
+	Y2_IS_HW_ERR	= 1<<31,	/* Interrupt HW Error */
+	Y2_IS_STAT_BMU	= 1<<30,	/* Status BMU Interrupt */
+	Y2_IS_ASF	= 1<<29,	/* ASF subsystem Interrupt */
+
+	Y2_IS_POLL_CHK	= 1<<27,	/* Check IRQ from polling unit */
+	Y2_IS_TWSI_RDY	= 1<<26,	/* IRQ on end of TWSI Tx */
+	Y2_IS_IRQ_SW	= 1<<25,	/* SW forced IRQ	*/
+	Y2_IS_TIMINT	= 1<<24,	/* IRQ from Timer	*/
+
+	Y2_IS_IRQ_PHY2	= 1<<12,	/* Interrupt from PHY 2 */
+	Y2_IS_IRQ_MAC2	= 1<<11,	/* Interrupt from MAC 2 */
+	Y2_IS_CHK_RX2	= 1<<10,	/* Descriptor error Rx 2 */
+	Y2_IS_CHK_TXS2	= 1<<9,		/* Descriptor error TXS 2 */
+	Y2_IS_CHK_TXA2	= 1<<8,		/* Descriptor error TXA 2 */
+
+	Y2_IS_IRQ_PHY1	= 1<<4,		/* Interrupt from PHY 1 */
+	Y2_IS_IRQ_MAC1	= 1<<3,		/* Interrupt from MAC 1 */
+	Y2_IS_CHK_RX1	= 1<<2,		/* Descriptor error Rx 1 */
+	Y2_IS_CHK_TXS1	= 1<<1,		/* Descriptor error TXS 1 */
+	Y2_IS_CHK_TXA1	= 1<<0,		/* Descriptor error TXA 1 */
+
+	Y2_IS_BASE	= Y2_IS_HW_ERR | Y2_IS_STAT_BMU,
+	Y2_IS_PORT_1	= Y2_IS_IRQ_PHY1 | Y2_IS_IRQ_MAC1
+		          | Y2_IS_CHK_TXA1 | Y2_IS_CHK_RX1,
+	Y2_IS_PORT_2	= Y2_IS_IRQ_PHY2 | Y2_IS_IRQ_MAC2
+			  | Y2_IS_CHK_TXA2 | Y2_IS_CHK_RX2,
+	Y2_IS_ERROR     = Y2_IS_HW_ERR |
+			  Y2_IS_IRQ_MAC1 | Y2_IS_CHK_TXA1 | Y2_IS_CHK_RX1 |
+			  Y2_IS_IRQ_MAC2 | Y2_IS_CHK_TXA2 | Y2_IS_CHK_RX2,
+};
+
+/*	B2_IRQM_HWE_MSK	32 bit	IRQ Moderation HW Error Mask */
+enum {
+	IS_ERR_MSK	= 0x00003fff,/* 		All Error bits */
+
+	IS_IRQ_TIST_OV	= 1<<13, /* Time Stamp Timer Overflow (YUKON only) */
+	IS_IRQ_SENSOR	= 1<<12, /* IRQ from Sensor (YUKON only) */
+	IS_IRQ_MST_ERR	= 1<<11, /* IRQ master error detected */
+	IS_IRQ_STAT	= 1<<10, /* IRQ status exception */
+	IS_NO_STAT_M1	= 1<<9,	/* No Rx Status from MAC 1 */
+	IS_NO_STAT_M2	= 1<<8,	/* No Rx Status from MAC 2 */
+	IS_NO_TIST_M1	= 1<<7,	/* No Time Stamp from MAC 1 */
+	IS_NO_TIST_M2	= 1<<6,	/* No Time Stamp from MAC 2 */
+	IS_RAM_RD_PAR	= 1<<5,	/* RAM Read  Parity Error */
+	IS_RAM_WR_PAR	= 1<<4,	/* RAM Write Parity Error */
+	IS_M1_PAR_ERR	= 1<<3,	/* MAC 1 Parity Error */
+	IS_M2_PAR_ERR	= 1<<2,	/* MAC 2 Parity Error */
+	IS_R1_PAR_ERR	= 1<<1,	/* Queue R1 Parity Error */
+	IS_R2_PAR_ERR	= 1<<0,	/* Queue R2 Parity Error */
+};
+
+/* Hardware error interrupt mask for Yukon 2 */
+enum {
+	Y2_IS_TIST_OV	= 1<<29,/* Time Stamp Timer overflow interrupt */
+	Y2_IS_SENSOR	= 1<<28, /* Sensor interrupt */
+	Y2_IS_MST_ERR	= 1<<27, /* Master error interrupt */
+	Y2_IS_IRQ_STAT	= 1<<26, /* Status exception interrupt */
+	Y2_IS_PCI_EXP	= 1<<25, /* PCI-Express interrupt */
+	Y2_IS_PCI_NEXP	= 1<<24, /* PCI-Express error similar to PCI error */
+						/* Link 2 */
+	Y2_IS_PAR_RD2	= 1<<13, /* Read RAM parity error interrupt */
+	Y2_IS_PAR_WR2	= 1<<12, /* Write RAM parity error interrupt */
+	Y2_IS_PAR_MAC2	= 1<<11, /* MAC hardware fault interrupt */
+	Y2_IS_PAR_RX2	= 1<<10, /* Parity Error Rx Queue 2 */
+	Y2_IS_TCP_TXS2	= 1<<9, /* TCP length mismatch sync Tx queue IRQ */
+	Y2_IS_TCP_TXA2	= 1<<8, /* TCP length mismatch async Tx queue IRQ */
+						/* Link 1 */
+	Y2_IS_PAR_RD1	= 1<<5, /* Read RAM parity error interrupt */
+	Y2_IS_PAR_WR1	= 1<<4, /* Write RAM parity error interrupt */
+	Y2_IS_PAR_MAC1	= 1<<3, /* MAC hardware fault interrupt */
+	Y2_IS_PAR_RX1	= 1<<2, /* Parity Error Rx Queue 1 */
+	Y2_IS_TCP_TXS1	= 1<<1, /* TCP length mismatch sync Tx queue IRQ */
+	Y2_IS_TCP_TXA1	= 1<<0, /* TCP length mismatch async Tx queue IRQ */
+
+	Y2_HWE_L1_MASK	= Y2_IS_PAR_RD1 | Y2_IS_PAR_WR1 | Y2_IS_PAR_MAC1 |
+			  Y2_IS_PAR_RX1 | Y2_IS_TCP_TXS1| Y2_IS_TCP_TXA1,
+	Y2_HWE_L2_MASK	= Y2_IS_PAR_RD2 | Y2_IS_PAR_WR2 | Y2_IS_PAR_MAC2 |
+			  Y2_IS_PAR_RX2 | Y2_IS_TCP_TXS2| Y2_IS_TCP_TXA2,
+
+	Y2_HWE_ALL_MASK	= Y2_IS_TIST_OV | Y2_IS_MST_ERR | Y2_IS_IRQ_STAT |
+			  Y2_HWE_L1_MASK | Y2_HWE_L2_MASK,
+};
+
+/*	B28_DPT_CTRL	 8 bit	Descriptor Poll Timer Ctrl Reg */
+enum {
+	DPT_START	= 1<<1,
+	DPT_STOP	= 1<<0,
+};
+
+/*	B2_TST_CTRL1	 8 bit	Test Control Register 1 */
+enum {
+	TST_FRC_DPERR_MR = 1<<7, /* force DATAPERR on MST RD */
+	TST_FRC_DPERR_MW = 1<<6, /* force DATAPERR on MST WR */
+	TST_FRC_DPERR_TR = 1<<5, /* force DATAPERR on TRG RD */
+	TST_FRC_DPERR_TW = 1<<4, /* force DATAPERR on TRG WR */
+	TST_FRC_APERR_M	 = 1<<3, /* force ADDRPERR on MST */
+	TST_FRC_APERR_T	 = 1<<2, /* force ADDRPERR on TRG */
+	TST_CFG_WRITE_ON = 1<<1, /* Enable  Config Reg WR */
+	TST_CFG_WRITE_OFF= 1<<0, /* Disable Config Reg WR */
+};
+
+/* 	B2_GPIO */
+enum {
+	GLB_GPIO_CLK_DEB_ENA = 1<<31,	/* Clock Debug Enable */
+	GLB_GPIO_CLK_DBG_MSK = 0xf<<26, /* Clock Debug */
+
+	GLB_GPIO_INT_RST_D3_DIS = 1<<15, /* Disable Internal Reset After D3 to D0 */
+	GLB_GPIO_LED_PAD_SPEED_UP = 1<<14, /* LED PAD Speed Up */
+	GLB_GPIO_STAT_RACE_DIS	= 1<<13, /* Status Race Disable */
+	GLB_GPIO_TEST_SEL_MSK	= 3<<11, /* Testmode Select */
+	GLB_GPIO_TEST_SEL_BASE	= 1<<11,
+	GLB_GPIO_RAND_ENA	= 1<<10, /* Random Enable */
+	GLB_GPIO_RAND_BIT_1	= 1<<9,  /* Random Bit 1 */
+};
+
+/*	B2_MAC_CFG		 8 bit	MAC Configuration / Chip Revision */
+enum {
+	CFG_CHIP_R_MSK	  = 0xf<<4,	/* Bit 7.. 4: Chip Revision */
+					/* Bit 3.. 2:	reserved */
+	CFG_DIS_M2_CLK	  = 1<<1,	/* Disable Clock for 2nd MAC */
+	CFG_SNG_MAC	  = 1<<0,	/* MAC Config: 0=2 MACs / 1=1 MAC*/
+};
+
+/*	B2_CHIP_ID		 8 bit 	Chip Identification Number */
+enum {
+	CHIP_ID_YUKON_XL   = 0xb3, /* YUKON-2 XL */
+	CHIP_ID_YUKON_EC_U = 0xb4, /* YUKON-2 EC Ultra */
+	CHIP_ID_YUKON_EX   = 0xb5, /* YUKON-2 Extreme */
+	CHIP_ID_YUKON_EC   = 0xb6, /* YUKON-2 EC */
+	CHIP_ID_YUKON_FE   = 0xb7, /* YUKON-2 FE */
+	CHIP_ID_YUKON_FE_P = 0xb8, /* YUKON-2 FE+ */
+	CHIP_ID_YUKON_SUPR = 0xb9, /* YUKON-2 Supreme */
+	CHIP_ID_YUKON_UL_2 = 0xba, /* YUKON-2 Ultra 2 */
+};
+enum yukon_ec_rev {
+	CHIP_REV_YU_EC_A1    = 0,  /* Chip Rev. for Yukon-EC A1/A0 */
+	CHIP_REV_YU_EC_A2    = 1,  /* Chip Rev. for Yukon-EC A2 */
+	CHIP_REV_YU_EC_A3    = 2,  /* Chip Rev. for Yukon-EC A3 */
+};
+enum yukon_ec_u_rev {
+	CHIP_REV_YU_EC_U_A0  = 1,
+	CHIP_REV_YU_EC_U_A1  = 2,
+	CHIP_REV_YU_EC_U_B0  = 3,
+};
+enum yukon_fe_rev {
+	CHIP_REV_YU_FE_A1    = 1,
+	CHIP_REV_YU_FE_A2    = 2,
+};
+enum yukon_fe_p_rev {
+	CHIP_REV_YU_FE2_A0   = 0,
+};
+enum yukon_ex_rev {
+	CHIP_REV_YU_EX_A0    = 1,
+	CHIP_REV_YU_EX_B0    = 2,
+};
+enum yukon_supr_rev {
+	CHIP_REV_YU_SU_A0    = 0,
+};
+
+
+/*	B2_Y2_CLK_GATE	 8 bit	Clock Gating (Yukon-2 only) */
+enum {
+	Y2_STATUS_LNK2_INAC	= 1<<7, /* Status Link 2 inactive (0 = active) */
+	Y2_CLK_GAT_LNK2_DIS	= 1<<6, /* Disable clock gating Link 2 */
+	Y2_COR_CLK_LNK2_DIS	= 1<<5, /* Disable Core clock Link 2 */
+	Y2_PCI_CLK_LNK2_DIS	= 1<<4, /* Disable PCI clock Link 2 */
+	Y2_STATUS_LNK1_INAC	= 1<<3, /* Status Link 1 inactive (0 = active) */
+	Y2_CLK_GAT_LNK1_DIS	= 1<<2, /* Disable clock gating Link 1 */
+	Y2_COR_CLK_LNK1_DIS	= 1<<1, /* Disable Core clock Link 1 */
+	Y2_PCI_CLK_LNK1_DIS	= 1<<0, /* Disable PCI clock Link 1 */
+};
+
+/*	B2_Y2_HW_RES	8 bit	HW Resources (Yukon-2 only) */
+enum {
+	CFG_LED_MODE_MSK	= 7<<2,	/* Bit  4.. 2:	LED Mode Mask */
+	CFG_LINK_2_AVAIL	= 1<<1,	/* Link 2 available */
+	CFG_LINK_1_AVAIL	= 1<<0,	/* Link 1 available */
+};
+#define CFG_LED_MODE(x)		(((x) & CFG_LED_MODE_MSK) >> 2)
+#define CFG_DUAL_MAC_MSK	(CFG_LINK_2_AVAIL | CFG_LINK_1_AVAIL)
+
+
+/* B2_Y2_CLK_CTRL	32 bit	Clock Frequency Control Register (Yukon-2/EC) */
+enum {
+	Y2_CLK_DIV_VAL_MSK	= 0xff<<16,/* Bit 23..16: Clock Divisor Value */
+#define	Y2_CLK_DIV_VAL(x)	(((x)<<16) & Y2_CLK_DIV_VAL_MSK)
+	Y2_CLK_DIV_VAL2_MSK	= 7<<21,   /* Bit 23..21: Clock Divisor Value */
+	Y2_CLK_SELECT2_MSK	= 0x1f<<16,/* Bit 20..16: Clock Select */
+#define Y2_CLK_DIV_VAL_2(x)	(((x)<<21) & Y2_CLK_DIV_VAL2_MSK)
+#define Y2_CLK_SEL_VAL_2(x)	(((x)<<16) & Y2_CLK_SELECT2_MSK)
+	Y2_CLK_DIV_ENA		= 1<<1, /* Enable  Core Clock Division */
+	Y2_CLK_DIV_DIS		= 1<<0,	/* Disable Core Clock Division */
+};
+
+/*	B2_TI_CTRL		 8 bit	Timer control */
+/*	B2_IRQM_CTRL	 8 bit	IRQ Moderation Timer Control */
+enum {
+	TIM_START	= 1<<2,	/* Start Timer */
+	TIM_STOP	= 1<<1,	/* Stop  Timer */
+	TIM_CLR_IRQ	= 1<<0,	/* Clear Timer IRQ (!IRQM) */
+};
+
+/*	B2_TI_TEST		 8 Bit	Timer Test */
+/*	B2_IRQM_TEST	 8 bit	IRQ Moderation Timer Test */
+/*	B28_DPT_TST		 8 bit	Descriptor Poll Timer Test Reg */
+enum {
+	TIM_T_ON	= 1<<2,	/* Test mode on */
+	TIM_T_OFF	= 1<<1,	/* Test mode off */
+	TIM_T_STEP	= 1<<0,	/* Test step */
+};
+
+/*	B3_RAM_ADDR		32 bit	RAM Address, to read or write */
+					/* Bit 31..19:	reserved */
+#define RAM_ADR_RAN	0x0007ffffL	/* Bit 18.. 0:	RAM Address Range */
+/* RAM Interface Registers */
+
+/*	B3_RI_CTRL		16 bit	RAM Interface Control Register */
+enum {
+	RI_CLR_RD_PERR	= 1<<9,	/* Clear IRQ RAM Read Parity Err */
+	RI_CLR_WR_PERR	= 1<<8,	/* Clear IRQ RAM Write Parity Err*/
+
+	RI_RST_CLR	= 1<<1,	/* Clear RAM Interface Reset */
+	RI_RST_SET	= 1<<0,	/* Set   RAM Interface Reset */
+};
+
+#define SK_RI_TO_53	36		/* RAM interface timeout */
+
+
+/* Port related registers FIFO, and Arbiter */
+#define SK_REG(port,reg)	(((port)<<7)+(reg))
+
+/* Transmit Arbiter Registers MAC 1 and 2, use SK_REG() to access */
+/*	TXA_ITI_INI		32 bit	Tx Arb Interval Timer Init Val */
+/*	TXA_ITI_VAL		32 bit	Tx Arb Interval Timer Value */
+/*	TXA_LIM_INI		32 bit	Tx Arb Limit Counter Init Val */
+/*	TXA_LIM_VAL		32 bit	Tx Arb Limit Counter Value */
+
+#define TXA_MAX_VAL	0x00ffffffUL	/* Bit 23.. 0:	Max TXA Timer/Cnt Val */
+
+/*	TXA_CTRL		 8 bit	Tx Arbiter Control Register */
+enum {
+	TXA_ENA_FSYNC	= 1<<7,	/* Enable  force of sync Tx queue */
+	TXA_DIS_FSYNC	= 1<<6,	/* Disable force of sync Tx queue */
+	TXA_ENA_ALLOC	= 1<<5,	/* Enable  alloc of free bandwidth */
+	TXA_DIS_ALLOC	= 1<<4,	/* Disable alloc of free bandwidth */
+	TXA_START_RC	= 1<<3,	/* Start sync Rate Control */
+	TXA_STOP_RC	= 1<<2,	/* Stop  sync Rate Control */
+	TXA_ENA_ARB	= 1<<1,	/* Enable  Tx Arbiter */
+	TXA_DIS_ARB	= 1<<0,	/* Disable Tx Arbiter */
+};
+
+/*
+ *	Bank 4 - 5
+ */
+/* Transmit Arbiter Registers MAC 1 and 2, use SK_REG() to access */
+enum {
+	TXA_ITI_INI	= 0x0200,/* 32 bit	Tx Arb Interval Timer Init Val*/
+	TXA_ITI_VAL	= 0x0204,/* 32 bit	Tx Arb Interval Timer Value */
+	TXA_LIM_INI	= 0x0208,/* 32 bit	Tx Arb Limit Counter Init Val */
+	TXA_LIM_VAL	= 0x020c,/* 32 bit	Tx Arb Limit Counter Value */
+	TXA_CTRL	= 0x0210,/*  8 bit	Tx Arbiter Control Register */
+	TXA_TEST	= 0x0211,/*  8 bit	Tx Arbiter Test Register */
+	TXA_STAT	= 0x0212,/*  8 bit	Tx Arbiter Status Register */
+};
+
+
+enum {
+	B6_EXT_REG	= 0x0300,/* External registers (GENESIS only) */
+	B7_CFG_SPC	= 0x0380,/* copy of the Configuration register */
+	B8_RQ1_REGS	= 0x0400,/* Receive Queue 1 */
+	B8_RQ2_REGS	= 0x0480,/* Receive Queue 2 */
+	B8_TS1_REGS	= 0x0600,/* Transmit sync queue 1 */
+	B8_TA1_REGS	= 0x0680,/* Transmit async queue 1 */
+	B8_TS2_REGS	= 0x0700,/* Transmit sync queue 2 */
+	B8_TA2_REGS	= 0x0780,/* Transmit sync queue 2 */
+	B16_RAM_REGS	= 0x0800,/* RAM Buffer Registers */
+};
+
+/* Queue Register Offsets, use Q_ADDR() to access */
+enum {
+	B8_Q_REGS = 0x0400, /* base of Queue registers */
+	Q_D	= 0x00,	/* 8*32	bit	Current Descriptor */
+	Q_VLAN  = 0x20, /* 16 bit	Current VLAN Tag */
+	Q_DONE	= 0x24,	/* 16 bit	Done Index */
+	Q_AC_L	= 0x28,	/* 32 bit	Current Address Counter Low dWord */
+	Q_AC_H	= 0x2c,	/* 32 bit	Current Address Counter High dWord */
+	Q_BC	= 0x30,	/* 32 bit	Current Byte Counter */
+	Q_CSR	= 0x34,	/* 32 bit	BMU Control/Status Register */
+	Q_TEST	= 0x38,	/* 32 bit	Test/Control Register */
+
+/* Yukon-2 */
+	Q_WM	= 0x40,	/* 16 bit	FIFO Watermark */
+	Q_AL	= 0x42,	/*  8 bit	FIFO Alignment */
+	Q_RSP	= 0x44,	/* 16 bit	FIFO Read Shadow Pointer */
+	Q_RSL	= 0x46,	/*  8 bit	FIFO Read Shadow Level */
+	Q_RP	= 0x48,	/*  8 bit	FIFO Read Pointer */
+	Q_RL	= 0x4a,	/*  8 bit	FIFO Read Level */
+	Q_WP	= 0x4c,	/*  8 bit	FIFO Write Pointer */
+	Q_WSP	= 0x4d,	/*  8 bit	FIFO Write Shadow Pointer */
+	Q_WL	= 0x4e,	/*  8 bit	FIFO Write Level */
+	Q_WSL	= 0x4f,	/*  8 bit	FIFO Write Shadow Level */
+};
+#define Q_ADDR(reg, offs) (B8_Q_REGS + (reg) + (offs))
+
+/*	Q_TEST				32 bit	Test Register */
+enum {
+	/* Transmit */
+	F_TX_CHK_AUTO_OFF = 1<<31, /* Tx checksum auto calc off (Yukon EX) */
+	F_TX_CHK_AUTO_ON  = 1<<30, /* Tx checksum auto calc off (Yukon EX) */
+
+	/* Receive */
+	F_M_RX_RAM_DIS	= 1<<24, /* MAC Rx RAM Read Port disable */
+
+	/* Hardware testbits not used */
+};
+
+/* Queue Prefetch Unit Offsets, use Y2_QADDR() to address (Yukon-2 only)*/
+enum {
+	Y2_B8_PREF_REGS		= 0x0450,
+
+	PREF_UNIT_CTRL		= 0x00,	/* 32 bit	Control register */
+	PREF_UNIT_LAST_IDX	= 0x04,	/* 16 bit	Last Index */
+	PREF_UNIT_ADDR_LO	= 0x08,	/* 32 bit	List start addr, low part */
+	PREF_UNIT_ADDR_HI	= 0x0c,	/* 32 bit	List start addr, high part*/
+	PREF_UNIT_GET_IDX	= 0x10,	/* 16 bit	Get Index */
+	PREF_UNIT_PUT_IDX	= 0x14,	/* 16 bit	Put Index */
+	PREF_UNIT_FIFO_WP	= 0x20,	/*  8 bit	FIFO write pointer */
+	PREF_UNIT_FIFO_RP	= 0x24,	/*  8 bit	FIFO read pointer */
+	PREF_UNIT_FIFO_WM	= 0x28,	/*  8 bit	FIFO watermark */
+	PREF_UNIT_FIFO_LEV	= 0x2c,	/*  8 bit	FIFO level */
+
+	PREF_UNIT_MASK_IDX	= 0x0fff,
+};
+#define Y2_QADDR(q,reg)		(Y2_B8_PREF_REGS + (q) + (reg))
+
+/* RAM Buffer Register Offsets */
+enum {
+
+	RB_START	= 0x00,/* 32 bit	RAM Buffer Start Address */
+	RB_END	= 0x04,/* 32 bit	RAM Buffer End Address */
+	RB_WP	= 0x08,/* 32 bit	RAM Buffer Write Pointer */
+	RB_RP	= 0x0c,/* 32 bit	RAM Buffer Read Pointer */
+	RB_RX_UTPP	= 0x10,/* 32 bit	Rx Upper Threshold, Pause Packet */
+	RB_RX_LTPP	= 0x14,/* 32 bit	Rx Lower Threshold, Pause Packet */
+	RB_RX_UTHP	= 0x18,/* 32 bit	Rx Upper Threshold, High Prio */
+	RB_RX_LTHP	= 0x1c,/* 32 bit	Rx Lower Threshold, High Prio */
+	/* 0x10 - 0x1f:	reserved at Tx RAM Buffer Registers */
+	RB_PC	= 0x20,/* 32 bit	RAM Buffer Packet Counter */
+	RB_LEV	= 0x24,/* 32 bit	RAM Buffer Level Register */
+	RB_CTRL	= 0x28,/* 32 bit	RAM Buffer Control Register */
+	RB_TST1	= 0x29,/*  8 bit	RAM Buffer Test Register 1 */
+	RB_TST2	= 0x2a,/*  8 bit	RAM Buffer Test Register 2 */
+};
+
+/* Receive and Transmit Queues */
+enum {
+	Q_R1	= 0x0000,	/* Receive Queue 1 */
+	Q_R2	= 0x0080,	/* Receive Queue 2 */
+	Q_XS1	= 0x0200,	/* Synchronous Transmit Queue 1 */
+	Q_XA1	= 0x0280,	/* Asynchronous Transmit Queue 1 */
+	Q_XS2	= 0x0300,	/* Synchronous Transmit Queue 2 */
+	Q_XA2	= 0x0380,	/* Asynchronous Transmit Queue 2 */
+};
+
+/* Different PHY Types */
+enum {
+	PHY_ADDR_MARV	= 0,
+};
+
+#define RB_ADDR(offs, queue) ((u16) B16_RAM_REGS + (queue) + (offs))
+
+
+enum {
+	LNK_SYNC_INI	= 0x0c30,/* 32 bit	Link Sync Cnt Init Value */
+	LNK_SYNC_VAL	= 0x0c34,/* 32 bit	Link Sync Cnt Current Value */
+	LNK_SYNC_CTRL	= 0x0c38,/*  8 bit	Link Sync Cnt Control Register */
+	LNK_SYNC_TST	= 0x0c39,/*  8 bit	Link Sync Cnt Test Register */
+
+	LNK_LED_REG	= 0x0c3c,/*  8 bit	Link LED Register */
+
+/* Receive GMAC FIFO (YUKON and Yukon-2) */
+
+	RX_GMF_EA	= 0x0c40,/* 32 bit	Rx GMAC FIFO End Address */
+	RX_GMF_AF_THR	= 0x0c44,/* 32 bit	Rx GMAC FIFO Almost Full Thresh. */
+	RX_GMF_CTRL_T	= 0x0c48,/* 32 bit	Rx GMAC FIFO Control/Test */
+	RX_GMF_FL_MSK	= 0x0c4c,/* 32 bit	Rx GMAC FIFO Flush Mask */
+	RX_GMF_FL_THR	= 0x0c50,/* 32 bit	Rx GMAC FIFO Flush Threshold */
+	RX_GMF_TR_THR	= 0x0c54,/* 32 bit	Rx Truncation Threshold (Yukon-2) */
+	RX_GMF_UP_THR	= 0x0c58,/*  8 bit	Rx Upper Pause Thr (Yukon-EC_U) */
+	RX_GMF_LP_THR	= 0x0c5a,/*  8 bit	Rx Lower Pause Thr (Yukon-EC_U) */
+	RX_GMF_VLAN	= 0x0c5c,/* 32 bit	Rx VLAN Type Register (Yukon-2) */
+	RX_GMF_WP	= 0x0c60,/* 32 bit	Rx GMAC FIFO Write Pointer */
+
+	RX_GMF_WLEV	= 0x0c68,/* 32 bit	Rx GMAC FIFO Write Level */
+
+	RX_GMF_RP	= 0x0c70,/* 32 bit	Rx GMAC FIFO Read Pointer */
+
+	RX_GMF_RLEV	= 0x0c78,/* 32 bit	Rx GMAC FIFO Read Level */
+};
+
+
+/*	Q_BC			32 bit	Current Byte Counter */
+
+/* BMU Control Status Registers */
+/*	B0_R1_CSR		32 bit	BMU Ctrl/Stat Rx Queue 1 */
+/*	B0_R2_CSR		32 bit	BMU Ctrl/Stat Rx Queue 2 */
+/*	B0_XA1_CSR		32 bit	BMU Ctrl/Stat Sync Tx Queue 1 */
+/*	B0_XS1_CSR		32 bit	BMU Ctrl/Stat Async Tx Queue 1 */
+/*	B0_XA2_CSR		32 bit	BMU Ctrl/Stat Sync Tx Queue 2 */
+/*	B0_XS2_CSR		32 bit	BMU Ctrl/Stat Async Tx Queue 2 */
+/*	Q_CSR			32 bit	BMU Control/Status Register */
+
+/* Rx BMU Control / Status Registers (Yukon-2) */
+enum {
+	BMU_IDLE	= 1<<31, /* BMU Idle State */
+	BMU_RX_TCP_PKT	= 1<<30, /* Rx TCP Packet (when RSS Hash enabled) */
+	BMU_RX_IP_PKT	= 1<<29, /* Rx IP  Packet (when RSS Hash enabled) */
+
+	BMU_ENA_RX_RSS_HASH = 1<<15, /* Enable  Rx RSS Hash */
+	BMU_DIS_RX_RSS_HASH = 1<<14, /* Disable Rx RSS Hash */
+	BMU_ENA_RX_CHKSUM = 1<<13, /* Enable  Rx TCP/IP Checksum Check */
+	BMU_DIS_RX_CHKSUM = 1<<12, /* Disable Rx TCP/IP Checksum Check */
+	BMU_CLR_IRQ_PAR	= 1<<11, /* Clear IRQ on Parity errors (Rx) */
+	BMU_CLR_IRQ_TCP	= 1<<11, /* Clear IRQ on TCP segment. error (Tx) */
+	BMU_CLR_IRQ_CHK	= 1<<10, /* Clear IRQ Check */
+	BMU_STOP	= 1<<9, /* Stop  Rx/Tx Queue */
+	BMU_START	= 1<<8, /* Start Rx/Tx Queue */
+	BMU_FIFO_OP_ON	= 1<<7, /* FIFO Operational On */
+	BMU_FIFO_OP_OFF	= 1<<6, /* FIFO Operational Off */
+	BMU_FIFO_ENA	= 1<<5, /* Enable FIFO */
+	BMU_FIFO_RST	= 1<<4, /* Reset  FIFO */
+	BMU_OP_ON	= 1<<3, /* BMU Operational On */
+	BMU_OP_OFF	= 1<<2, /* BMU Operational Off */
+	BMU_RST_CLR	= 1<<1, /* Clear BMU Reset (Enable) */
+	BMU_RST_SET	= 1<<0, /* Set   BMU Reset */
+
+	BMU_CLR_RESET	= BMU_FIFO_RST | BMU_OP_OFF | BMU_RST_CLR,
+	BMU_OPER_INIT	= BMU_CLR_IRQ_PAR | BMU_CLR_IRQ_CHK | BMU_START |
+			  BMU_FIFO_ENA | BMU_OP_ON,
+
+	BMU_WM_DEFAULT = 0x600,
+	BMU_WM_PEX     = 0x80,
+};
+
+/* Tx BMU Control / Status Registers (Yukon-2) */
+								/* Bit 31: same as for Rx */
+enum {
+	BMU_TX_IPIDINCR_ON	= 1<<13, /* Enable  IP ID Increment */
+	BMU_TX_IPIDINCR_OFF	= 1<<12, /* Disable IP ID Increment */
+	BMU_TX_CLR_IRQ_TCP	= 1<<11, /* Clear IRQ on TCP segment length mismatch */
+};
+
+/* Queue Prefetch Unit Offsets, use Y2_QADDR() to address (Yukon-2 only)*/
+/* PREF_UNIT_CTRL	32 bit	Prefetch Control register */
+enum {
+	PREF_UNIT_OP_ON		= 1<<3,	/* prefetch unit operational */
+	PREF_UNIT_OP_OFF	= 1<<2,	/* prefetch unit not operational */
+	PREF_UNIT_RST_CLR	= 1<<1,	/* Clear Prefetch Unit Reset */
+	PREF_UNIT_RST_SET	= 1<<0,	/* Set   Prefetch Unit Reset */
+};
+
+/* RAM Buffer Register Offsets, use RB_ADDR(Queue, Offs) to access */
+/*	RB_START		32 bit	RAM Buffer Start Address */
+/*	RB_END			32 bit	RAM Buffer End Address */
+/*	RB_WP			32 bit	RAM Buffer Write Pointer */
+/*	RB_RP			32 bit	RAM Buffer Read Pointer */
+/*	RB_RX_UTPP		32 bit	Rx Upper Threshold, Pause Pack */
+/*	RB_RX_LTPP		32 bit	Rx Lower Threshold, Pause Pack */
+/*	RB_RX_UTHP		32 bit	Rx Upper Threshold, High Prio */
+/*	RB_RX_LTHP		32 bit	Rx Lower Threshold, High Prio */
+/*	RB_PC			32 bit	RAM Buffer Packet Counter */
+/*	RB_LEV			32 bit	RAM Buffer Level Register */
+
+#define RB_MSK	0x0007ffff	/* Bit 18.. 0:	RAM Buffer Pointer Bits */
+/*	RB_TST2			 8 bit	RAM Buffer Test Register 2 */
+/*	RB_TST1			 8 bit	RAM Buffer Test Register 1 */
+
+/*	RB_CTRL			 8 bit	RAM Buffer Control Register */
+enum {
+	RB_ENA_STFWD	= 1<<5,	/* Enable  Store & Forward */
+	RB_DIS_STFWD	= 1<<4,	/* Disable Store & Forward */
+	RB_ENA_OP_MD	= 1<<3,	/* Enable  Operation Mode */
+	RB_DIS_OP_MD	= 1<<2,	/* Disable Operation Mode */
+	RB_RST_CLR	= 1<<1,	/* Clear RAM Buf STM Reset */
+	RB_RST_SET	= 1<<0,	/* Set   RAM Buf STM Reset */
+};
+
+
+/* Transmit GMAC FIFO (YUKON only) */
+enum {
+	TX_GMF_EA	= 0x0d40,/* 32 bit	Tx GMAC FIFO End Address */
+	TX_GMF_AE_THR	= 0x0d44,/* 32 bit	Tx GMAC FIFO Almost Empty Thresh.*/
+	TX_GMF_CTRL_T	= 0x0d48,/* 32 bit	Tx GMAC FIFO Control/Test */
+
+	TX_GMF_WP	= 0x0d60,/* 32 bit 	Tx GMAC FIFO Write Pointer */
+	TX_GMF_WSP	= 0x0d64,/* 32 bit 	Tx GMAC FIFO Write Shadow Ptr. */
+	TX_GMF_WLEV	= 0x0d68,/* 32 bit 	Tx GMAC FIFO Write Level */
+
+	TX_GMF_RP	= 0x0d70,/* 32 bit 	Tx GMAC FIFO Read Pointer */
+	TX_GMF_RSTP	= 0x0d74,/* 32 bit 	Tx GMAC FIFO Restart Pointer */
+	TX_GMF_RLEV	= 0x0d78,/* 32 bit 	Tx GMAC FIFO Read Level */
+
+	/* Threshold values for Yukon-EC Ultra and Extreme */
+	ECU_AE_THR	= 0x0070, /* Almost Empty Threshold */
+	ECU_TXFF_LEV	= 0x01a0, /* Tx BMU FIFO Level */
+	ECU_JUMBO_WM	= 0x0080, /* Jumbo Mode Watermark */
+};
+
+/* Descriptor Poll Timer Registers */
+enum {
+	B28_DPT_INI	= 0x0e00,/* 24 bit	Descriptor Poll Timer Init Val */
+	B28_DPT_VAL	= 0x0e04,/* 24 bit	Descriptor Poll Timer Curr Val */
+	B28_DPT_CTRL	= 0x0e08,/*  8 bit	Descriptor Poll Timer Ctrl Reg */
+
+	B28_DPT_TST	= 0x0e0a,/*  8 bit	Descriptor Poll Timer Test Reg */
+};
+
+/* Time Stamp Timer Registers (YUKON only) */
+enum {
+	GMAC_TI_ST_VAL	= 0x0e14,/* 32 bit	Time Stamp Timer Curr Val */
+	GMAC_TI_ST_CTRL	= 0x0e18,/*  8 bit	Time Stamp Timer Ctrl Reg */
+	GMAC_TI_ST_TST	= 0x0e1a,/*  8 bit	Time Stamp Timer Test Reg */
+};
+
+/* Polling Unit Registers (Yukon-2 only) */
+enum {
+	POLL_CTRL	= 0x0e20, /* 32 bit	Polling Unit Control Reg */
+	POLL_LAST_IDX	= 0x0e24,/* 16 bit	Polling Unit List Last Index */
+
+	POLL_LIST_ADDR_LO= 0x0e28,/* 32 bit	Poll. List Start Addr (low) */
+	POLL_LIST_ADDR_HI= 0x0e2c,/* 32 bit	Poll. List Start Addr (high) */
+};
+
+enum {
+	SMB_CFG		 = 0x0e40, /* 32 bit	SMBus Config Register */
+	SMB_CSR		 = 0x0e44, /* 32 bit	SMBus Control/Status Register */
+};
+
+enum {
+	CPU_WDOG	 = 0x0e48, /* 32 bit	Watchdog Register  */
+	CPU_CNTR	 = 0x0e4C, /* 32 bit	Counter Register  */
+	CPU_TIM		 = 0x0e50,/* 32 bit	Timer Compare Register  */
+	CPU_AHB_ADDR	 = 0x0e54, /* 32 bit	CPU AHB Debug  Register  */
+	CPU_AHB_WDATA	 = 0x0e58, /* 32 bit	CPU AHB Debug  Register  */
+	CPU_AHB_RDATA	 = 0x0e5C, /* 32 bit	CPU AHB Debug  Register  */
+	HCU_MAP_BASE	 = 0x0e60, /* 32 bit	Reset Mapping Base */
+	CPU_AHB_CTRL	 = 0x0e64, /* 32 bit	CPU AHB Debug  Register  */
+	HCU_CCSR	 = 0x0e68, /* 32 bit	CPU Control and Status Register */
+	HCU_HCSR	 = 0x0e6C, /* 32 bit	Host Control and Status Register */
+};
+
+/* ASF Subsystem Registers (Yukon-2 only) */
+enum {
+	B28_Y2_SMB_CONFIG  = 0x0e40,/* 32 bit	ASF SMBus Config Register */
+	B28_Y2_SMB_CSD_REG = 0x0e44,/* 32 bit	ASF SMB Control/Status/Data */
+	B28_Y2_ASF_IRQ_V_BASE=0x0e60,/* 32 bit	ASF IRQ Vector Base */
+
+	B28_Y2_ASF_STAT_CMD= 0x0e68,/* 32 bit	ASF Status and Command Reg */
+	B28_Y2_ASF_HOST_COM= 0x0e6c,/* 32 bit	ASF Host Communication Reg */
+	B28_Y2_DATA_REG_1  = 0x0e70,/* 32 bit	ASF/Host Data Register 1 */
+	B28_Y2_DATA_REG_2  = 0x0e74,/* 32 bit	ASF/Host Data Register 2 */
+	B28_Y2_DATA_REG_3  = 0x0e78,/* 32 bit	ASF/Host Data Register 3 */
+	B28_Y2_DATA_REG_4  = 0x0e7c,/* 32 bit	ASF/Host Data Register 4 */
+};
+
+/* Status BMU Registers (Yukon-2 only)*/
+enum {
+	STAT_CTRL	= 0x0e80,/* 32 bit	Status BMU Control Reg */
+	STAT_LAST_IDX	= 0x0e84,/* 16 bit	Status BMU Last Index */
+
+	STAT_LIST_ADDR_LO= 0x0e88,/* 32 bit	Status List Start Addr (low) */
+	STAT_LIST_ADDR_HI= 0x0e8c,/* 32 bit	Status List Start Addr (high) */
+	STAT_TXA1_RIDX	= 0x0e90,/* 16 bit	Status TxA1 Report Index Reg */
+	STAT_TXS1_RIDX	= 0x0e92,/* 16 bit	Status TxS1 Report Index Reg */
+	STAT_TXA2_RIDX	= 0x0e94,/* 16 bit	Status TxA2 Report Index Reg */
+	STAT_TXS2_RIDX	= 0x0e96,/* 16 bit	Status TxS2 Report Index Reg */
+	STAT_TX_IDX_TH	= 0x0e98,/* 16 bit	Status Tx Index Threshold Reg */
+	STAT_PUT_IDX	= 0x0e9c,/* 16 bit	Status Put Index Reg */
+
+/* FIFO Control/Status Registers (Yukon-2 only)*/
+	STAT_FIFO_WP	= 0x0ea0,/*  8 bit	Status FIFO Write Pointer Reg */
+	STAT_FIFO_RP	= 0x0ea4,/*  8 bit	Status FIFO Read Pointer Reg */
+	STAT_FIFO_RSP	= 0x0ea6,/*  8 bit	Status FIFO Read Shadow Ptr */
+	STAT_FIFO_LEVEL	= 0x0ea8,/*  8 bit	Status FIFO Level Reg */
+	STAT_FIFO_SHLVL	= 0x0eaa,/*  8 bit	Status FIFO Shadow Level Reg */
+	STAT_FIFO_WM	= 0x0eac,/*  8 bit	Status FIFO Watermark Reg */
+	STAT_FIFO_ISR_WM= 0x0ead,/*  8 bit	Status FIFO ISR Watermark Reg */
+
+/* Level and ISR Timer Registers (Yukon-2 only)*/
+	STAT_LEV_TIMER_INI= 0x0eb0,/* 32 bit	Level Timer Init. Value Reg */
+	STAT_LEV_TIMER_CNT= 0x0eb4,/* 32 bit	Level Timer Counter Reg */
+	STAT_LEV_TIMER_CTRL= 0x0eb8,/*  8 bit	Level Timer Control Reg */
+	STAT_LEV_TIMER_TEST= 0x0eb9,/*  8 bit	Level Timer Test Reg */
+	STAT_TX_TIMER_INI  = 0x0ec0,/* 32 bit	Tx Timer Init. Value Reg */
+	STAT_TX_TIMER_CNT  = 0x0ec4,/* 32 bit	Tx Timer Counter Reg */
+	STAT_TX_TIMER_CTRL = 0x0ec8,/*  8 bit	Tx Timer Control Reg */
+	STAT_TX_TIMER_TEST = 0x0ec9,/*  8 bit	Tx Timer Test Reg */
+	STAT_ISR_TIMER_INI = 0x0ed0,/* 32 bit	ISR Timer Init. Value Reg */
+	STAT_ISR_TIMER_CNT = 0x0ed4,/* 32 bit	ISR Timer Counter Reg */
+	STAT_ISR_TIMER_CTRL= 0x0ed8,/*  8 bit	ISR Timer Control Reg */
+	STAT_ISR_TIMER_TEST= 0x0ed9,/*  8 bit	ISR Timer Test Reg */
+};
+
+enum {
+	LINKLED_OFF 	     = 0x01,
+	LINKLED_ON  	     = 0x02,
+	LINKLED_LINKSYNC_OFF = 0x04,
+	LINKLED_LINKSYNC_ON  = 0x08,
+	LINKLED_BLINK_OFF    = 0x10,
+	LINKLED_BLINK_ON     = 0x20,
+};
+
+/* GMAC and GPHY Control Registers (YUKON only) */
+enum {
+	GMAC_CTRL	= 0x0f00,/* 32 bit	GMAC Control Reg */
+	GPHY_CTRL	= 0x0f04,/* 32 bit	GPHY Control Reg */
+	GMAC_IRQ_SRC	= 0x0f08,/*  8 bit	GMAC Interrupt Source Reg */
+	GMAC_IRQ_MSK	= 0x0f0c,/*  8 bit	GMAC Interrupt Mask Reg */
+	GMAC_LINK_CTRL	= 0x0f10,/* 16 bit	Link Control Reg */
+
+/* Wake-up Frame Pattern Match Control Registers (YUKON only) */
+	WOL_CTRL_STAT	= 0x0f20,/* 16 bit	WOL Control/Status Reg */
+	WOL_MATCH_CTL	= 0x0f22,/*  8 bit	WOL Match Control Reg */
+	WOL_MATCH_RES	= 0x0f23,/*  8 bit	WOL Match Result Reg */
+	WOL_MAC_ADDR	= 0x0f24,/* 32 bit	WOL MAC Address */
+	WOL_PATT_RPTR	= 0x0f2c,/*  8 bit	WOL Pattern Read Pointer */
+
+/* WOL Pattern Length Registers (YUKON only) */
+	WOL_PATT_LEN_LO	= 0x0f30,/* 32 bit	WOL Pattern Length 3..0 */
+	WOL_PATT_LEN_HI	= 0x0f34,/* 24 bit	WOL Pattern Length 6..4 */
+
+/* WOL Pattern Counter Registers (YUKON only) */
+	WOL_PATT_CNT_0	= 0x0f38,/* 32 bit	WOL Pattern Counter 3..0 */
+	WOL_PATT_CNT_4	= 0x0f3c,/* 24 bit	WOL Pattern Counter 6..4 */
+};
+#define WOL_REGS(port, x)	(x + (port)*0x80)
+
+enum {
+	WOL_PATT_RAM_1	= 0x1000,/*  WOL Pattern RAM Link 1 */
+	WOL_PATT_RAM_2	= 0x1400,/*  WOL Pattern RAM Link 2 */
+};
+#define WOL_PATT_RAM_BASE(port)	(WOL_PATT_RAM_1 + (port)*0x400)
+
+enum {
+	BASE_GMAC_1	= 0x2800,/* GMAC 1 registers */
+	BASE_GMAC_2	= 0x3800,/* GMAC 2 registers */
+};
+
+/*
+ * Marvel-PHY Registers, indirect addressed over GMAC
+ */
+enum {
+	PHY_MARV_CTRL		= 0x00,/* 16 bit r/w	PHY Control Register */
+	PHY_MARV_STAT		= 0x01,/* 16 bit r/o	PHY Status Register */
+	PHY_MARV_ID0		= 0x02,/* 16 bit r/o	PHY ID0 Register */
+	PHY_MARV_ID1		= 0x03,/* 16 bit r/o	PHY ID1 Register */
+	PHY_MARV_AUNE_ADV	= 0x04,/* 16 bit r/w	Auto-Neg. Advertisement */
+	PHY_MARV_AUNE_LP	= 0x05,/* 16 bit r/o	Link Part Ability Reg */
+	PHY_MARV_AUNE_EXP	= 0x06,/* 16 bit r/o	Auto-Neg. Expansion Reg */
+	PHY_MARV_NEPG		= 0x07,/* 16 bit r/w	Next Page Register */
+	PHY_MARV_NEPG_LP	= 0x08,/* 16 bit r/o	Next Page Link Partner */
+	/* Marvel-specific registers */
+	PHY_MARV_1000T_CTRL	= 0x09,/* 16 bit r/w	1000Base-T Control Reg */
+	PHY_MARV_1000T_STAT	= 0x0a,/* 16 bit r/o	1000Base-T Status Reg */
+	PHY_MARV_EXT_STAT	= 0x0f,/* 16 bit r/o	Extended Status Reg */
+	PHY_MARV_PHY_CTRL	= 0x10,/* 16 bit r/w	PHY Specific Ctrl Reg */
+	PHY_MARV_PHY_STAT	= 0x11,/* 16 bit r/o	PHY Specific Stat Reg */
+	PHY_MARV_INT_MASK	= 0x12,/* 16 bit r/w	Interrupt Mask Reg */
+	PHY_MARV_INT_STAT	= 0x13,/* 16 bit r/o	Interrupt Status Reg */
+	PHY_MARV_EXT_CTRL	= 0x14,/* 16 bit r/w	Ext. PHY Specific Ctrl */
+	PHY_MARV_RXE_CNT	= 0x15,/* 16 bit r/w	Receive Error Counter */
+	PHY_MARV_EXT_ADR	= 0x16,/* 16 bit r/w	Ext. Ad. for Cable Diag. */
+	PHY_MARV_PORT_IRQ	= 0x17,/* 16 bit r/o	Port 0 IRQ (88E1111 only) */
+	PHY_MARV_LED_CTRL	= 0x18,/* 16 bit r/w	LED Control Reg */
+	PHY_MARV_LED_OVER	= 0x19,/* 16 bit r/w	Manual LED Override Reg */
+	PHY_MARV_EXT_CTRL_2	= 0x1a,/* 16 bit r/w	Ext. PHY Specific Ctrl 2 */
+	PHY_MARV_EXT_P_STAT	= 0x1b,/* 16 bit r/w	Ext. PHY Spec. Stat Reg */
+	PHY_MARV_CABLE_DIAG	= 0x1c,/* 16 bit r/o	Cable Diagnostic Reg */
+	PHY_MARV_PAGE_ADDR	= 0x1d,/* 16 bit r/w	Extended Page Address Reg */
+	PHY_MARV_PAGE_DATA	= 0x1e,/* 16 bit r/w	Extended Page Data Reg */
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+	PHY_MARV_FE_LED_PAR	= 0x16,/* 16 bit r/w	LED Parallel Select Reg. */
+	PHY_MARV_FE_LED_SER	= 0x17,/* 16 bit r/w	LED Stream Select S. LED */
+	PHY_MARV_FE_VCT_TX	= 0x1a,/* 16 bit r/w	VCT Reg. for TXP/N Pins */
+	PHY_MARV_FE_VCT_RX	= 0x1b,/* 16 bit r/o	VCT Reg. for RXP/N Pins */
+	PHY_MARV_FE_SPEC_2	= 0x1c,/* 16 bit r/w	Specific Control Reg. 2 */
+};
+
+enum {
+	PHY_CT_RESET	= 1<<15, /* Bit 15: (sc)	clear all PHY related regs */
+	PHY_CT_LOOP	= 1<<14, /* Bit 14:	enable Loopback over PHY */
+	PHY_CT_SPS_LSB	= 1<<13, /* Bit 13:	Speed select, lower bit */
+	PHY_CT_ANE	= 1<<12, /* Bit 12:	Auto-Negotiation Enabled */
+	PHY_CT_PDOWN	= 1<<11, /* Bit 11:	Power Down Mode */
+	PHY_CT_ISOL	= 1<<10, /* Bit 10:	Isolate Mode */
+	PHY_CT_RE_CFG	= 1<<9, /* Bit  9:	(sc) Restart Auto-Negotiation */
+	PHY_CT_DUP_MD	= 1<<8, /* Bit  8:	Duplex Mode */
+	PHY_CT_COL_TST	= 1<<7, /* Bit  7:	Collision Test enabled */
+	PHY_CT_SPS_MSB	= 1<<6, /* Bit  6:	Speed select, upper bit */
+};
+
+enum {
+	PHY_CT_SP1000	= PHY_CT_SPS_MSB, /* enable speed of 1000 Mbps */
+	PHY_CT_SP100	= PHY_CT_SPS_LSB, /* enable speed of  100 Mbps */
+	PHY_CT_SP10	= 0,		  /* enable speed of   10 Mbps */
+};
+
+enum {
+	PHY_ST_EXT_ST	= 1<<8, /* Bit  8:	Extended Status Present */
+
+	PHY_ST_PRE_SUP	= 1<<6, /* Bit  6:	Preamble Suppression */
+	PHY_ST_AN_OVER	= 1<<5, /* Bit  5:	Auto-Negotiation Over */
+	PHY_ST_REM_FLT	= 1<<4, /* Bit  4:	Remote Fault Condition Occured */
+	PHY_ST_AN_CAP	= 1<<3, /* Bit  3:	Auto-Negotiation Capability */
+	PHY_ST_LSYNC	= 1<<2, /* Bit  2:	Link Synchronized */
+	PHY_ST_JAB_DET	= 1<<1, /* Bit  1:	Jabber Detected */
+	PHY_ST_EXT_REG	= 1<<0, /* Bit  0:	Extended Register available */
+};
+
+enum {
+	PHY_I1_OUI_MSK	= 0x3f<<10, /* Bit 15..10:	Organization Unique ID */
+	PHY_I1_MOD_NUM	= 0x3f<<4, /* Bit  9.. 4:	Model Number */
+	PHY_I1_REV_MSK	= 0xf, /* Bit  3.. 0:	Revision Number */
+};
+
+/* different Marvell PHY Ids */
+enum {
+	PHY_MARV_ID0_VAL= 0x0141, /* Marvell Unique Identifier */
+
+	PHY_BCOM_ID1_A1	= 0x6041,
+	PHY_BCOM_ID1_B2	= 0x6043,
+	PHY_BCOM_ID1_C0	= 0x6044,
+	PHY_BCOM_ID1_C5	= 0x6047,
+
+	PHY_MARV_ID1_B0	= 0x0C23, /* Yukon 	(PHY 88E1011) */
+	PHY_MARV_ID1_B2	= 0x0C25, /* Yukon-Plus (PHY 88E1011) */
+	PHY_MARV_ID1_C2	= 0x0CC2, /* Yukon-EC	(PHY 88E1111) */
+	PHY_MARV_ID1_Y2	= 0x0C91, /* Yukon-2	(PHY 88E1112) */
+	PHY_MARV_ID1_FE = 0x0C83, /* Yukon-FE   (PHY 88E3082 Rev.A1) */
+	PHY_MARV_ID1_ECU= 0x0CB0, /* Yukon-ECU  (PHY 88E1149 Rev.B2?) */
+};
+
+/* Advertisement register bits */
+enum {
+	PHY_AN_NXT_PG	= 1<<15, /* Bit 15:	Request Next Page */
+	PHY_AN_ACK	= 1<<14, /* Bit 14:	(ro) Acknowledge Received */
+	PHY_AN_RF	= 1<<13, /* Bit 13:	Remote Fault Bits */
+
+	PHY_AN_PAUSE_ASYM = 1<<11,/* Bit 11:	Try for asymmetric */
+	PHY_AN_PAUSE_CAP = 1<<10, /* Bit 10:	Try for pause */
+	PHY_AN_100BASE4	= 1<<9, /* Bit 9:	Try for 100mbps 4k packets */
+	PHY_AN_100FULL	= 1<<8, /* Bit 8:	Try for 100mbps full-duplex */
+	PHY_AN_100HALF	= 1<<7, /* Bit 7:	Try for 100mbps half-duplex */
+	PHY_AN_10FULL	= 1<<6, /* Bit 6:	Try for 10mbps full-duplex */
+	PHY_AN_10HALF	= 1<<5, /* Bit 5:	Try for 10mbps half-duplex */
+	PHY_AN_CSMA	= 1<<0, /* Bit 0:	Only selector supported */
+	PHY_AN_SEL	= 0x1f, /* Bit 4..0:	Selector Field, 00001=Ethernet*/
+	PHY_AN_FULL	= PHY_AN_100FULL | PHY_AN_10FULL | PHY_AN_CSMA,
+	PHY_AN_ALL	= PHY_AN_10HALF | PHY_AN_10FULL |
+			  PHY_AN_100HALF | PHY_AN_100FULL,
+};
+
+/*****  PHY_BCOM_1000T_STAT	16 bit r/o	1000Base-T Status Reg *****/
+/*****  PHY_MARV_1000T_STAT	16 bit r/o	1000Base-T Status Reg *****/
+enum {
+	PHY_B_1000S_MSF	= 1<<15, /* Bit 15:	Master/Slave Fault */
+	PHY_B_1000S_MSR	= 1<<14, /* Bit 14:	Master/Slave Result */
+	PHY_B_1000S_LRS	= 1<<13, /* Bit 13:	Local Receiver Status */
+	PHY_B_1000S_RRS	= 1<<12, /* Bit 12:	Remote Receiver Status */
+	PHY_B_1000S_LP_FD	= 1<<11, /* Bit 11:	Link Partner can FD */
+	PHY_B_1000S_LP_HD	= 1<<10, /* Bit 10:	Link Partner can HD */
+									/* Bit  9..8:	reserved */
+	PHY_B_1000S_IEC	= 0xff, /* Bit  7..0:	Idle Error Count */
+};
+
+/** Marvell-Specific */
+enum {
+	PHY_M_AN_NXT_PG	= 1<<15, /* Request Next Page */
+	PHY_M_AN_ACK	= 1<<14, /* (ro)	Acknowledge Received */
+	PHY_M_AN_RF	= 1<<13, /* Remote Fault */
+
+	PHY_M_AN_ASP	= 1<<11, /* Asymmetric Pause */
+	PHY_M_AN_PC	= 1<<10, /* MAC Pause implemented */
+	PHY_M_AN_100_T4	= 1<<9, /* Not cap. 100Base-T4 (always 0) */
+	PHY_M_AN_100_FD	= 1<<8, /* Advertise 100Base-TX Full Duplex */
+	PHY_M_AN_100_HD	= 1<<7, /* Advertise 100Base-TX Half Duplex */
+	PHY_M_AN_10_FD	= 1<<6, /* Advertise 10Base-TX Full Duplex */
+	PHY_M_AN_10_HD	= 1<<5, /* Advertise 10Base-TX Half Duplex */
+	PHY_M_AN_SEL_MSK =0x1f<<4,	/* Bit  4.. 0: Selector Field Mask */
+};
+
+/* special defines for FIBER (88E1011S only) */
+enum {
+	PHY_M_AN_ASP_X	= 1<<8, /* Asymmetric Pause */
+	PHY_M_AN_PC_X	= 1<<7, /* MAC Pause implemented */
+	PHY_M_AN_1000X_AHD	= 1<<6, /* Advertise 10000Base-X Half Duplex */
+	PHY_M_AN_1000X_AFD	= 1<<5, /* Advertise 10000Base-X Full Duplex */
+};
+
+/* Pause Bits (PHY_M_AN_ASP_X and PHY_M_AN_PC_X) encoding */
+enum {
+	PHY_M_P_NO_PAUSE_X	= 0<<7,/* Bit  8.. 7:	no Pause Mode */
+	PHY_M_P_SYM_MD_X	= 1<<7, /* Bit  8.. 7:	symmetric Pause Mode */
+	PHY_M_P_ASYM_MD_X	= 2<<7,/* Bit  8.. 7:	asymmetric Pause Mode */
+	PHY_M_P_BOTH_MD_X	= 3<<7,/* Bit  8.. 7:	both Pause Mode */
+};
+
+/*****  PHY_MARV_1000T_CTRL	16 bit r/w	1000Base-T Control Reg *****/
+enum {
+	PHY_M_1000C_TEST	= 7<<13,/* Bit 15..13:	Test Modes */
+	PHY_M_1000C_MSE	= 1<<12, /* Manual Master/Slave Enable */
+	PHY_M_1000C_MSC	= 1<<11, /* M/S Configuration (1=Master) */
+	PHY_M_1000C_MPD	= 1<<10, /* Multi-Port Device */
+	PHY_M_1000C_AFD	= 1<<9, /* Advertise Full Duplex */
+	PHY_M_1000C_AHD	= 1<<8, /* Advertise Half Duplex */
+};
+
+/*****  PHY_MARV_PHY_CTRL	16 bit r/w	PHY Specific Ctrl Reg *****/
+enum {
+	PHY_M_PC_TX_FFD_MSK	= 3<<14,/* Bit 15..14: Tx FIFO Depth Mask */
+	PHY_M_PC_RX_FFD_MSK	= 3<<12,/* Bit 13..12: Rx FIFO Depth Mask */
+	PHY_M_PC_ASS_CRS_TX	= 1<<11, /* Assert CRS on Transmit */
+	PHY_M_PC_FL_GOOD	= 1<<10, /* Force Link Good */
+	PHY_M_PC_EN_DET_MSK	= 3<<8,/* Bit  9.. 8: Energy Detect Mask */
+	PHY_M_PC_ENA_EXT_D	= 1<<7, /* Enable Ext. Distance (10BT) */
+	PHY_M_PC_MDIX_MSK	= 3<<5,/* Bit  6.. 5: MDI/MDIX Config. Mask */
+	PHY_M_PC_DIS_125CLK	= 1<<4, /* Disable 125 CLK */
+	PHY_M_PC_MAC_POW_UP	= 1<<3, /* MAC Power up */
+	PHY_M_PC_SQE_T_ENA	= 1<<2, /* SQE Test Enabled */
+	PHY_M_PC_POL_R_DIS	= 1<<1, /* Polarity Reversal Disabled */
+	PHY_M_PC_DIS_JABBER	= 1<<0, /* Disable Jabber */
+};
+
+enum {
+	PHY_M_PC_EN_DET		= 2<<8,	/* Energy Detect (Mode 1) */
+	PHY_M_PC_EN_DET_PLUS	= 3<<8, /* Energy Detect Plus (Mode 2) */
+};
+
+#define PHY_M_PC_MDI_XMODE(x)	(((u16)(x)<<5) & PHY_M_PC_MDIX_MSK)
+
+enum {
+	PHY_M_PC_MAN_MDI	= 0, /* 00 = Manual MDI configuration */
+	PHY_M_PC_MAN_MDIX	= 1, /* 01 = Manual MDIX configuration */
+	PHY_M_PC_ENA_AUTO	= 3, /* 11 = Enable Automatic Crossover */
+};
+
+/* for Yukon-EC Ultra Gigabit Ethernet PHY (88E1149 only) */
+enum {
+	PHY_M_PC_COP_TX_DIS	= 1<<3, /* Copper Transmitter Disable */
+	PHY_M_PC_POW_D_ENA	= 1<<2,	/* Power Down Enable */
+};
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+enum {
+	PHY_M_PC_ENA_DTE_DT	= 1<<15, /* Enable Data Terminal Equ. (DTE) Detect */
+	PHY_M_PC_ENA_ENE_DT	= 1<<14, /* Enable Energy Detect (sense & pulse) */
+	PHY_M_PC_DIS_NLP_CK	= 1<<13, /* Disable Normal Link Puls (NLP) Check */
+	PHY_M_PC_ENA_LIP_NP	= 1<<12, /* Enable Link Partner Next Page Reg. */
+	PHY_M_PC_DIS_NLP_GN	= 1<<11, /* Disable Normal Link Puls Generation */
+
+	PHY_M_PC_DIS_SCRAMB	= 1<<9, /* Disable Scrambler */
+	PHY_M_PC_DIS_FEFI	= 1<<8, /* Disable Far End Fault Indic. (FEFI) */
+
+	PHY_M_PC_SH_TP_SEL	= 1<<6, /* Shielded Twisted Pair Select */
+	PHY_M_PC_RX_FD_MSK	= 3<<2,/* Bit  3.. 2: Rx FIFO Depth Mask */
+};
+
+/*****  PHY_MARV_PHY_STAT	16 bit r/o	PHY Specific Status Reg *****/
+enum {
+	PHY_M_PS_SPEED_MSK	= 3<<14, /* Bit 15..14: Speed Mask */
+	PHY_M_PS_SPEED_1000	= 1<<15, /*		10 = 1000 Mbps */
+	PHY_M_PS_SPEED_100	= 1<<14, /*		01 =  100 Mbps */
+	PHY_M_PS_SPEED_10	= 0,	 /*		00 =   10 Mbps */
+	PHY_M_PS_FULL_DUP	= 1<<13, /* Full Duplex */
+	PHY_M_PS_PAGE_REC	= 1<<12, /* Page Received */
+	PHY_M_PS_SPDUP_RES	= 1<<11, /* Speed & Duplex Resolved */
+	PHY_M_PS_LINK_UP	= 1<<10, /* Link Up */
+	PHY_M_PS_CABLE_MSK	= 7<<7,  /* Bit  9.. 7: Cable Length Mask */
+	PHY_M_PS_MDI_X_STAT	= 1<<6,  /* MDI Crossover Stat (1=MDIX) */
+	PHY_M_PS_DOWNS_STAT	= 1<<5,  /* Downshift Status (1=downsh.) */
+	PHY_M_PS_ENDET_STAT	= 1<<4,  /* Energy Detect Status (1=act) */
+	PHY_M_PS_TX_P_EN	= 1<<3,  /* Tx Pause Enabled */
+	PHY_M_PS_RX_P_EN	= 1<<2,  /* Rx Pause Enabled */
+	PHY_M_PS_POL_REV	= 1<<1,  /* Polarity Reversed */
+	PHY_M_PS_JABBER		= 1<<0,  /* Jabber */
+};
+
+#define PHY_M_PS_PAUSE_MSK	(PHY_M_PS_TX_P_EN | PHY_M_PS_RX_P_EN)
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+enum {
+	PHY_M_PS_DTE_DETECT	= 1<<15, /* Data Terminal Equipment (DTE) Detected */
+	PHY_M_PS_RES_SPEED	= 1<<14, /* Resolved Speed (1=100 Mbps, 0=10 Mbps */
+};
+
+enum {
+	PHY_M_IS_AN_ERROR	= 1<<15, /* Auto-Negotiation Error */
+	PHY_M_IS_LSP_CHANGE	= 1<<14, /* Link Speed Changed */
+	PHY_M_IS_DUP_CHANGE	= 1<<13, /* Duplex Mode Changed */
+	PHY_M_IS_AN_PR		= 1<<12, /* Page Received */
+	PHY_M_IS_AN_COMPL	= 1<<11, /* Auto-Negotiation Completed */
+	PHY_M_IS_LST_CHANGE	= 1<<10, /* Link Status Changed */
+	PHY_M_IS_SYMB_ERROR	= 1<<9, /* Symbol Error */
+	PHY_M_IS_FALSE_CARR	= 1<<8, /* False Carrier */
+	PHY_M_IS_FIFO_ERROR	= 1<<7, /* FIFO Overflow/Underrun Error */
+	PHY_M_IS_MDI_CHANGE	= 1<<6, /* MDI Crossover Changed */
+	PHY_M_IS_DOWNSH_DET	= 1<<5, /* Downshift Detected */
+	PHY_M_IS_END_CHANGE	= 1<<4, /* Energy Detect Changed */
+
+	PHY_M_IS_DTE_CHANGE	= 1<<2, /* DTE Power Det. Status Changed */
+	PHY_M_IS_POL_CHANGE	= 1<<1, /* Polarity Changed */
+	PHY_M_IS_JABBER		= 1<<0, /* Jabber */
+
+	PHY_M_DEF_MSK		= PHY_M_IS_LSP_CHANGE | PHY_M_IS_LST_CHANGE
+				 | PHY_M_IS_DUP_CHANGE,
+	PHY_M_AN_MSK	       = PHY_M_IS_AN_ERROR | PHY_M_IS_AN_COMPL,
+};
+
+
+/*****  PHY_MARV_EXT_CTRL	16 bit r/w	Ext. PHY Specific Ctrl *****/
+enum {
+	PHY_M_EC_ENA_BC_EXT = 1<<15, /* Enable Block Carr. Ext. (88E1111 only) */
+	PHY_M_EC_ENA_LIN_LB = 1<<14, /* Enable Line Loopback (88E1111 only) */
+
+	PHY_M_EC_DIS_LINK_P = 1<<12, /* Disable Link Pulses (88E1111 only) */
+	PHY_M_EC_M_DSC_MSK  = 3<<10, /* Bit 11..10:	Master Downshift Counter */
+					/* (88E1011 only) */
+	PHY_M_EC_S_DSC_MSK  = 3<<8,/* Bit  9.. 8:	Slave  Downshift Counter */
+				       /* (88E1011 only) */
+	PHY_M_EC_M_DSC_MSK2 = 7<<9,/* Bit 11.. 9:	Master Downshift Counter */
+					/* (88E1111 only) */
+	PHY_M_EC_DOWN_S_ENA = 1<<8, /* Downshift Enable (88E1111 only) */
+					/* !!! Errata in spec. (1 = disable) */
+	PHY_M_EC_RX_TIM_CT  = 1<<7, /* RGMII Rx Timing Control*/
+	PHY_M_EC_MAC_S_MSK  = 7<<4,/* Bit  6.. 4:	Def. MAC interface speed */
+	PHY_M_EC_FIB_AN_ENA = 1<<3, /* Fiber Auto-Neg. Enable (88E1011S only) */
+	PHY_M_EC_DTE_D_ENA  = 1<<2, /* DTE Detect Enable (88E1111 only) */
+	PHY_M_EC_TX_TIM_CT  = 1<<1, /* RGMII Tx Timing Control */
+	PHY_M_EC_TRANS_DIS  = 1<<0, /* Transmitter Disable (88E1111 only) */};
+
+#define PHY_M_EC_M_DSC(x)	((u16)(x)<<10 & PHY_M_EC_M_DSC_MSK)
+					/* 00=1x; 01=2x; 10=3x; 11=4x */
+#define PHY_M_EC_S_DSC(x)	((u16)(x)<<8 & PHY_M_EC_S_DSC_MSK)
+					/* 00=dis; 01=1x; 10=2x; 11=3x */
+#define PHY_M_EC_DSC_2(x)	((u16)(x)<<9 & PHY_M_EC_M_DSC_MSK2)
+					/* 000=1x; 001=2x; 010=3x; 011=4x */
+#define PHY_M_EC_MAC_S(x)	((u16)(x)<<4 & PHY_M_EC_MAC_S_MSK)
+					/* 01X=0; 110=2.5; 111=25 (MHz) */
+
+/* for Yukon-2 Gigabit Ethernet PHY (88E1112 only) */
+enum {
+	PHY_M_PC_DIS_LINK_Pa	= 1<<15,/* Disable Link Pulses */
+	PHY_M_PC_DSC_MSK	= 7<<12,/* Bit 14..12:	Downshift Counter */
+	PHY_M_PC_DOWN_S_ENA	= 1<<11,/* Downshift Enable */
+};
+/* !!! Errata in spec. (1 = disable) */
+
+#define PHY_M_PC_DSC(x)			(((u16)(x)<<12) & PHY_M_PC_DSC_MSK)
+											/* 100=5x; 101=6x; 110=7x; 111=8x */
+enum {
+	MAC_TX_CLK_0_MHZ	= 2,
+	MAC_TX_CLK_2_5_MHZ	= 6,
+	MAC_TX_CLK_25_MHZ 	= 7,
+};
+
+/*****  PHY_MARV_LED_CTRL	16 bit r/w	LED Control Reg *****/
+enum {
+	PHY_M_LEDC_DIS_LED	= 1<<15, /* Disable LED */
+	PHY_M_LEDC_PULS_MSK	= 7<<12,/* Bit 14..12: Pulse Stretch Mask */
+	PHY_M_LEDC_F_INT	= 1<<11, /* Force Interrupt */
+	PHY_M_LEDC_BL_R_MSK	= 7<<8,/* Bit 10.. 8: Blink Rate Mask */
+	PHY_M_LEDC_DP_C_LSB	= 1<<7, /* Duplex Control (LSB, 88E1111 only) */
+	PHY_M_LEDC_TX_C_LSB	= 1<<6, /* Tx Control (LSB, 88E1111 only) */
+	PHY_M_LEDC_LK_C_MSK	= 7<<3,/* Bit  5.. 3: Link Control Mask */
+					/* (88E1111 only) */
+};
+
+enum {
+	PHY_M_LEDC_LINK_MSK	= 3<<3,/* Bit  4.. 3: Link Control Mask */
+									/* (88E1011 only) */
+	PHY_M_LEDC_DP_CTRL	= 1<<2, /* Duplex Control */
+	PHY_M_LEDC_DP_C_MSB	= 1<<2, /* Duplex Control (MSB, 88E1111 only) */
+	PHY_M_LEDC_RX_CTRL	= 1<<1, /* Rx Activity / Link */
+	PHY_M_LEDC_TX_CTRL	= 1<<0, /* Tx Activity / Link */
+	PHY_M_LEDC_TX_C_MSB	= 1<<0, /* Tx Control (MSB, 88E1111 only) */
+};
+
+#define PHY_M_LED_PULS_DUR(x)	(((u16)(x)<<12) & PHY_M_LEDC_PULS_MSK)
+
+/*****  PHY_MARV_PHY_STAT (page 3)16 bit r/w	Polarity Control Reg. *****/
+enum {
+	PHY_M_POLC_LS1M_MSK	= 0xf<<12, /* Bit 15..12: LOS,STAT1 Mix % Mask */
+	PHY_M_POLC_IS0M_MSK	= 0xf<<8,  /* Bit 11.. 8: INIT,STAT0 Mix % Mask */
+	PHY_M_POLC_LOS_MSK	= 0x3<<6,  /* Bit  7.. 6: LOS Pol. Ctrl. Mask */
+	PHY_M_POLC_INIT_MSK	= 0x3<<4,  /* Bit  5.. 4: INIT Pol. Ctrl. Mask */
+	PHY_M_POLC_STA1_MSK	= 0x3<<2,  /* Bit  3.. 2: STAT1 Pol. Ctrl. Mask */
+	PHY_M_POLC_STA0_MSK	= 0x3,     /* Bit  1.. 0: STAT0 Pol. Ctrl. Mask */
+};
+
+#define PHY_M_POLC_LS1_P_MIX(x)	(((x)<<12) & PHY_M_POLC_LS1M_MSK)
+#define PHY_M_POLC_IS0_P_MIX(x)	(((x)<<8) & PHY_M_POLC_IS0M_MSK)
+#define PHY_M_POLC_LOS_CTRL(x)	(((x)<<6) & PHY_M_POLC_LOS_MSK)
+#define PHY_M_POLC_INIT_CTRL(x)	(((x)<<4) & PHY_M_POLC_INIT_MSK)
+#define PHY_M_POLC_STA1_CTRL(x)	(((x)<<2) & PHY_M_POLC_STA1_MSK)
+#define PHY_M_POLC_STA0_CTRL(x)	(((x)<<0) & PHY_M_POLC_STA0_MSK)
+
+enum {
+	PULS_NO_STR	= 0,/* no pulse stretching */
+	PULS_21MS	= 1,/* 21 ms to 42 ms */
+	PULS_42MS	= 2,/* 42 ms to 84 ms */
+	PULS_84MS	= 3,/* 84 ms to 170 ms */
+	PULS_170MS	= 4,/* 170 ms to 340 ms */
+	PULS_340MS	= 5,/* 340 ms to 670 ms */
+	PULS_670MS	= 6,/* 670 ms to 1.3 s */
+	PULS_1300MS	= 7,/* 1.3 s to 2.7 s */
+};
+
+#define PHY_M_LED_BLINK_RT(x)	(((u16)(x)<<8) & PHY_M_LEDC_BL_R_MSK)
+
+enum {
+	BLINK_42MS	= 0,/* 42 ms */
+	BLINK_84MS	= 1,/* 84 ms */
+	BLINK_170MS	= 2,/* 170 ms */
+	BLINK_340MS	= 3,/* 340 ms */
+	BLINK_670MS	= 4,/* 670 ms */
+};
+
+/*****  PHY_MARV_LED_OVER	16 bit r/w	Manual LED Override Reg *****/
+#define PHY_M_LED_MO_SGMII(x)	((x)<<14)	/* Bit 15..14:  SGMII AN Timer */
+
+#define PHY_M_LED_MO_DUP(x)	((x)<<10)	/* Bit 11..10:  Duplex */
+#define PHY_M_LED_MO_10(x)	((x)<<8)	/* Bit  9.. 8:  Link 10 */
+#define PHY_M_LED_MO_100(x)	((x)<<6)	/* Bit  7.. 6:  Link 100 */
+#define PHY_M_LED_MO_1000(x)	((x)<<4)	/* Bit  5.. 4:  Link 1000 */
+#define PHY_M_LED_MO_RX(x)	((x)<<2)	/* Bit  3.. 2:  Rx */
+#define PHY_M_LED_MO_TX(x)	((x)<<0)	/* Bit  1.. 0:  Tx */
+
+enum led_mode {
+	MO_LED_NORM  = 0,
+	MO_LED_BLINK = 1,
+	MO_LED_OFF   = 2,
+	MO_LED_ON    = 3,
+};
+
+/*****  PHY_MARV_EXT_CTRL_2	16 bit r/w	Ext. PHY Specific Ctrl 2 *****/
+enum {
+	PHY_M_EC2_FI_IMPED	= 1<<6, /* Fiber Input  Impedance */
+	PHY_M_EC2_FO_IMPED	= 1<<5, /* Fiber Output Impedance */
+	PHY_M_EC2_FO_M_CLK	= 1<<4, /* Fiber Mode Clock Enable */
+	PHY_M_EC2_FO_BOOST	= 1<<3, /* Fiber Output Boost */
+	PHY_M_EC2_FO_AM_MSK	= 7,/* Bit  2.. 0:	Fiber Output Amplitude */
+};
+
+/*****  PHY_MARV_EXT_P_STAT 16 bit r/w	Ext. PHY Specific Status *****/
+enum {
+	PHY_M_FC_AUTO_SEL	= 1<<15, /* Fiber/Copper Auto Sel. Dis. */
+	PHY_M_FC_AN_REG_ACC	= 1<<14, /* Fiber/Copper AN Reg. Access */
+	PHY_M_FC_RESOLUTION	= 1<<13, /* Fiber/Copper Resolution */
+	PHY_M_SER_IF_AN_BP	= 1<<12, /* Ser. IF AN Bypass Enable */
+	PHY_M_SER_IF_BP_ST	= 1<<11, /* Ser. IF AN Bypass Status */
+	PHY_M_IRQ_POLARITY	= 1<<10, /* IRQ polarity */
+	PHY_M_DIS_AUT_MED	= 1<<9, /* Disable Aut. Medium Reg. Selection */
+	/* (88E1111 only) */
+
+	PHY_M_UNDOC1		= 1<<7, /* undocumented bit !! */
+	PHY_M_DTE_POW_STAT	= 1<<4, /* DTE Power Status (88E1111 only) */
+	PHY_M_MODE_MASK	= 0xf, /* Bit  3.. 0: copy of HWCFG MODE[3:0] */
+};
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+/*****  PHY_MARV_FE_LED_PAR		16 bit r/w	LED Parallel Select Reg. *****/
+									/* Bit 15..12: reserved (used internally) */
+enum {
+	PHY_M_FELP_LED2_MSK = 0xf<<8,	/* Bit 11.. 8: LED2 Mask (LINK) */
+	PHY_M_FELP_LED1_MSK = 0xf<<4,	/* Bit  7.. 4: LED1 Mask (ACT) */
+	PHY_M_FELP_LED0_MSK = 0xf, /* Bit  3.. 0: LED0 Mask (SPEED) */
+};
+
+#define PHY_M_FELP_LED2_CTRL(x)	(((u16)(x)<<8) & PHY_M_FELP_LED2_MSK)
+#define PHY_M_FELP_LED1_CTRL(x)	(((u16)(x)<<4) & PHY_M_FELP_LED1_MSK)
+#define PHY_M_FELP_LED0_CTRL(x)	(((u16)(x)<<0) & PHY_M_FELP_LED0_MSK)
+
+enum {
+	LED_PAR_CTRL_COLX	= 0x00,
+	LED_PAR_CTRL_ERROR	= 0x01,
+	LED_PAR_CTRL_DUPLEX	= 0x02,
+	LED_PAR_CTRL_DP_COL	= 0x03,
+	LED_PAR_CTRL_SPEED	= 0x04,
+	LED_PAR_CTRL_LINK	= 0x05,
+	LED_PAR_CTRL_TX		= 0x06,
+	LED_PAR_CTRL_RX		= 0x07,
+	LED_PAR_CTRL_ACT	= 0x08,
+	LED_PAR_CTRL_LNK_RX	= 0x09,
+	LED_PAR_CTRL_LNK_AC	= 0x0a,
+	LED_PAR_CTRL_ACT_BL	= 0x0b,
+	LED_PAR_CTRL_TX_BL	= 0x0c,
+	LED_PAR_CTRL_RX_BL	= 0x0d,
+	LED_PAR_CTRL_COL_BL	= 0x0e,
+	LED_PAR_CTRL_INACT	= 0x0f
+};
+
+/*****,PHY_MARV_FE_SPEC_2		16 bit r/w	Specific Control Reg. 2 *****/
+enum {
+	PHY_M_FESC_DIS_WAIT	= 1<<2, /* Disable TDR Waiting Period */
+	PHY_M_FESC_ENA_MCLK	= 1<<1, /* Enable MAC Rx Clock in sleep mode */
+	PHY_M_FESC_SEL_CL_A	= 1<<0, /* Select Class A driver (100B-TX) */
+};
+
+/* for Yukon-2 Gigabit Ethernet PHY (88E1112 only) */
+/*****  PHY_MARV_PHY_CTRL (page 1)		16 bit r/w	Fiber Specific Ctrl *****/
+enum {
+	PHY_M_FIB_FORCE_LNK	= 1<<10,/* Force Link Good */
+	PHY_M_FIB_SIGD_POL	= 1<<9,	/* SIGDET Polarity */
+	PHY_M_FIB_TX_DIS	= 1<<3,	/* Transmitter Disable */
+};
+
+/* for Yukon-2 Gigabit Ethernet PHY (88E1112 only) */
+/*****  PHY_MARV_PHY_CTRL (page 2)		16 bit r/w	MAC Specific Ctrl *****/
+enum {
+	PHY_M_MAC_MD_MSK	= 7<<7, /* Bit  9.. 7: Mode Select Mask */
+	PHY_M_MAC_GMIF_PUP	= 1<<3,	/* GMII Power Up (88E1149 only) */
+	PHY_M_MAC_MD_AUTO	= 3,/* Auto Copper/1000Base-X */
+	PHY_M_MAC_MD_COPPER	= 5,/* Copper only */
+	PHY_M_MAC_MD_1000BX	= 7,/* 1000Base-X only */
+};
+#define PHY_M_MAC_MODE_SEL(x)	(((x)<<7) & PHY_M_MAC_MD_MSK)
+
+/*****  PHY_MARV_PHY_CTRL (page 3)		16 bit r/w	LED Control Reg. *****/
+enum {
+	PHY_M_LEDC_LOS_MSK	= 0xf<<12,/* Bit 15..12: LOS LED Ctrl. Mask */
+	PHY_M_LEDC_INIT_MSK	= 0xf<<8, /* Bit 11.. 8: INIT LED Ctrl. Mask */
+	PHY_M_LEDC_STA1_MSK	= 0xf<<4,/* Bit  7.. 4: STAT1 LED Ctrl. Mask */
+	PHY_M_LEDC_STA0_MSK	= 0xf, /* Bit  3.. 0: STAT0 LED Ctrl. Mask */
+};
+
+#define PHY_M_LEDC_LOS_CTRL(x)	(((x)<<12) & PHY_M_LEDC_LOS_MSK)
+#define PHY_M_LEDC_INIT_CTRL(x)	(((x)<<8) & PHY_M_LEDC_INIT_MSK)
+#define PHY_M_LEDC_STA1_CTRL(x)	(((x)<<4) & PHY_M_LEDC_STA1_MSK)
+#define PHY_M_LEDC_STA0_CTRL(x)	(((x)<<0) & PHY_M_LEDC_STA0_MSK)
+
+/* GMAC registers  */
+/* Port Registers */
+enum {
+	GM_GP_STAT	= 0x0000,	/* 16 bit r/o	General Purpose Status */
+	GM_GP_CTRL	= 0x0004,	/* 16 bit r/w	General Purpose Control */
+	GM_TX_CTRL	= 0x0008,	/* 16 bit r/w	Transmit Control Reg. */
+	GM_RX_CTRL	= 0x000c,	/* 16 bit r/w	Receive Control Reg. */
+	GM_TX_FLOW_CTRL	= 0x0010,	/* 16 bit r/w	Transmit Flow-Control */
+	GM_TX_PARAM	= 0x0014,	/* 16 bit r/w	Transmit Parameter Reg. */
+	GM_SERIAL_MODE	= 0x0018,	/* 16 bit r/w	Serial Mode Register */
+/* Source Address Registers */
+	GM_SRC_ADDR_1L	= 0x001c,	/* 16 bit r/w	Source Address 1 (low) */
+	GM_SRC_ADDR_1M	= 0x0020,	/* 16 bit r/w	Source Address 1 (middle) */
+	GM_SRC_ADDR_1H	= 0x0024,	/* 16 bit r/w	Source Address 1 (high) */
+	GM_SRC_ADDR_2L	= 0x0028,	/* 16 bit r/w	Source Address 2 (low) */
+	GM_SRC_ADDR_2M	= 0x002c,	/* 16 bit r/w	Source Address 2 (middle) */
+	GM_SRC_ADDR_2H	= 0x0030,	/* 16 bit r/w	Source Address 2 (high) */
+
+/* Multicast Address Hash Registers */
+	GM_MC_ADDR_H1	= 0x0034,	/* 16 bit r/w	Multicast Address Hash 1 */
+	GM_MC_ADDR_H2	= 0x0038,	/* 16 bit r/w	Multicast Address Hash 2 */
+	GM_MC_ADDR_H3	= 0x003c,	/* 16 bit r/w	Multicast Address Hash 3 */
+	GM_MC_ADDR_H4	= 0x0040,	/* 16 bit r/w	Multicast Address Hash 4 */
+
+/* Interrupt Source Registers */
+	GM_TX_IRQ_SRC	= 0x0044,	/* 16 bit r/o	Tx Overflow IRQ Source */
+	GM_RX_IRQ_SRC	= 0x0048,	/* 16 bit r/o	Rx Overflow IRQ Source */
+	GM_TR_IRQ_SRC	= 0x004c,	/* 16 bit r/o	Tx/Rx Over. IRQ Source */
+
+/* Interrupt Mask Registers */
+	GM_TX_IRQ_MSK	= 0x0050,	/* 16 bit r/w	Tx Overflow IRQ Mask */
+	GM_RX_IRQ_MSK	= 0x0054,	/* 16 bit r/w	Rx Overflow IRQ Mask */
+	GM_TR_IRQ_MSK	= 0x0058,	/* 16 bit r/w	Tx/Rx Over. IRQ Mask */
+
+/* Serial Management Interface (SMI) Registers */
+	GM_SMI_CTRL	= 0x0080,	/* 16 bit r/w	SMI Control Register */
+	GM_SMI_DATA	= 0x0084,	/* 16 bit r/w	SMI Data Register */
+	GM_PHY_ADDR	= 0x0088,	/* 16 bit r/w	GPHY Address Register */
+/* MIB Counters */
+	GM_MIB_CNT_BASE	= 0x0100,	/* Base Address of MIB Counters */
+	GM_MIB_CNT_END	= 0x025C,	/* Last MIB counter */
+};
+
+
+/*
+ * MIB Counters base address definitions (low word) -
+ * use offset 4 for access to high word	(32 bit r/o)
+ */
+enum {
+	GM_RXF_UC_OK    = GM_MIB_CNT_BASE + 0,	/* Unicast Frames Received OK */
+	GM_RXF_BC_OK	= GM_MIB_CNT_BASE + 8,	/* Broadcast Frames Received OK */
+	GM_RXF_MPAUSE	= GM_MIB_CNT_BASE + 16,	/* Pause MAC Ctrl Frames Received */
+	GM_RXF_MC_OK	= GM_MIB_CNT_BASE + 24,	/* Multicast Frames Received OK */
+	GM_RXF_FCS_ERR	= GM_MIB_CNT_BASE + 32,	/* Rx Frame Check Seq. Error */
+
+	GM_RXO_OK_LO	= GM_MIB_CNT_BASE + 48,	/* Octets Received OK Low */
+	GM_RXO_OK_HI	= GM_MIB_CNT_BASE + 56,	/* Octets Received OK High */
+	GM_RXO_ERR_LO	= GM_MIB_CNT_BASE + 64,	/* Octets Received Invalid Low */
+	GM_RXO_ERR_HI	= GM_MIB_CNT_BASE + 72,	/* Octets Received Invalid High */
+	GM_RXF_SHT	= GM_MIB_CNT_BASE + 80,	/* Frames <64 Byte Received OK */
+	GM_RXE_FRAG	= GM_MIB_CNT_BASE + 88,	/* Frames <64 Byte Received with FCS Err */
+	GM_RXF_64B	= GM_MIB_CNT_BASE + 96,	/* 64 Byte Rx Frame */
+	GM_RXF_127B	= GM_MIB_CNT_BASE + 104,/* 65-127 Byte Rx Frame */
+	GM_RXF_255B	= GM_MIB_CNT_BASE + 112,/* 128-255 Byte Rx Frame */
+	GM_RXF_511B	= GM_MIB_CNT_BASE + 120,/* 256-511 Byte Rx Frame */
+	GM_RXF_1023B	= GM_MIB_CNT_BASE + 128,/* 512-1023 Byte Rx Frame */
+	GM_RXF_1518B	= GM_MIB_CNT_BASE + 136,/* 1024-1518 Byte Rx Frame */
+	GM_RXF_MAX_SZ	= GM_MIB_CNT_BASE + 144,/* 1519-MaxSize Byte Rx Frame */
+	GM_RXF_LNG_ERR	= GM_MIB_CNT_BASE + 152,/* Rx Frame too Long Error */
+	GM_RXF_JAB_PKT	= GM_MIB_CNT_BASE + 160,/* Rx Jabber Packet Frame */
+
+	GM_RXE_FIFO_OV	= GM_MIB_CNT_BASE + 176,/* Rx FIFO overflow Event */
+	GM_TXF_UC_OK	= GM_MIB_CNT_BASE + 192,/* Unicast Frames Xmitted OK */
+	GM_TXF_BC_OK	= GM_MIB_CNT_BASE + 200,/* Broadcast Frames Xmitted OK */
+	GM_TXF_MPAUSE	= GM_MIB_CNT_BASE + 208,/* Pause MAC Ctrl Frames Xmitted */
+	GM_TXF_MC_OK	= GM_MIB_CNT_BASE + 216,/* Multicast Frames Xmitted OK */
+	GM_TXO_OK_LO	= GM_MIB_CNT_BASE + 224,/* Octets Transmitted OK Low */
+	GM_TXO_OK_HI	= GM_MIB_CNT_BASE + 232,/* Octets Transmitted OK High */
+	GM_TXF_64B	= GM_MIB_CNT_BASE + 240,/* 64 Byte Tx Frame */
+	GM_TXF_127B	= GM_MIB_CNT_BASE + 248,/* 65-127 Byte Tx Frame */
+	GM_TXF_255B	= GM_MIB_CNT_BASE + 256,/* 128-255 Byte Tx Frame */
+	GM_TXF_511B	= GM_MIB_CNT_BASE + 264,/* 256-511 Byte Tx Frame */
+	GM_TXF_1023B	= GM_MIB_CNT_BASE + 272,/* 512-1023 Byte Tx Frame */
+	GM_TXF_1518B	= GM_MIB_CNT_BASE + 280,/* 1024-1518 Byte Tx Frame */
+	GM_TXF_MAX_SZ	= GM_MIB_CNT_BASE + 288,/* 1519-MaxSize Byte Tx Frame */
+
+	GM_TXF_COL	= GM_MIB_CNT_BASE + 304,/* Tx Collision */
+	GM_TXF_LAT_COL	= GM_MIB_CNT_BASE + 312,/* Tx Late Collision */
+	GM_TXF_ABO_COL	= GM_MIB_CNT_BASE + 320,/* Tx aborted due to Exces. Col. */
+	GM_TXF_MUL_COL	= GM_MIB_CNT_BASE + 328,/* Tx Multiple Collision */
+	GM_TXF_SNG_COL	= GM_MIB_CNT_BASE + 336,/* Tx Single Collision */
+	GM_TXE_FIFO_UR	= GM_MIB_CNT_BASE + 344,/* Tx FIFO Underrun Event */
+};
+
+/* GMAC Bit Definitions */
+/*	GM_GP_STAT	16 bit r/o	General Purpose Status Register */
+enum {
+	GM_GPSR_SPEED		= 1<<15, /* Bit 15:	Port Speed (1 = 100 Mbps) */
+	GM_GPSR_DUPLEX		= 1<<14, /* Bit 14:	Duplex Mode (1 = Full) */
+	GM_GPSR_FC_TX_DIS	= 1<<13, /* Bit 13:	Tx Flow-Control Mode Disabled */
+	GM_GPSR_LINK_UP		= 1<<12, /* Bit 12:	Link Up Status */
+	GM_GPSR_PAUSE		= 1<<11, /* Bit 11:	Pause State */
+	GM_GPSR_TX_ACTIVE	= 1<<10, /* Bit 10:	Tx in Progress */
+	GM_GPSR_EXC_COL		= 1<<9,	/* Bit  9:	Excessive Collisions Occured */
+	GM_GPSR_LAT_COL		= 1<<8,	/* Bit  8:	Late Collisions Occured */
+
+	GM_GPSR_PHY_ST_CH	= 1<<5,	/* Bit  5:	PHY Status Change */
+	GM_GPSR_GIG_SPEED	= 1<<4,	/* Bit  4:	Gigabit Speed (1 = 1000 Mbps) */
+	GM_GPSR_PART_MODE	= 1<<3,	/* Bit  3:	Partition mode */
+	GM_GPSR_FC_RX_DIS	= 1<<2,	/* Bit  2:	Rx Flow-Control Mode Disabled */
+	GM_GPSR_PROM_EN		= 1<<1,	/* Bit  1:	Promiscuous Mode Enabled */
+};
+
+/*	GM_GP_CTRL	16 bit r/w	General Purpose Control Register */
+enum {
+	GM_GPCR_PROM_ENA	= 1<<14,	/* Bit 14:	Enable Promiscuous Mode */
+	GM_GPCR_FC_TX_DIS	= 1<<13, /* Bit 13:	Disable Tx Flow-Control Mode */
+	GM_GPCR_TX_ENA		= 1<<12, /* Bit 12:	Enable Transmit */
+	GM_GPCR_RX_ENA		= 1<<11, /* Bit 11:	Enable Receive */
+	GM_GPCR_BURST_ENA	= 1<<10, /* Bit 10:	Enable Burst Mode */
+	GM_GPCR_LOOP_ENA	= 1<<9,	/* Bit  9:	Enable MAC Loopback Mode */
+	GM_GPCR_PART_ENA	= 1<<8,	/* Bit  8:	Enable Partition Mode */
+	GM_GPCR_GIGS_ENA	= 1<<7,	/* Bit  7:	Gigabit Speed (1000 Mbps) */
+	GM_GPCR_FL_PASS		= 1<<6,	/* Bit  6:	Force Link Pass */
+	GM_GPCR_DUP_FULL	= 1<<5,	/* Bit  5:	Full Duplex Mode */
+	GM_GPCR_FC_RX_DIS	= 1<<4,	/* Bit  4:	Disable Rx Flow-Control Mode */
+	GM_GPCR_SPEED_100	= 1<<3,   /* Bit  3:	Port Speed 100 Mbps */
+	GM_GPCR_AU_DUP_DIS	= 1<<2,	/* Bit  2:	Disable Auto-Update Duplex */
+	GM_GPCR_AU_FCT_DIS	= 1<<1,	/* Bit  1:	Disable Auto-Update Flow-C. */
+	GM_GPCR_AU_SPD_DIS	= 1<<0,	/* Bit  0:	Disable Auto-Update Speed */
+};
+
+#define GM_GPCR_SPEED_1000	(GM_GPCR_GIGS_ENA | GM_GPCR_SPEED_100)
+#define GM_GPCR_AU_ALL_DIS	(GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS|GM_GPCR_AU_SPD_DIS)
+
+/*	GM_TX_CTRL			16 bit r/w	Transmit Control Register */
+enum {
+	GM_TXCR_FORCE_JAM	= 1<<15, /* Bit 15:	Force Jam / Flow-Control */
+	GM_TXCR_CRC_DIS		= 1<<14, /* Bit 14:	Disable insertion of CRC */
+	GM_TXCR_PAD_DIS		= 1<<13, /* Bit 13:	Disable padding of packets */
+	GM_TXCR_COL_THR_MSK	= 7<<10, /* Bit 12..10:	Collision Threshold */
+};
+
+#define TX_COL_THR(x)		(((x)<<10) & GM_TXCR_COL_THR_MSK)
+#define TX_COL_DEF		0x04
+
+/*	GM_RX_CTRL			16 bit r/w	Receive Control Register */
+enum {
+	GM_RXCR_UCF_ENA	= 1<<15, /* Bit 15:	Enable Unicast filtering */
+	GM_RXCR_MCF_ENA	= 1<<14, /* Bit 14:	Enable Multicast filtering */
+	GM_RXCR_CRC_DIS	= 1<<13, /* Bit 13:	Remove 4-byte CRC */
+	GM_RXCR_PASS_FC	= 1<<12, /* Bit 12:	Pass FC packets to FIFO */
+};
+
+/*	GM_TX_PARAM		16 bit r/w	Transmit Parameter Register */
+enum {
+	GM_TXPA_JAMLEN_MSK	= 0x03<<14,	/* Bit 15..14:	Jam Length */
+	GM_TXPA_JAMIPG_MSK	= 0x1f<<9,	/* Bit 13..9:	Jam IPG */
+	GM_TXPA_JAMDAT_MSK	= 0x1f<<4,	/* Bit  8..4:	IPG Jam to Data */
+	GM_TXPA_BO_LIM_MSK	= 0x0f,		/* Bit  3.. 0: Backoff Limit Mask */
+
+	TX_JAM_LEN_DEF		= 0x03,
+	TX_JAM_IPG_DEF		= 0x0b,
+	TX_IPG_JAM_DEF		= 0x1c,
+	TX_BOF_LIM_DEF		= 0x04,
+};
+
+#define TX_JAM_LEN_VAL(x)	(((x)<<14) & GM_TXPA_JAMLEN_MSK)
+#define TX_JAM_IPG_VAL(x)	(((x)<<9)  & GM_TXPA_JAMIPG_MSK)
+#define TX_IPG_JAM_DATA(x)	(((x)<<4)  & GM_TXPA_JAMDAT_MSK)
+#define TX_BACK_OFF_LIM(x)	((x) & GM_TXPA_BO_LIM_MSK)
+
+
+/*	GM_SERIAL_MODE			16 bit r/w	Serial Mode Register */
+enum {
+	GM_SMOD_DATABL_MSK	= 0x1f<<11, /* Bit 15..11:	Data Blinder (r/o) */
+	GM_SMOD_LIMIT_4		= 1<<10, /* Bit 10:	4 consecutive Tx trials */
+	GM_SMOD_VLAN_ENA	= 1<<9,	/* Bit  9:	Enable VLAN  (Max. Frame Len) */
+	GM_SMOD_JUMBO_ENA	= 1<<8,	/* Bit  8:	Enable Jumbo (Max. Frame Len) */
+	 GM_SMOD_IPG_MSK	= 0x1f	/* Bit 4..0:	Inter-Packet Gap (IPG) */
+};
+
+#define DATA_BLIND_VAL(x)	(((x)<<11) & GM_SMOD_DATABL_MSK)
+#define DATA_BLIND_DEF		0x04
+
+#define IPG_DATA_VAL(x)		(x & GM_SMOD_IPG_MSK)
+#define IPG_DATA_DEF		0x1e
+
+/*	GM_SMI_CTRL			16 bit r/w	SMI Control Register */
+enum {
+	GM_SMI_CT_PHY_A_MSK	= 0x1f<<11,/* Bit 15..11:	PHY Device Address */
+	GM_SMI_CT_REG_A_MSK	= 0x1f<<6,/* Bit 10.. 6:	PHY Register Address */
+	GM_SMI_CT_OP_RD		= 1<<5,	/* Bit  5:	OpCode Read (0=Write)*/
+	GM_SMI_CT_RD_VAL	= 1<<4,	/* Bit  4:	Read Valid (Read completed) */
+	GM_SMI_CT_BUSY		= 1<<3,	/* Bit  3:	Busy (Operation in progress) */
+};
+
+#define GM_SMI_CT_PHY_AD(x)	(((u16)(x)<<11) & GM_SMI_CT_PHY_A_MSK)
+#define GM_SMI_CT_REG_AD(x)	(((u16)(x)<<6) & GM_SMI_CT_REG_A_MSK)
+
+/*	GM_PHY_ADDR				16 bit r/w	GPHY Address Register */
+enum {
+	GM_PAR_MIB_CLR	= 1<<5,	/* Bit  5:	Set MIB Clear Counter Mode */
+	GM_PAR_MIB_TST	= 1<<4,	/* Bit  4:	MIB Load Counter (Test Mode) */
+};
+
+/* Receive Frame Status Encoding */
+enum {
+	GMR_FS_LEN	= 0x7fff<<16, /* Bit 30..16:	Rx Frame Length */
+	GMR_FS_VLAN	= 1<<13, /* VLAN Packet */
+	GMR_FS_JABBER	= 1<<12, /* Jabber Packet */
+	GMR_FS_UN_SIZE	= 1<<11, /* Undersize Packet */
+	GMR_FS_MC	= 1<<10, /* Multicast Packet */
+	GMR_FS_BC	= 1<<9,  /* Broadcast Packet */
+	GMR_FS_RX_OK	= 1<<8,  /* Receive OK (Good Packet) */
+	GMR_FS_GOOD_FC	= 1<<7,  /* Good Flow-Control Packet */
+	GMR_FS_BAD_FC	= 1<<6,  /* Bad  Flow-Control Packet */
+	GMR_FS_MII_ERR	= 1<<5,  /* MII Error */
+	GMR_FS_LONG_ERR	= 1<<4,  /* Too Long Packet */
+	GMR_FS_FRAGMENT	= 1<<3,  /* Fragment */
+
+	GMR_FS_CRC_ERR	= 1<<1,  /* CRC Error */
+	GMR_FS_RX_FF_OV	= 1<<0,  /* Rx FIFO Overflow */
+
+	GMR_FS_ANY_ERR	= GMR_FS_RX_FF_OV | GMR_FS_CRC_ERR |
+			  GMR_FS_FRAGMENT | GMR_FS_LONG_ERR |
+			  GMR_FS_MII_ERR | GMR_FS_BAD_FC |
+			  GMR_FS_UN_SIZE | GMR_FS_JABBER,
+};
+
+/*	RX_GMF_CTRL_T	32 bit	Rx GMAC FIFO Control/Test */
+enum {
+	RX_TRUNC_ON	= 1<<27,  	/* enable  packet truncation */
+	RX_TRUNC_OFF	= 1<<26, 	/* disable packet truncation */
+	RX_VLAN_STRIP_ON = 1<<25,	/* enable  VLAN stripping */
+	RX_VLAN_STRIP_OFF = 1<<24,	/* disable VLAN stripping */
+
+	RX_MACSEC_FLUSH_ON  = 1<<23,
+	RX_MACSEC_FLUSH_OFF = 1<<22,
+	RX_MACSEC_ASF_FLUSH_ON = 1<<21,
+	RX_MACSEC_ASF_FLUSH_OFF = 1<<20,
+
+	GMF_RX_OVER_ON      = 1<<19,	/* enable flushing on receive overrun */
+	GMF_RX_OVER_OFF     = 1<<18,	/* disable flushing on receive overrun */
+	GMF_ASF_RX_OVER_ON  = 1<<17,	/* enable flushing of ASF when overrun */
+	GMF_ASF_RX_OVER_OFF = 1<<16,	/* disable flushing of ASF when overrun */
+
+	GMF_WP_TST_ON	= 1<<14,	/* Write Pointer Test On */
+	GMF_WP_TST_OFF	= 1<<13,	/* Write Pointer Test Off */
+	GMF_WP_STEP	= 1<<12,	/* Write Pointer Step/Increment */
+
+	GMF_RP_TST_ON	= 1<<10,	/* Read Pointer Test On */
+	GMF_RP_TST_OFF	= 1<<9,		/* Read Pointer Test Off */
+	GMF_RP_STEP	= 1<<8,		/* Read Pointer Step/Increment */
+	GMF_RX_F_FL_ON	= 1<<7,		/* Rx FIFO Flush Mode On */
+	GMF_RX_F_FL_OFF	= 1<<6,		/* Rx FIFO Flush Mode Off */
+	GMF_CLI_RX_FO	= 1<<5,		/* Clear IRQ Rx FIFO Overrun */
+	GMF_CLI_RX_C	= 1<<4,		/* Clear IRQ Rx Frame Complete */
+
+	GMF_OPER_ON	= 1<<3,		/* Operational Mode On */
+	GMF_OPER_OFF	= 1<<2,		/* Operational Mode Off */
+	GMF_RST_CLR	= 1<<1,		/* Clear GMAC FIFO Reset */
+	GMF_RST_SET	= 1<<0,		/* Set   GMAC FIFO Reset */
+
+	RX_GMF_FL_THR_DEF = 0xa,	/* flush threshold (default) */
+
+	GMF_RX_CTRL_DEF	= GMF_OPER_ON | GMF_RX_F_FL_ON,
+};
+
+/*	TX_GMF_EA		32 bit	Tx GMAC FIFO End Address */
+enum {
+	TX_DYN_WM_ENA	= 3,	/* Yukon-FE+ specific */
+};
+
+/*	TX_GMF_CTRL_T	32 bit	Tx GMAC FIFO Control/Test */
+enum {
+	TX_STFW_DIS	= 1<<31,/* Disable Store & Forward (Yukon-EC Ultra) */
+	TX_STFW_ENA	= 1<<30,/* Enable  Store & Forward (Yukon-EC Ultra) */
+
+	TX_VLAN_TAG_ON	= 1<<25,/* enable  VLAN tagging */
+	TX_VLAN_TAG_OFF	= 1<<24,/* disable VLAN tagging */
+
+	TX_JUMBO_ENA	= 1<<23,/* PCI Jumbo Mode enable (Yukon-EC Ultra) */
+	TX_JUMBO_DIS	= 1<<22,/* PCI Jumbo Mode enable (Yukon-EC Ultra) */
+
+	GMF_WSP_TST_ON	= 1<<18,/* Write Shadow Pointer Test On */
+	GMF_WSP_TST_OFF	= 1<<17,/* Write Shadow Pointer Test Off */
+	GMF_WSP_STEP	= 1<<16,/* Write Shadow Pointer Step/Increment */
+
+	GMF_CLI_TX_FU	= 1<<6,	/* Clear IRQ Tx FIFO Underrun */
+	GMF_CLI_TX_FC	= 1<<5,	/* Clear IRQ Tx Frame Complete */
+	GMF_CLI_TX_PE	= 1<<4,	/* Clear IRQ Tx Parity Error */
+};
+
+/*	GMAC_TI_ST_CTRL	 8 bit	Time Stamp Timer Ctrl Reg (YUKON only) */
+enum {
+	GMT_ST_START	= 1<<2,	/* Start Time Stamp Timer */
+	GMT_ST_STOP	= 1<<1,	/* Stop  Time Stamp Timer */
+	GMT_ST_CLR_IRQ	= 1<<0,	/* Clear Time Stamp Timer IRQ */
+};
+
+/* B28_Y2_ASF_STAT_CMD		32 bit	ASF Status and Command Reg */
+enum {
+	Y2_ASF_OS_PRES	= 1<<4,	/* ASF operation system present */
+	Y2_ASF_RESET	= 1<<3,	/* ASF system in reset state */
+	Y2_ASF_RUNNING	= 1<<2,	/* ASF system operational */
+	Y2_ASF_CLR_HSTI = 1<<1,	/* Clear ASF IRQ */
+	Y2_ASF_IRQ	= 1<<0,	/* Issue an IRQ to ASF system */
+
+	Y2_ASF_UC_STATE = 3<<2,	/* ASF uC State */
+	Y2_ASF_CLK_HALT	= 0,	/* ASF system clock stopped */
+};
+
+/* B28_Y2_ASF_HOST_COM	32 bit	ASF Host Communication Reg */
+enum {
+	Y2_ASF_CLR_ASFI = 1<<1,	/* Clear host IRQ */
+	Y2_ASF_HOST_IRQ = 1<<0,	/* Issue an IRQ to HOST system */
+};
+/*	HCU_CCSR	CPU Control and Status Register */
+enum {
+	HCU_CCSR_SMBALERT_MONITOR= 1<<27, /* SMBALERT pin monitor */
+	HCU_CCSR_CPU_SLEEP	= 1<<26, /* CPU sleep status */
+	/* Clock Stretching Timeout */
+	HCU_CCSR_CS_TO		= 1<<25,
+	HCU_CCSR_WDOG		= 1<<24, /* Watchdog Reset */
+
+	HCU_CCSR_CLR_IRQ_HOST	= 1<<17, /* Clear IRQ_HOST */
+	HCU_CCSR_SET_IRQ_HCU	= 1<<16, /* Set IRQ_HCU */
+
+	HCU_CCSR_AHB_RST	= 1<<9, /* Reset AHB bridge */
+	HCU_CCSR_CPU_RST_MODE	= 1<<8, /* CPU Reset Mode */
+
+	HCU_CCSR_SET_SYNC_CPU	= 1<<5,
+	HCU_CCSR_CPU_CLK_DIVIDE_MSK = 3<<3,/* CPU Clock Divide */
+	HCU_CCSR_CPU_CLK_DIVIDE_BASE= 1<<3,
+	HCU_CCSR_OS_PRSNT	= 1<<2, /* ASF OS Present */
+/* Microcontroller State */
+	HCU_CCSR_UC_STATE_MSK	= 3,
+	HCU_CCSR_UC_STATE_BASE	= 1<<0,
+	HCU_CCSR_ASF_RESET	= 0,
+	HCU_CCSR_ASF_HALTED	= 1<<1,
+	HCU_CCSR_ASF_RUNNING	= 1<<0,
+};
+
+/*	HCU_HCSR	Host Control and Status Register */
+enum {
+	HCU_HCSR_SET_IRQ_CPU	= 1<<16, /* Set IRQ_CPU */
+
+	HCU_HCSR_CLR_IRQ_HCU	= 1<<1, /* Clear IRQ_HCU */
+	HCU_HCSR_SET_IRQ_HOST	= 1<<0,	/* Set IRQ_HOST */
+};
+
+/*	STAT_CTRL		32 bit	Status BMU control register (Yukon-2 only) */
+enum {
+	SC_STAT_CLR_IRQ	= 1<<4,	/* Status Burst IRQ clear */
+	SC_STAT_OP_ON	= 1<<3,	/* Operational Mode On */
+	SC_STAT_OP_OFF	= 1<<2,	/* Operational Mode Off */
+	SC_STAT_RST_CLR	= 1<<1,	/* Clear Status Unit Reset (Enable) */
+	SC_STAT_RST_SET	= 1<<0,	/* Set   Status Unit Reset */
+};
+
+/*	GMAC_CTRL		32 bit	GMAC Control Reg (YUKON only) */
+enum {
+	GMC_SET_RST	    = 1<<15,/* MAC SEC RST */
+	GMC_SEC_RST_OFF     = 1<<14,/* MAC SEC RSt OFF */
+	GMC_BYP_MACSECRX_ON = 1<<13,/* Bypass macsec RX */
+	GMC_BYP_MACSECRX_OFF= 1<<12,/* Bypass macsec RX off */
+	GMC_BYP_MACSECTX_ON = 1<<11,/* Bypass macsec TX */
+	GMC_BYP_MACSECTX_OFF= 1<<10,/* Bypass macsec TX  off*/
+	GMC_BYP_RETR_ON	= 1<<9, /* Bypass retransmit FIFO On */
+	GMC_BYP_RETR_OFF= 1<<8, /* Bypass retransmit FIFO Off */
+
+	GMC_H_BURST_ON	= 1<<7,	/* Half Duplex Burst Mode On */
+	GMC_H_BURST_OFF	= 1<<6,	/* Half Duplex Burst Mode Off */
+	GMC_F_LOOPB_ON	= 1<<5,	/* FIFO Loopback On */
+	GMC_F_LOOPB_OFF	= 1<<4,	/* FIFO Loopback Off */
+	GMC_PAUSE_ON	= 1<<3,	/* Pause On */
+	GMC_PAUSE_OFF	= 1<<2,	/* Pause Off */
+	GMC_RST_CLR	= 1<<1,	/* Clear GMAC Reset */
+	GMC_RST_SET	= 1<<0,	/* Set   GMAC Reset */
+};
+
+/*	GPHY_CTRL		32 bit	GPHY Control Reg (YUKON only) */
+enum {
+	GPC_TX_PAUSE	= 1<<30, /* Tx pause enabled (ro) */
+	GPC_RX_PAUSE	= 1<<29, /* Rx pause enabled (ro) */
+	GPC_SPEED	= 3<<27, /* PHY speed (ro) */
+	GPC_LINK	= 1<<26, /* Link up (ro) */
+	GPC_DUPLEX	= 1<<25, /* Duplex (ro) */
+	GPC_CLOCK	= 1<<24, /* 125Mhz clock stable (ro) */
+
+	GPC_PDOWN	= 1<<23, /* Internal regulator 2.5 power down */
+	GPC_TSTMODE	= 1<<22, /* Test mode */
+	GPC_REG18	= 1<<21, /* Reg18 Power down */
+	GPC_REG12SEL	= 3<<19, /* Reg12 power setting */
+	GPC_REG18SEL	= 3<<17, /* Reg18 power setting */
+	GPC_SPILOCK	= 1<<16, /* SPI lock (ASF) */
+
+	GPC_LEDMUX	= 3<<14, /* LED Mux */
+	GPC_INTPOL	= 1<<13, /* Interrupt polarity */
+	GPC_DETECT	= 1<<12, /* Energy detect */
+	GPC_1000HD	= 1<<11, /* Enable 1000Mbit HD */
+	GPC_SLAVE	= 1<<10, /* Slave mode */
+	GPC_PAUSE	= 1<<9, /* Pause enable */
+	GPC_LEDCTL	= 3<<6, /* GPHY Leds */
+
+	GPC_RST_CLR	= 1<<1,	/* Clear GPHY Reset */
+	GPC_RST_SET	= 1<<0,	/* Set   GPHY Reset */
+};
+
+/*	GMAC_IRQ_SRC	 8 bit	GMAC Interrupt Source Reg (YUKON only) */
+/*	GMAC_IRQ_MSK	 8 bit	GMAC Interrupt Mask   Reg (YUKON only) */
+enum {
+	GM_IS_TX_CO_OV	= 1<<5,	/* Transmit Counter Overflow IRQ */
+	GM_IS_RX_CO_OV	= 1<<4,	/* Receive Counter Overflow IRQ */
+	GM_IS_TX_FF_UR	= 1<<3,	/* Transmit FIFO Underrun */
+	GM_IS_TX_COMPL	= 1<<2,	/* Frame Transmission Complete */
+	GM_IS_RX_FF_OR	= 1<<1,	/* Receive FIFO Overrun */
+	GM_IS_RX_COMPL	= 1<<0,	/* Frame Reception Complete */
+
+#define GMAC_DEF_MSK     GM_IS_TX_FF_UR
+};
+
+/*	GMAC_LINK_CTRL	16 bit	GMAC Link Control Reg (YUKON only) */
+enum {						/* Bits 15.. 2:	reserved */
+	GMLC_RST_CLR	= 1<<1,	/* Clear GMAC Link Reset */
+	GMLC_RST_SET	= 1<<0,	/* Set   GMAC Link Reset */
+};
+
+
+/*	WOL_CTRL_STAT	16 bit	WOL Control/Status Reg */
+enum {
+	WOL_CTL_LINK_CHG_OCC		= 1<<15,
+	WOL_CTL_MAGIC_PKT_OCC		= 1<<14,
+	WOL_CTL_PATTERN_OCC		= 1<<13,
+	WOL_CTL_CLEAR_RESULT		= 1<<12,
+	WOL_CTL_ENA_PME_ON_LINK_CHG	= 1<<11,
+	WOL_CTL_DIS_PME_ON_LINK_CHG	= 1<<10,
+	WOL_CTL_ENA_PME_ON_MAGIC_PKT	= 1<<9,
+	WOL_CTL_DIS_PME_ON_MAGIC_PKT	= 1<<8,
+	WOL_CTL_ENA_PME_ON_PATTERN	= 1<<7,
+	WOL_CTL_DIS_PME_ON_PATTERN	= 1<<6,
+	WOL_CTL_ENA_LINK_CHG_UNIT	= 1<<5,
+	WOL_CTL_DIS_LINK_CHG_UNIT	= 1<<4,
+	WOL_CTL_ENA_MAGIC_PKT_UNIT	= 1<<3,
+	WOL_CTL_DIS_MAGIC_PKT_UNIT	= 1<<2,
+	WOL_CTL_ENA_PATTERN_UNIT	= 1<<1,
+	WOL_CTL_DIS_PATTERN_UNIT	= 1<<0,
+};
+
+
+/* Control flags */
+enum {
+	UDPTCP	= 1<<0,
+	CALSUM	= 1<<1,
+	WR_SUM	= 1<<2,
+	INIT_SUM= 1<<3,
+	LOCK_SUM= 1<<4,
+	INS_VLAN= 1<<5,
+	EOP	= 1<<7,
+};
+
+enum {
+	HW_OWNER 	= 1<<7,
+	OP_TCPWRITE	= 0x11,
+	OP_TCPSTART	= 0x12,
+	OP_TCPINIT	= 0x14,
+	OP_TCPLCK	= 0x18,
+	OP_TCPCHKSUM	= OP_TCPSTART,
+	OP_TCPIS	= OP_TCPINIT | OP_TCPSTART,
+	OP_TCPLW	= OP_TCPLCK | OP_TCPWRITE,
+	OP_TCPLSW	= OP_TCPLCK | OP_TCPSTART | OP_TCPWRITE,
+	OP_TCPLISW	= OP_TCPLCK | OP_TCPINIT | OP_TCPSTART | OP_TCPWRITE,
+
+	OP_ADDR64	= 0x21,
+	OP_VLAN		= 0x22,
+	OP_ADDR64VLAN	= OP_ADDR64 | OP_VLAN,
+	OP_LRGLEN	= 0x24,
+	OP_LRGLENVLAN	= OP_LRGLEN | OP_VLAN,
+	OP_MSS		= 0x28,
+	OP_MSSVLAN	= OP_MSS | OP_VLAN,
+
+	OP_BUFFER	= 0x40,
+	OP_PACKET	= 0x41,
+	OP_LARGESEND	= 0x43,
+	OP_LSOV2	= 0x45,
+
+/* YUKON-2 STATUS opcodes defines */
+	OP_RXSTAT	= 0x60,
+	OP_RXTIMESTAMP	= 0x61,
+	OP_RXVLAN	= 0x62,
+	OP_RXCHKS	= 0x64,
+	OP_RXCHKSVLAN	= OP_RXCHKS | OP_RXVLAN,
+	OP_RXTIMEVLAN	= OP_RXTIMESTAMP | OP_RXVLAN,
+	OP_RSS_HASH	= 0x65,
+	OP_TXINDEXLE	= 0x68,
+	OP_MACSEC	= 0x6c,
+	OP_PUTIDX	= 0x70,
+};
+
+enum status_css {
+	CSS_TCPUDPCSOK	= 1<<7,	/* TCP / UDP checksum is ok */
+	CSS_ISUDP	= 1<<6, /* packet is a UDP packet */
+	CSS_ISTCP	= 1<<5, /* packet is a TCP packet */
+	CSS_ISIPFRAG	= 1<<4, /* packet is a TCP/UDP frag, CS calc not done */
+	CSS_ISIPV6	= 1<<3, /* packet is a IPv6 packet */
+	CSS_IPV4CSUMOK	= 1<<2, /* IP v4: TCP header checksum is ok */
+	CSS_ISIPV4	= 1<<1, /* packet is a IPv4 packet */
+	CSS_LINK_BIT	= 1<<0, /* port number (legacy) */
+};
+
+/* Yukon 2 hardware interface */
+struct sky2_tx_le {
+	u32	addr;
+	u16	length;	/* also vlan tag or checksum start */
+	u8	ctrl;
+	u8	opcode;
+} __attribute((packed));
+
+struct sky2_rx_le {
+	u32	addr;
+	u16	length;
+	u8	ctrl;
+	u8	opcode;
+} __attribute((packed));
+
+struct sky2_status_le {
+	u32	status;	/* also checksum */
+	u16	length;	/* also vlan tag */
+	u8	css;
+	u8	opcode;
+} __attribute((packed));
+
+struct tx_ring_info {
+	struct io_buffer *iob;
+	u32 mapaddr;
+	u32 maplen;
+};
+
+struct rx_ring_info {
+	struct io_buffer *iob;
+	u32	data_addr;
+	u32	data_size;
+};
+
+enum flow_control {
+	FC_NONE	= 0,
+	FC_TX	= 1,
+	FC_RX	= 2,
+	FC_BOTH	= 3,
+};
+
+struct sky2_port {
+	struct sky2_hw	     *hw;
+	struct net_device    *netdev;
+	unsigned	     port;
+
+	struct tx_ring_info  *tx_ring;
+	struct sky2_tx_le    *tx_le;
+	u16		     tx_cons;		/* next le to check */
+	u16		     tx_prod;		/* next le to use */
+
+	struct rx_ring_info  *rx_ring;
+	struct sky2_rx_le    *rx_le;
+
+	u16		     rx_next;		/* next re to check */
+	u16		     rx_put;		/* next le index to use */
+	u16		     rx_data_size;
+
+	u32		     rx_le_map;
+	u32		     tx_le_map;
+	u16		     advertising;	/* ADVERTISED_ bits */
+	u16		     speed;	/* SPEED_1000, SPEED_100, ... */
+	u8		     autoneg;	/* AUTONEG_ENABLE, AUTONEG_DISABLE */
+	u8		     duplex;	/* DUPLEX_HALF, DUPLEX_FULL */
+	enum flow_control    flow_mode;
+	enum flow_control    flow_status;
+};
+
+struct sky2_hw {
+	unsigned long	     regs;
+	struct pci_device    *pdev;
+	struct net_device    *dev[2];
+	unsigned long	     flags;
+#define SKY2_HW_USE_MSI		0x00000001
+#define SKY2_HW_FIBRE_PHY	0x00000002
+#define SKY2_HW_GIGABIT		0x00000004
+#define SKY2_HW_NEWER_PHY	0x00000008
+#define SKY2_HW_RAM_BUFFER	0x00000010
+#define SKY2_HW_NEW_LE		0x00000020	/* new LSOv2 format */
+#define SKY2_HW_AUTO_TX_SUM	0x00000040	/* new IP decode for Tx */
+#define SKY2_HW_ADV_POWER_CTL	0x00000080	/* additional PHY power regs */
+
+	u8	     	     chip_id;
+	u8		     chip_rev;
+	u8		     pmd_type;
+	u8		     ports;
+
+	struct sky2_status_le *st_le;
+	u32		     st_idx;
+	u32		     st_dma;
+};
+
+static inline int sky2_is_copper(const struct sky2_hw *hw)
+{
+	return !(hw->flags & SKY2_HW_FIBRE_PHY);
+}
+
+/* Register accessor for memory mapped device */
+static inline u32 sky2_read32(const struct sky2_hw *hw, unsigned reg)
+{
+	return readl(hw->regs + reg);
+}
+
+static inline u16 sky2_read16(const struct sky2_hw *hw, unsigned reg)
+{
+	return readw(hw->regs + reg);
+}
+
+static inline u8 sky2_read8(const struct sky2_hw *hw, unsigned reg)
+{
+	return readb(hw->regs + reg);
+}
+
+static inline void sky2_write32(const struct sky2_hw *hw, unsigned reg, u32 val)
+{
+	writel(val, hw->regs + reg);
+}
+
+static inline void sky2_write16(const struct sky2_hw *hw, unsigned reg, u16 val)
+{
+	writew(val, hw->regs + reg);
+}
+
+static inline void sky2_write8(const struct sky2_hw *hw, unsigned reg, u8 val)
+{
+	writeb(val, hw->regs + reg);
+}
+
+/* Yukon PHY related registers */
+#define SK_GMAC_REG(port,reg) \
+	(BASE_GMAC_1 + (port) * (BASE_GMAC_2-BASE_GMAC_1) + (reg))
+#define GM_PHY_RETRIES	100
+
+static inline u16 gma_read16(const struct sky2_hw *hw, unsigned port, unsigned reg)
+{
+	return sky2_read16(hw, SK_GMAC_REG(port,reg));
+}
+
+static inline u32 gma_read32(struct sky2_hw *hw, unsigned port, unsigned reg)
+{
+	unsigned base = SK_GMAC_REG(port, reg);
+	return (u32) sky2_read16(hw, base)
+		| (u32) sky2_read16(hw, base+4) << 16;
+}
+
+static inline void gma_write16(const struct sky2_hw *hw, unsigned port, int r, u16 v)
+{
+	sky2_write16(hw, SK_GMAC_REG(port,r), v);
+}
+
+static inline void gma_set_addr(struct sky2_hw *hw, unsigned port, unsigned reg,
+				    const u8 *addr)
+{
+	gma_write16(hw, port, reg,  (u16) addr[0] | ((u16) addr[1] << 8));
+	gma_write16(hw, port, reg+4,(u16) addr[2] | ((u16) addr[3] << 8));
+	gma_write16(hw, port, reg+8,(u16) addr[4] | ((u16) addr[5] << 8));
+}
+
+/* PCI config space access */
+static inline u32 sky2_pci_read32(const struct sky2_hw *hw, unsigned reg)
+{
+	return sky2_read32(hw, Y2_CFG_SPC + reg);
+}
+
+static inline u16 sky2_pci_read16(const struct sky2_hw *hw, unsigned reg)
+{
+	return sky2_read16(hw, Y2_CFG_SPC + reg);
+}
+
+static inline void sky2_pci_write32(struct sky2_hw *hw, unsigned reg, u32 val)
+{
+	sky2_write32(hw, Y2_CFG_SPC + reg, val);
+}
+
+static inline void sky2_pci_write16(struct sky2_hw *hw, unsigned reg, u16 val)
+{
+	sky2_write16(hw, Y2_CFG_SPC + reg, val);
+}
+#endif
diff --git a/gpxe/src/drivers/net/smc9000.c b/gpxe/src/drivers/net/smc9000.c
index 31a1e1b..cfbf104 100644
--- a/gpxe/src/drivers/net/smc9000.c
+++ b/gpxe/src/drivers/net/smc9000.c
@@ -26,6 +26,9 @@
  *                                       Linux driver.
  *
  *---------------------------------------------------------------------------*/
+
+FILE_LICENCE ( GPL_ANY );
+
 #define LINUX_OUT_MACROS 1
 #define SMC9000_DEBUG    0
 
diff --git a/gpxe/src/drivers/net/smc9000.h b/gpxe/src/drivers/net/smc9000.h
index 318b779..22b0e18 100644
--- a/gpxe/src/drivers/net/smc9000.h
+++ b/gpxe/src/drivers/net/smc9000.h
@@ -28,6 +28,9 @@
  * 98-09-27              Daniel Engström moved some static strings back to the
  *                                       main .c file
  * --------------------------------------------------------------------------*/
+
+FILE_LICENCE ( GPL_ANY );
+
 #ifndef	_SMC9000_H_
 # define _SMC9000_H_
 
@@ -228,14 +231,6 @@ typedef unsigned long int		dword;
 #define RS_MULTICAST	0x0001
 #define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
 
-// Management Interface Register (MII)
-#define MII_REG         0x0008
-#define MII_MSK_CRS100  0x4000 // Disables CRS100 detection during tx half dup
-#define MII_MDOE        0x0008 // MII Output Enable
-#define MII_MCLK        0x0004 // MII Clock, pin MDCLK
-#define MII_MDI         0x0002 // MII Input, pin MDI
-#define MII_MDO         0x0001 // MII Output, pin MDO
-
 // PHY Register Addresses (LAN91C111 Internal PHY)
 
 // PHY Control Register
diff --git a/gpxe/src/drivers/net/sundance.c b/gpxe/src/drivers/net/sundance.c
index 119c977..c446ac0 100644
--- a/gpxe/src/drivers/net/sundance.c
+++ b/gpxe/src/drivers/net/sundance.c
@@ -40,6 +40,8 @@
 *
 ****************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* to get some global routines like printf */
 #include "etherboot.h"
 /* to get the interface to the body of the program */
@@ -57,13 +59,6 @@
 #define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
 #define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
 
-/* May need to be moved to mii.h */
-struct mii_if_info {
-	int phy_id;
-	int advertising;
-	unsigned int full_duplex:1;	/* is full duplex? */
-};
-
 /* Set the mtu */
 static int mtu = 1514;
 
@@ -883,9 +878,9 @@ static void set_rx_mode(struct nic *nic __unused)
 }
 
 static struct pci_device_id sundance_nics[] = {
-	PCI_ROM(0x13f0, 0x0201, "sundance", "ST201 Sundance 'Alta' based Adaptor"),
-	PCI_ROM(0x1186, 0x1002, "dfe530txs", "D-Link DFE530TXS (Sundance ST201 Alta)"),
-	PCI_ROM(0x13f0, 0x0200, "ip100a", "IC+ IP100A"),
+	PCI_ROM(0x13f0, 0x0201, "sundance", "ST201 Sundance 'Alta' based Adaptor", 0),
+	PCI_ROM(0x1186, 0x1002, "dfe530txs", "D-Link DFE530TXS (Sundance ST201 Alta)", 0),
+	PCI_ROM(0x13f0, 0x0200, "ip100a", "IC+ IP100A", 0),
 };
 
 PCI_DRIVER ( sundance_driver, sundance_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/tg3.c b/gpxe/src/drivers/net/tg3.c
index 14093bb..20027b2 100644
--- a/gpxe/src/drivers/net/tg3.c
+++ b/gpxe/src/drivers/net/tg3.c
@@ -6,6 +6,8 @@
  * Copyright (C) 2003 Eric Biederman (ebiederman at lnxi.com)  [etherboot port]
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /* 11-13-2003	timlegge	Fix Issue with NetGear GA302T 
  * 11-18-2003   ebiederm        Generalize NetGear Fix to what the code was supposed to be.
  * 01-06-2005   Alf (Frederic Olivie) Add Dell bcm 5751 (0x1677) support
@@ -18,6 +20,7 @@
 #include <gpxe/pci.h>
 #include <gpxe/ethernet.h>
 #include "string.h"
+#include <mii.h>
 #include "tg3.h"
 
 #define SUPPORT_COPPER_PHY  1
@@ -3384,38 +3387,38 @@ static int tg3_probe ( struct nic *nic, struct pci_device *pdev ) {
 
 
 static struct pci_device_id tg3_nics[] = {
-PCI_ROM(0x14e4, 0x1644, "tg3-5700",        "Broadcom Tigon 3 5700"),
-PCI_ROM(0x14e4, 0x1645, "tg3-5701",        "Broadcom Tigon 3 5701"),
-PCI_ROM(0x14e4, 0x1646, "tg3-5702",        "Broadcom Tigon 3 5702"),
-PCI_ROM(0x14e4, 0x1647, "tg3-5703",        "Broadcom Tigon 3 5703"),
-PCI_ROM(0x14e4, 0x1648, "tg3-5704",        "Broadcom Tigon 3 5704"),
-PCI_ROM(0x14e4, 0x164d, "tg3-5702FE",      "Broadcom Tigon 3 5702FE"),
-PCI_ROM(0x14e4, 0x1653, "tg3-5705",        "Broadcom Tigon 3 5705"),
-PCI_ROM(0x14e4, 0x1654, "tg3-5705_2",      "Broadcom Tigon 3 5705_2"),
-PCI_ROM(0x14e4, 0x1659, "tg3-5721",        "Broadcom Tigon 3 5721"),
-PCI_ROM(0x14e4, 0x165d, "tg3-5705M",       "Broadcom Tigon 3 5705M"),
-PCI_ROM(0x14e4, 0x165e, "tg3-5705M_2",     "Broadcom Tigon 3 5705M_2"),
-PCI_ROM(0x14e4, 0x1677, "tg3-5751",        "Broadcom Tigon 3 5751"),
-PCI_ROM(0x14e4, 0x167a, "tg3-5754",        "Broadcom Tigon 3 5754"),
-PCI_ROM(0x14e4, 0x1693, "tg3-5787",	   "Broadcom Tigon 3 5787"),
-PCI_ROM(0x14e4, 0x1696, "tg3-5782",        "Broadcom Tigon 3 5782"),
-PCI_ROM(0x14e4, 0x169a, "tg3-5786",        "Broadcom Tigon 3 5786"),
-PCI_ROM(0x14e4, 0x169c, "tg3-5788",        "Broadcom Tigon 3 5788"),
-PCI_ROM(0x14e4, 0x169d, "tg3-5789",        "Broadcom Tigon 3 5789"),
-PCI_ROM(0x14e4, 0x16a6, "tg3-5702X",       "Broadcom Tigon 3 5702X"),
-PCI_ROM(0x14e4, 0x16a7, "tg3-5703X",       "Broadcom Tigon 3 5703X"),
-PCI_ROM(0x14e4, 0x16a8, "tg3-5704S",       "Broadcom Tigon 3 5704S"),
-PCI_ROM(0x14e4, 0x16c6, "tg3-5702A3",      "Broadcom Tigon 3 5702A3"),
-PCI_ROM(0x14e4, 0x16c7, "tg3-5703A3",      "Broadcom Tigon 3 5703A3"),
-PCI_ROM(0x14e4, 0x170d, "tg3-5901",        "Broadcom Tigon 3 5901"),
-PCI_ROM(0x14e4, 0x170e, "tg3-5901_2",      "Broadcom Tigon 3 5901_2"),
-PCI_ROM(0x1148, 0x4400, "tg3-9DXX",        "Syskonnect 9DXX"),
-PCI_ROM(0x1148, 0x4500, "tg3-9MXX",        "Syskonnect 9MXX"),
-PCI_ROM(0x173b, 0x03e8, "tg3-ac1000",      "Altima AC1000"),
-PCI_ROM(0x173b, 0x03e9, "tg3-ac1001",      "Altima AC1001"),
-PCI_ROM(0x173b, 0x03ea, "tg3-ac9100",      "Altima AC9100"),
-PCI_ROM(0x173b, 0x03eb, "tg3-ac1003",      "Altima AC1003"),
-PCI_ROM(0x0e11, 0x00ca, "tg3-hp",	   "HP Tigon 3"),
+PCI_ROM(0x14e4, 0x1644, "tg3-5700",        "Broadcom Tigon 3 5700", 0),
+PCI_ROM(0x14e4, 0x1645, "tg3-5701",        "Broadcom Tigon 3 5701", 0),
+PCI_ROM(0x14e4, 0x1646, "tg3-5702",        "Broadcom Tigon 3 5702", 0),
+PCI_ROM(0x14e4, 0x1647, "tg3-5703",        "Broadcom Tigon 3 5703", 0),
+PCI_ROM(0x14e4, 0x1648, "tg3-5704",        "Broadcom Tigon 3 5704", 0),
+PCI_ROM(0x14e4, 0x164d, "tg3-5702FE",      "Broadcom Tigon 3 5702FE", 0),
+PCI_ROM(0x14e4, 0x1653, "tg3-5705",        "Broadcom Tigon 3 5705", 0),
+PCI_ROM(0x14e4, 0x1654, "tg3-5705_2",      "Broadcom Tigon 3 5705_2", 0),
+PCI_ROM(0x14e4, 0x1659, "tg3-5721",        "Broadcom Tigon 3 5721", 0),
+PCI_ROM(0x14e4, 0x165d, "tg3-5705M",       "Broadcom Tigon 3 5705M", 0),
+PCI_ROM(0x14e4, 0x165e, "tg3-5705M_2",     "Broadcom Tigon 3 5705M_2", 0),
+PCI_ROM(0x14e4, 0x1677, "tg3-5751",        "Broadcom Tigon 3 5751", 0),
+PCI_ROM(0x14e4, 0x167a, "tg3-5754",        "Broadcom Tigon 3 5754", 0),
+PCI_ROM(0x14e4, 0x1693, "tg3-5787",	   "Broadcom Tigon 3 5787", 0),
+PCI_ROM(0x14e4, 0x1696, "tg3-5782",        "Broadcom Tigon 3 5782", 0),
+PCI_ROM(0x14e4, 0x169a, "tg3-5786",        "Broadcom Tigon 3 5786", 0),
+PCI_ROM(0x14e4, 0x169c, "tg3-5788",        "Broadcom Tigon 3 5788", 0),
+PCI_ROM(0x14e4, 0x169d, "tg3-5789",        "Broadcom Tigon 3 5789", 0),
+PCI_ROM(0x14e4, 0x16a6, "tg3-5702X",       "Broadcom Tigon 3 5702X", 0),
+PCI_ROM(0x14e4, 0x16a7, "tg3-5703X",       "Broadcom Tigon 3 5703X", 0),
+PCI_ROM(0x14e4, 0x16a8, "tg3-5704S",       "Broadcom Tigon 3 5704S", 0),
+PCI_ROM(0x14e4, 0x16c6, "tg3-5702A3",      "Broadcom Tigon 3 5702A3", 0),
+PCI_ROM(0x14e4, 0x16c7, "tg3-5703A3",      "Broadcom Tigon 3 5703A3", 0),
+PCI_ROM(0x14e4, 0x170d, "tg3-5901",        "Broadcom Tigon 3 5901", 0),
+PCI_ROM(0x14e4, 0x170e, "tg3-5901_2",      "Broadcom Tigon 3 5901_2", 0),
+PCI_ROM(0x1148, 0x4400, "tg3-9DXX",        "Syskonnect 9DXX", 0),
+PCI_ROM(0x1148, 0x4500, "tg3-9MXX",        "Syskonnect 9MXX", 0),
+PCI_ROM(0x173b, 0x03e8, "tg3-ac1000",      "Altima AC1000", 0),
+PCI_ROM(0x173b, 0x03e9, "tg3-ac1001",      "Altima AC1001", 0),
+PCI_ROM(0x173b, 0x03ea, "tg3-ac9100",      "Altima AC9100", 0),
+PCI_ROM(0x173b, 0x03eb, "tg3-ac1003",      "Altima AC1003", 0),
+PCI_ROM(0x0e11, 0x00ca, "tg3-hp",	   "HP Tigon 3", 0),
 };
 
 PCI_DRIVER ( tg3_driver, tg3_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/tg3.h b/gpxe/src/drivers/net/tg3.h
index d1c09e0..deeb0ad 100644
--- a/gpxe/src/drivers/net/tg3.h
+++ b/gpxe/src/drivers/net/tg3.h
@@ -5,6 +5,8 @@
  * Copyright (C) 2001 Jeff Garzik (jgarzik at mandrakesoft.com)
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 #ifndef _T3_H
 #define _T3_H
 
@@ -75,100 +77,6 @@ typedef unsigned long dma_addr_t;
 #define WAKE_MAGIC		(1 << 5)
 #define WAKE_MAGICSECURE	(1 << 6) /* only meaningful if WAKE_MAGIC */
 
-/* Generic MII registers. */
-
-#define MII_BMCR            0x00        /* Basic mode control register */
-#define MII_BMSR            0x01        /* Basic mode status register  */
-#define MII_PHYSID1         0x02        /* PHYS ID 1                   */
-#define MII_PHYSID2         0x03        /* PHYS ID 2                   */
-#define MII_ADVERTISE       0x04        /* Advertisement control reg   */
-#define MII_LPA             0x05        /* Link partner ability reg    */
-#define MII_EXPANSION       0x06        /* Expansion register          */
-#define MII_DCOUNTER        0x12        /* Disconnect counter          */
-#define MII_FCSCOUNTER      0x13        /* False carrier counter       */
-#define MII_NWAYTEST        0x14        /* N-way auto-neg test reg     */
-#define MII_RERRCOUNTER     0x15        /* Receive error counter       */
-#define MII_SREVISION       0x16        /* Silicon revision            */
-#define MII_RESV1           0x17        /* Reserved...                 */
-#define MII_LBRERROR        0x18        /* Lpback, rx, bypass error    */
-#define MII_PHYADDR         0x19        /* PHY address                 */
-#define MII_RESV2           0x1a        /* Reserved...                 */
-#define MII_TPISTATUS       0x1b        /* TPI status for 10mbps       */
-#define MII_NCONFIG         0x1c        /* Network interface config    */
-
-/* Basic mode control register. */
-#define BMCR_RESV               0x007f  /* Unused...                   */
-#define BMCR_CTST               0x0080  /* Collision test              */
-#define BMCR_FULLDPLX           0x0100  /* Full duplex                 */
-#define BMCR_ANRESTART          0x0200  /* Auto negotiation restart    */
-#define BMCR_ISOLATE            0x0400  /* Disconnect DP83840 from MII */
-#define BMCR_PDOWN              0x0800  /* Powerdown the DP83840       */
-#define BMCR_ANENABLE           0x1000  /* Enable auto negotiation     */
-#define BMCR_SPEED100           0x2000  /* Select 100Mbps              */
-#define BMCR_LOOPBACK           0x4000  /* TXD loopback bits           */
-#define BMCR_RESET              0x8000  /* Reset the DP83840           */
-
-/* Basic mode status register. */
-#define BMSR_ERCAP              0x0001  /* Ext-reg capability          */
-#define BMSR_JCD                0x0002  /* Jabber detected             */
-#define BMSR_LSTATUS            0x0004  /* Link status                 */
-#define BMSR_ANEGCAPABLE        0x0008  /* Able to do auto-negotiation */
-#define BMSR_RFAULT             0x0010  /* Remote fault detected       */
-#define BMSR_ANEGCOMPLETE       0x0020  /* Auto-negotiation complete   */
-#define BMSR_RESV               0x07c0  /* Unused...                   */
-#define BMSR_10HALF             0x0800  /* Can do 10mbps, half-duplex  */
-#define BMSR_10FULL             0x1000  /* Can do 10mbps, full-duplex  */
-#define BMSR_100HALF            0x2000  /* Can do 100mbps, half-duplex */
-#define BMSR_100FULL            0x4000  /* Can do 100mbps, full-duplex */
-#define BMSR_100BASE4           0x8000  /* Can do 100mbps, 4k packets  */
-
-/* Advertisement control register. */
-#define ADVERTISE_SLCT          0x001f  /* Selector bits               */
-#define ADVERTISE_CSMA          0x0001  /* Only selector supported     */
-#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
-#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
-#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
-#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
-#define ADVERTISE_100BASE4      0x0200  /* Try for 100mbps 4k packets  */
-#define ADVERTISE_RESV          0x1c00  /* Unused...                   */
-#define ADVERTISE_RFAULT        0x2000  /* Say we can detect faults    */
-#define ADVERTISE_LPACK         0x4000  /* Ack link partners response  */
-#define ADVERTISE_NPAGE         0x8000  /* Next page bit               */
-
-#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
-			ADVERTISE_CSMA)
-#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
-                       ADVERTISE_100HALF | ADVERTISE_100FULL)
-
-/* Link partner ability register. */
-#define LPA_SLCT                0x001f  /* Same as advertise selector  */
-#define LPA_10HALF              0x0020  /* Can do 10mbps half-duplex   */
-#define LPA_10FULL              0x0040  /* Can do 10mbps full-duplex   */
-#define LPA_100HALF             0x0080  /* Can do 100mbps half-duplex  */
-#define LPA_100FULL             0x0100  /* Can do 100mbps full-duplex  */
-#define LPA_100BASE4            0x0200  /* Can do 100mbps 4k packets   */
-#define LPA_RESV                0x1c00  /* Unused...                   */
-#define LPA_RFAULT              0x2000  /* Link partner faulted        */
-#define LPA_LPACK               0x4000  /* Link partner acked us       */
-#define LPA_NPAGE               0x8000  /* Next page bit               */
-
-#define LPA_DUPLEX		(LPA_10FULL | LPA_100FULL)
-#define LPA_100			(LPA_100FULL | LPA_100HALF | LPA_100BASE4)
-
-/* Expansion register for auto-negotiation. */
-#define EXPANSION_NWAY          0x0001  /* Can do N-way auto-nego      */
-#define EXPANSION_LCWP          0x0002  /* Got new RX page code word   */
-#define EXPANSION_ENABLENPAGE   0x0004  /* This enables npage words    */
-#define EXPANSION_NPCAPABLE     0x0008  /* Link partner supports npage */
-#define EXPANSION_MFAULTS       0x0010  /* Multiple faults detected    */
-#define EXPANSION_RESV          0xffe0  /* Unused...                   */
-
-/* N-way test register. */
-#define NWAYTEST_RESV1          0x00ff  /* Unused...                   */
-#define NWAYTEST_LOOPBACK       0x0100  /* Enable loopback for N-way   */
-#define NWAYTEST_RESV2          0xfe00  /* Unused...                   */
-
-
 /* From tg3.h */
 
 #define TG3_64BIT_REG_HIGH		0x00UL
@@ -1632,19 +1540,6 @@ typedef unsigned long dma_addr_t;
 #define MII_TG3_INT_DUPLEXCHG		0x0008
 #define MII_TG3_INT_ANEG_PAGE_RX	0x0400
 
-/* XXX Add this to mii.h */
-#ifndef ADVERTISE_PAUSE
-#define ADVERTISE_PAUSE_CAP		0x0400
-#endif
-#ifndef ADVERTISE_PAUSE_ASYM
-#define ADVERTISE_PAUSE_ASYM		0x0800
-#endif
-#ifndef LPA_PAUSE
-#define LPA_PAUSE_CAP			0x0400
-#endif
-#ifndef LPA_PAUSE_ASYM
-#define LPA_PAUSE_ASYM			0x0800
-#endif
 
 /* There are two ways to manage the TX descriptors on the tigon3.
  * Either the descriptors are in host DMA'able memory, or they
diff --git a/gpxe/src/drivers/net/tlan.c b/gpxe/src/drivers/net/tlan.c
index 4fae017..e5f04fa 100644
--- a/gpxe/src/drivers/net/tlan.c
+++ b/gpxe/src/drivers/net/tlan.c
@@ -38,10 +38,13 @@
 *    Indent Options: indent -kr -i8
 ***************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "etherboot.h"
 #include "nic.h"
 #include <gpxe/pci.h>
 #include <gpxe/ethernet.h>
+#include <mii.h>
 #include "tlan.h"
 
 #define drv_version "v1.4"
@@ -398,21 +401,21 @@ void TLan_FinishReset(struct nic *nic)
 	}
 	TLan_DioWrite8(BASE, TLAN_NET_MASK, data);
 	TLan_DioWrite16(BASE, TLAN_MAX_RX, ((1536) + 7) & ~7);
-	TLan_MiiReadReg(nic, phy, MII_GEN_ID_HI, &tlphy_id1);
-	TLan_MiiReadReg(nic, phy, MII_GEN_ID_LO, &tlphy_id2);
+	TLan_MiiReadReg(nic, phy, MII_PHYSID1, &tlphy_id1);
+	TLan_MiiReadReg(nic, phy, MII_PHYSID2, &tlphy_id2);
 
 	if ((tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY)
 	    || (priv->aui)) {
-		status = MII_GS_LINK;
+		status = BMSR_LSTATUS;
 		DBG ( "TLAN:  %s: Link forced.\n", priv->nic_name );
 	} else {
-		TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+		TLan_MiiReadReg(nic, phy, MII_BMSR, &status);
 		udelay(1000);
-		TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
-		if ((status & MII_GS_LINK) &&	/* We only support link info on Nat.Sem. PHY's */
+		TLan_MiiReadReg(nic, phy, MII_BMSR, &status);
+		if ((status & BMSR_LSTATUS) &&	/* We only support link info on Nat.Sem. PHY's */
 		    (tlphy_id1 == NAT_SEM_ID1)
 		    && (tlphy_id2 == NAT_SEM_ID2)) {
-			TLan_MiiReadReg(nic, phy, MII_AN_LPA, &partner);
+			TLan_MiiReadReg(nic, phy, MII_LPA, &partner);
 			TLan_MiiReadReg(nic, phy, TLAN_TLPHY_PAR,
 					&tlphy_par);
 
@@ -448,7 +451,7 @@ void TLan_FinishReset(struct nic *nic)
 			mdelay(10000);
 			TLan_PhyMonitor(nic);
 #endif
-		} else if (status & MII_GS_LINK) {
+		} else if (status & BMSR_LSTATUS) {
 			DBG ( "TLAN: %s: Link active\n", priv->nic_name );
 			TLan_DioWrite8(BASE, TLAN_LED_REG, TLAN_LED_LINK);
 		}
@@ -463,7 +466,7 @@ void TLan_FinishReset(struct nic *nic)
 		TLan_DioWrite8(BASE, TLAN_NET_SIO, sio);
 	}
 
-	if (status & MII_GS_LINK) {
+	if (status & BMSR_LSTATUS) {
 		TLan_SetMac(nic, 0, nic->node_addr);
 		priv->phyOnline = 1;
 		outb((TLAN_HC_INT_ON >> 8), BASE + TLAN_HOST_CMD + 1);
@@ -1344,7 +1347,7 @@ void TLan_PhyDetect(struct nic *nic)
 		return;
 	}
 
-	TLan_MiiReadReg(nic, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi);
+	TLan_MiiReadReg(nic, TLAN_PHY_MAX_ADDR, MII_PHYSID1, &hi);
 
 	if (hi != 0xFFFF) {
 		priv->phy[0] = TLAN_PHY_MAX_ADDR;
@@ -1354,9 +1357,9 @@ void TLan_PhyDetect(struct nic *nic)
 
 	priv->phy[1] = TLAN_PHY_NONE;
 	for (phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++) {
-		TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &control);
-		TLan_MiiReadReg(nic, phy, MII_GEN_ID_HI, &hi);
-		TLan_MiiReadReg(nic, phy, MII_GEN_ID_LO, &lo);
+		TLan_MiiReadReg(nic, phy, MII_BMCR, &control);
+		TLan_MiiReadReg(nic, phy, MII_PHYSID1, &hi);
+		TLan_MiiReadReg(nic, phy, MII_PHYSID2, &lo);
 		if ((control != 0xFFFF) || (hi != 0xFFFF)
 		    || (lo != 0xFFFF)) {
 			printf("PHY found at %hX %hX %hX %hX\n", 
@@ -1384,15 +1387,15 @@ void TLan_PhyPowerDown(struct nic *nic)
 
 	u16 value;
 	DBG ( "%s: Powering down PHY(s).\n", priv->nic_name );
-	value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE;
+	value = BMCR_PDOWN | BMCR_LOOPBACK | BMCR_ISOLATE;
 	TLan_MiiSync(BASE);
-	TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_GEN_CTL, value);
+	TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_BMCR, value);
 	if ((priv->phyNum == 0) && (priv->phy[1] != TLAN_PHY_NONE)
 	    &&
 	    (!(tlan_pci_tbl[chip_idx].
 	       flags & TLAN_ADAPTER_USE_INTERN_10))) {
 		TLan_MiiSync(BASE);
-		TLan_MiiWriteReg(nic, priv->phy[1], MII_GEN_CTL, value);
+		TLan_MiiWriteReg(nic, priv->phy[1], MII_BMCR, value);
 	}
 
 	/* Wait for 50 ms and powerup
@@ -1412,8 +1415,8 @@ void TLan_PhyPowerUp(struct nic *nic)
 
 	DBG ( "%s: Powering up PHY.\n", priv->nic_name );
 	TLan_MiiSync(BASE);
-	value = MII_GC_LOOPBK;
-	TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_GEN_CTL, value);
+	value = BMCR_LOOPBACK;
+	TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_BMCR, value);
 	TLan_MiiSync(BASE);
 	/* Wait for 500 ms and reset the
 	 * tranceiver.  The TLAN docs say both 50 ms and
@@ -1434,11 +1437,11 @@ void TLan_PhyReset(struct nic *nic)
 
 	DBG ( "%s: Reseting PHY.\n", priv->nic_name );
 	TLan_MiiSync(BASE);
-	value = MII_GC_LOOPBK | MII_GC_RESET;
-	TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, value);
-	TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &value);
-	while (value & MII_GC_RESET) {
-		TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &value);
+	value = BMCR_LOOPBACK | BMCR_RESET;
+	TLan_MiiWriteReg(nic, phy, MII_BMCR, value);
+	TLan_MiiReadReg(nic, phy, MII_BMCR, &value);
+	while (value & BMCR_RESET) {
+		TLan_MiiReadReg(nic, phy, MII_BMCR, &value);
 	}
 
 	/* Wait for 500 ms and initialize.
@@ -1464,34 +1467,34 @@ void TLan_PhyStartLink(struct nic *nic)
 
 	phy = priv->phy[priv->phyNum];
 	DBG ( "%s: Trying to activate link.\n", priv->nic_name );
-	TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
-	TLan_MiiReadReg(nic, phy, MII_GEN_STS, &ability);
+	TLan_MiiReadReg(nic, phy, MII_BMSR, &status);
+	TLan_MiiReadReg(nic, phy, MII_BMSR, &ability);
 
-	if ((status & MII_GS_AUTONEG) && (!priv->aui)) {
+	if ((status & BMSR_ANEGCAPABLE) && (!priv->aui)) {
 		ability = status >> 11;
 		if (priv->speed == TLAN_SPEED_10 &&
 		    priv->duplex == TLAN_DUPLEX_HALF) {
-			TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x0000);
+			TLan_MiiWriteReg(nic, phy, MII_BMCR, 0x0000);
 		} else if (priv->speed == TLAN_SPEED_10 &&
 			   priv->duplex == TLAN_DUPLEX_FULL) {
 			priv->tlanFullDuplex = TRUE;
-			TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x0100);
+			TLan_MiiWriteReg(nic, phy, MII_BMCR, 0x0100);
 		} else if (priv->speed == TLAN_SPEED_100 &&
 			   priv->duplex == TLAN_DUPLEX_HALF) {
-			TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x2000);
+			TLan_MiiWriteReg(nic, phy, MII_BMCR, 0x2000);
 		} else if (priv->speed == TLAN_SPEED_100 &&
 			   priv->duplex == TLAN_DUPLEX_FULL) {
 			priv->tlanFullDuplex = TRUE;
-			TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x2100);
+			TLan_MiiWriteReg(nic, phy, MII_BMCR, 0x2100);
 		} else {
 
 			/* Set Auto-Neg advertisement */
-			TLan_MiiWriteReg(nic, phy, MII_AN_ADV,
+			TLan_MiiWriteReg(nic, phy, MII_ADVERTISE,
 					 (ability << 5) | 1);
 			/* Enablee Auto-Neg */
-			TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x1000);
+			TLan_MiiWriteReg(nic, phy, MII_BMCR, 0x1000);
 			/* Restart Auto-Neg */
-			TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x1200);
+			TLan_MiiWriteReg(nic, phy, MII_BMCR, 0x1200);
 			/* Wait for 4 sec for autonegotiation
 			 * to complete.  The max spec time is less than this
 			 * but the card need additional time to start AN.
@@ -1525,14 +1528,14 @@ void TLan_PhyStartLink(struct nic *nic)
 		} else {
 			tctl &= ~TLAN_TC_AUISEL;
 			if (priv->duplex == TLAN_DUPLEX_FULL) {
-				control |= MII_GC_DUPLEX;
+				control |= BMCR_FULLDPLX;
 				priv->tlanFullDuplex = TRUE;
 			}
 			if (priv->speed == TLAN_SPEED_100) {
-				control |= MII_GC_SPEEDSEL;
+				control |= BMCR_SPEED100;
 			}
 		}
-		TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, control);
+		TLan_MiiWriteReg(nic, phy, MII_BMCR, control);
 		TLan_MiiWriteReg(nic, phy, TLAN_TLPHY_CTL, tctl);
 	}
 
@@ -1557,11 +1560,11 @@ void TLan_PhyFinishAutoNeg(struct nic *nic)
 
 	phy = priv->phy[priv->phyNum];
 
-	TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+	TLan_MiiReadReg(nic, phy, MII_BMSR, &status);
 	udelay(1000);
-	TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+	TLan_MiiReadReg(nic, phy, MII_BMSR, &status);
 
-	if (!(status & MII_GS_AUTOCMPLT)) {
+	if (!(status & BMSR_ANEGCOMPLETE)) {
 		/* Wait for 8 sec to give the process
 		 * more time.  Perhaps we should fail after a while.
 		 */
@@ -1582,8 +1585,8 @@ void TLan_PhyFinishAutoNeg(struct nic *nic)
 	}
 
 	DBG ( "TLAN: %s: Autonegotiation complete.\n", priv->nic_name );
-	TLan_MiiReadReg(nic, phy, MII_AN_ADV, &an_adv);
-	TLan_MiiReadReg(nic, phy, MII_AN_LPA, &an_lpa);
+	TLan_MiiReadReg(nic, phy, MII_ADVERTISE, &an_adv);
+	TLan_MiiReadReg(nic, phy, MII_LPA, &an_lpa);
 	mode = an_adv & an_lpa & 0x03E0;
 	if (mode & 0x0100) {
 		printf("Full Duplex\n");
@@ -1610,13 +1613,13 @@ void TLan_PhyFinishAutoNeg(struct nic *nic)
 	if (priv->phyNum == 0) {
 		if ((priv->duplex == TLAN_DUPLEX_FULL)
 		    || (an_adv & an_lpa & 0x0040)) {
-			TLan_MiiWriteReg(nic, phy, MII_GEN_CTL,
-					 MII_GC_AUTOENB | MII_GC_DUPLEX);
+			TLan_MiiWriteReg(nic, phy, MII_BMCR,
+					 BMCR_ANENABLE | BMCR_FULLDPLX);
 			DBG 
 			    ( "TLAN:  Starting internal PHY with FULL-DUPLEX\n" );
 		} else {
-			TLan_MiiWriteReg(nic, phy, MII_GEN_CTL,
-					 MII_GC_AUTOENB);
+			TLan_MiiWriteReg(nic, phy, MII_BMCR,
+					 BMCR_ANENABLE);
 			DBG 
 			    ( "TLAN:  Starting internal PHY with HALF-DUPLEX\n" );
 		}
@@ -1659,10 +1662,10 @@ void TLan_PhyMonitor(struct net_device *dev)
 	phy = priv->phy[priv->phyNum];
 
 	/* Get PHY status register */
-	TLan_MiiReadReg(nic, phy, MII_GEN_STS, &phy_status);
+	TLan_MiiReadReg(nic, phy, MII_BMSR, &phy_status);
 
 	/* Check if link has been lost */
-	if (!(phy_status & MII_GS_LINK)) {
+	if (!(phy_status & BMSR_LSTATUS)) {
 		if (priv->link) {
 			priv->link = 0;
 			printf("TLAN: %s has lost link\n", priv->nic_name);
@@ -1675,7 +1678,7 @@ void TLan_PhyMonitor(struct net_device *dev)
 	}
 
 	/* Link restablished? */
-	if ((phy_status & MII_GS_LINK) && !priv->link) {
+	if ((phy_status & BMSR_LSTATUS) && !priv->link) {
 		priv->link = 1;
 		printf("TLAN: %s has reestablished link\n",
 		       priv->nic_name);
@@ -1691,19 +1694,19 @@ void TLan_PhyMonitor(struct net_device *dev)
 #endif				/* MONITOR */
 
 static struct pci_device_id tlan_nics[] = {
-	PCI_ROM(0x0e11, 0xae34, "netel10", "Compaq Netelligent 10 T PCI UTP"),
-	PCI_ROM(0x0e11, 0xae32, "netel100","Compaq Netelligent 10/100 TX PCI UTP"),
-	PCI_ROM(0x0e11, 0xae35, "netflex3i", "Compaq Integrated NetFlex-3/P"),
-	PCI_ROM(0x0e11, 0xf130, "thunder", "Compaq NetFlex-3/P"),
-	PCI_ROM(0x0e11, 0xf150, "netflex3b", "Compaq NetFlex-3/P"),
-	PCI_ROM(0x0e11, 0xae43, "netel100pi", "Compaq Netelligent Integrated 10/100 TX UTP"),
-	PCI_ROM(0x0e11, 0xae40, "netel100d", "Compaq Netelligent Dual 10/100 TX PCI UTP"),
-	PCI_ROM(0x0e11, 0xb011, "netel100i", "Compaq Netelligent 10/100 TX Embedded UTP"),
-	PCI_ROM(0x108d, 0x0013, "oc2183", "Olicom OC-2183/2185"),
-	PCI_ROM(0x108d, 0x0012, "oc2325", "Olicom OC-2325"),
-	PCI_ROM(0x108d, 0x0014, "oc2326", "Olicom OC-2326"),
-	PCI_ROM(0x0e11, 0xb030, "netelligent_10_100_ws_5100", "Compaq Netelligent 10/100 TX UTP"),
-	PCI_ROM(0x0e11, 0xb012, "netelligent_10_t2", "Compaq Netelligent 10 T/2 PCI UTP/Coax"),
+	PCI_ROM(0x0e11, 0xae34, "netel10", "Compaq Netelligent 10 T PCI UTP", 0),
+	PCI_ROM(0x0e11, 0xae32, "netel100","Compaq Netelligent 10/100 TX PCI UTP", 0),
+	PCI_ROM(0x0e11, 0xae35, "netflex3i", "Compaq Integrated NetFlex-3/P", 0),
+	PCI_ROM(0x0e11, 0xf130, "thunder", "Compaq NetFlex-3/P", 0),
+	PCI_ROM(0x0e11, 0xf150, "netflex3b", "Compaq NetFlex-3/P", 0),
+	PCI_ROM(0x0e11, 0xae43, "netel100pi", "Compaq Netelligent Integrated 10/100 TX UTP", 0),
+	PCI_ROM(0x0e11, 0xae40, "netel100d", "Compaq Netelligent Dual 10/100 TX PCI UTP", 0),
+	PCI_ROM(0x0e11, 0xb011, "netel100i", "Compaq Netelligent 10/100 TX Embedded UTP", 0),
+	PCI_ROM(0x108d, 0x0013, "oc2183", "Olicom OC-2183/2185", 0),
+	PCI_ROM(0x108d, 0x0012, "oc2325", "Olicom OC-2325", 0),
+	PCI_ROM(0x108d, 0x0014, "oc2326", "Olicom OC-2326", 0),
+	PCI_ROM(0x0e11, 0xb030, "netelligent_10_100_ws_5100", "Compaq Netelligent 10/100 TX UTP", 0),
+	PCI_ROM(0x0e11, 0xb012, "netelligent_10_t2", "Compaq Netelligent 10 T/2 PCI UTP/Coax", 0),
 };
 
 PCI_DRIVER ( tlan_driver, tlan_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/tlan.h b/gpxe/src/drivers/net/tlan.h
index de57d59..31b3c8f 100644
--- a/gpxe/src/drivers/net/tlan.h
+++ b/gpxe/src/drivers/net/tlan.h
@@ -34,6 +34,8 @@
 * Indent Style: indent -kr -i8
 ***************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /*****************************************************************
 * TLan Definitions
 *
@@ -311,41 +313,6 @@ typedef struct tlan_adapter_entry {
 
 /* ThunderLAN MII Registers */
 
-/* Generic MII/PHY Registers */
-
-#define MII_GEN_CTL			0x00
-#define 	MII_GC_RESET		0x8000
-#define		MII_GC_LOOPBK		0x4000
-#define		MII_GC_SPEEDSEL		0x2000
-#define		MII_GC_AUTOENB		0x1000
-#define		MII_GC_PDOWN		0x0800
-#define		MII_GC_ISOLATE		0x0400
-#define		MII_GC_AUTORSRT		0x0200
-#define		MII_GC_DUPLEX		0x0100
-#define		MII_GC_COLTEST		0x0080
-#define		MII_GC_RESERVED		0x007F
-#define MII_GEN_STS			0x01
-#define		MII_GS_100BT4		0x8000
-#define		MII_GS_100BTXFD		0x4000
-#define		MII_GS_100BTXHD		0x2000
-#define		MII_GS_10BTFD		0x1000
-#define		MII_GS_10BTHD		0x0800
-#define		MII_GS_RESERVED		0x07C0
-#define		MII_GS_AUTOCMPLT	0x0020
-#define		MII_GS_RFLT		0x0010
-#define		MII_GS_AUTONEG		0x0008
-#define		MII_GS_LINK		0x0004
-#define		MII_GS_JABBER		0x0002
-#define		MII_GS_EXTCAP		0x0001
-#define MII_GEN_ID_HI			0x02
-#define MII_GEN_ID_LO			0x03
-#define 	MII_GIL_OUI		0xFC00
-#define 	MII_GIL_MODEL		0x03F0
-#define 	MII_GIL_REVISION	0x000F
-#define MII_AN_ADV			0x04
-#define MII_AN_LPA			0x05
-#define MII_AN_EXP			0x06
-
 /* ThunderLAN Specific MII/PHY Registers */
 
 #define TLAN_TLPHY_ID			0x10
diff --git a/gpxe/src/drivers/net/tulip.c b/gpxe/src/drivers/net/tulip.c
index afd7d45..e08e0d8 100644
--- a/gpxe/src/drivers/net/tulip.c
+++ b/gpxe/src/drivers/net/tulip.c
@@ -43,6 +43,8 @@
   ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html
 */
 
+FILE_LICENCE ( GPL_ANY );
+
 /*********************************************************************/
 /* Revision History                                                  */
 /*********************************************************************/
@@ -2045,42 +2047,42 @@ static int tulip_check_duplex(struct nic *nic)
 }
 
 static struct pci_device_id tulip_nics[] = {
-PCI_ROM(0x1011, 0x0002, "dc21040",     "Digital Tulip"),
-PCI_ROM(0x1011, 0x0009, "ds21140",     "Digital Tulip Fast"),
-PCI_ROM(0x1011, 0x0014, "dc21041",     "Digital Tulip+"),
-PCI_ROM(0x1011, 0x0019, "ds21142",     "Digital Tulip 21142"),
-PCI_ROM(0x10b7, 0x9300, "3csoho100b-tx","3ComSOHO100B-TX"),
-PCI_ROM(0x10b9, 0x5261, "ali1563",     "ALi 1563 integrated ethernet"),
-PCI_ROM(0x10d9, 0x0512, "mx98713",     "Macronix MX987x3"),
-PCI_ROM(0x10d9, 0x0531, "mx98715",     "Macronix MX987x5"),
-PCI_ROM(0x1113, 0x1217, "mxic-98715",  "Macronix MX987x5"),
-PCI_ROM(0x11ad, 0xc115, "lc82c115",    "LinkSys LNE100TX"),
-PCI_ROM(0x11ad, 0x0002, "82c168",      "Netgear FA310TX"),
-PCI_ROM(0x1282, 0x9100, "dm9100",      "Davicom 9100"),
-PCI_ROM(0x1282, 0x9102, "dm9102",      "Davicom 9102"),
-PCI_ROM(0x1282, 0x9009, "dm9009",      "Davicom 9009"),
-PCI_ROM(0x1282, 0x9132, "dm9132",      "Davicom 9132"),
-PCI_ROM(0x1317, 0x0985, "centaur-p",   "ADMtek Centaur-P"),
-PCI_ROM(0x1317, 0x0981, "an981",       "ADMtek AN981 Comet"),		/* ADMTek Centaur-P (stmicro) */
-PCI_ROM(0x1113, 0x1216, "an983",       "ADMTek AN983 Comet"),
-PCI_ROM(0x1317, 0x9511, "an983b",      "ADMTek Comet 983b"),
-PCI_ROM(0x1317, 0x1985, "centaur-c",   "ADMTek Centaur-C"),
-PCI_ROM(0x8086, 0x0039, "intel21145",  "Intel Tulip"),
-PCI_ROM(0x125b, 0x1400, "ax88140",     "ASIX AX88140"),
-PCI_ROM(0x11f6, 0x9881, "rl100tx",     "Compex RL100-TX"),
-PCI_ROM(0x115d, 0x0003, "xircomtulip", "Xircom Tulip"),
-PCI_ROM(0x104a, 0x0981, "tulip-0981",  "Tulip 0x104a 0x0981"),
-PCI_ROM(0x104a, 0x2774, "SGThomson-STE10100A", "Tulip 0x104a 0x2774"),	/*Modified by Ramesh Chander*/
-PCI_ROM(0x1113, 0x9511, "tulip-9511",  "Tulip 0x1113 0x9511"),
-PCI_ROM(0x1186, 0x1561, "tulip-1561",  "Tulip 0x1186 0x1561"),
-PCI_ROM(0x1259, 0xa120, "tulip-a120",  "Tulip 0x1259 0xa120"),
-PCI_ROM(0x13d1, 0xab02, "tulip-ab02",  "Tulip 0x13d1 0xab02"),
-PCI_ROM(0x13d1, 0xab03, "tulip-ab03",  "Tulip 0x13d1 0xab03"),
-PCI_ROM(0x13d1, 0xab08, "tulip-ab08",  "Tulip 0x13d1 0xab08"),
-PCI_ROM(0x14f1, 0x1803, "lanfinity",   "Conexant LANfinity"),
-PCI_ROM(0x1626, 0x8410, "tulip-8410",  "Tulip 0x1626 0x8410"),
-PCI_ROM(0x1737, 0xab08, "tulip-1737-ab08","Tulip 0x1737 0xab08"),
-PCI_ROM(0x1737, 0xab09, "tulip-ab09",  "Tulip 0x1737 0xab09"),
+PCI_ROM(0x1011, 0x0002, "dc21040",     "Digital Tulip", 0),
+PCI_ROM(0x1011, 0x0009, "ds21140",     "Digital Tulip Fast", 0),
+PCI_ROM(0x1011, 0x0014, "dc21041",     "Digital Tulip+", 0),
+PCI_ROM(0x1011, 0x0019, "ds21142",     "Digital Tulip 21142", 0),
+PCI_ROM(0x10b7, 0x9300, "3csoho100b-tx","3ComSOHO100B-TX", 0),
+PCI_ROM(0x10b9, 0x5261, "ali1563",     "ALi 1563 integrated ethernet", 0),
+PCI_ROM(0x10d9, 0x0512, "mx98713",     "Macronix MX987x3", 0),
+PCI_ROM(0x10d9, 0x0531, "mx98715",     "Macronix MX987x5", 0),
+PCI_ROM(0x1113, 0x1217, "mxic-98715",  "Macronix MX987x5", 0),
+PCI_ROM(0x11ad, 0xc115, "lc82c115",    "LinkSys LNE100TX", 0),
+PCI_ROM(0x11ad, 0x0002, "82c168",      "Netgear FA310TX", 0),
+PCI_ROM(0x1282, 0x9100, "dm9100",      "Davicom 9100", 0),
+PCI_ROM(0x1282, 0x9102, "dm9102",      "Davicom 9102", 0),
+PCI_ROM(0x1282, 0x9009, "dm9009",      "Davicom 9009", 0),
+PCI_ROM(0x1282, 0x9132, "dm9132",      "Davicom 9132", 0),
+PCI_ROM(0x1317, 0x0985, "centaur-p",   "ADMtek Centaur-P", 0),
+PCI_ROM(0x1317, 0x0981, "an981",       "ADMtek AN981 Comet", 0),		/* ADMTek Centaur-P (stmicro) */
+PCI_ROM(0x1113, 0x1216, "an983",       "ADMTek AN983 Comet", 0),
+PCI_ROM(0x1317, 0x9511, "an983b",      "ADMTek Comet 983b", 0),
+PCI_ROM(0x1317, 0x1985, "centaur-c",   "ADMTek Centaur-C", 0),
+PCI_ROM(0x8086, 0x0039, "intel21145",  "Intel Tulip", 0),
+PCI_ROM(0x125b, 0x1400, "ax88140",     "ASIX AX88140", 0),
+PCI_ROM(0x11f6, 0x9881, "rl100tx",     "Compex RL100-TX", 0),
+PCI_ROM(0x115d, 0x0003, "xircomtulip", "Xircom Tulip", 0),
+PCI_ROM(0x104a, 0x0981, "tulip-0981",  "Tulip 0x104a 0x0981", 0),
+PCI_ROM(0x104a, 0x2774, "SGThomson-STE10100A", "Tulip 0x104a 0x2774", 0),	/*Modified by Ramesh Chander*/
+PCI_ROM(0x1113, 0x9511, "tulip-9511",  "Tulip 0x1113 0x9511", 0),
+PCI_ROM(0x1186, 0x1561, "tulip-1561",  "Tulip 0x1186 0x1561", 0),
+PCI_ROM(0x1259, 0xa120, "tulip-a120",  "Tulip 0x1259 0xa120", 0),
+PCI_ROM(0x13d1, 0xab02, "tulip-ab02",  "Tulip 0x13d1 0xab02", 0),
+PCI_ROM(0x13d1, 0xab03, "tulip-ab03",  "Tulip 0x13d1 0xab03", 0),
+PCI_ROM(0x13d1, 0xab08, "tulip-ab08",  "Tulip 0x13d1 0xab08", 0),
+PCI_ROM(0x14f1, 0x1803, "lanfinity",   "Conexant LANfinity", 0),
+PCI_ROM(0x1626, 0x8410, "tulip-8410",  "Tulip 0x1626 0x8410", 0),
+PCI_ROM(0x1737, 0xab08, "tulip-1737-ab08","Tulip 0x1737 0xab08", 0),
+PCI_ROM(0x1737, 0xab09, "tulip-ab09",  "Tulip 0x1737 0xab09", 0),
 };
 
 PCI_DRIVER ( tulip_driver, tulip_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/via-rhine.c b/gpxe/src/drivers/net/via-rhine.c
index d9880c3..60e6230 100644
--- a/gpxe/src/drivers/net/via-rhine.c
+++ b/gpxe/src/drivers/net/via-rhine.c
@@ -1424,11 +1424,11 @@ static struct nic_operations rhine_operations = {
 };
 
 static struct pci_device_id rhine_nics[] = {
-PCI_ROM(0x1106, 0x3065, "dlink-530tx",     "VIA 6102"),
-PCI_ROM(0x1106, 0x3106, "via-rhine-6105",  "VIA 6105"),
-PCI_ROM(0x1106, 0x3043, "dlink-530tx-old", "VIA 3043"),		/* Rhine-I 86c100a */
-PCI_ROM(0x1106, 0x3053, "via6105m",        "VIA 6105M"),	
-PCI_ROM(0x1106, 0x6100, "via-rhine-old",   "VIA 86C100A"),	/* Rhine-II */
+PCI_ROM(0x1106, 0x3065, "dlink-530tx",     "VIA 6102", 0),
+PCI_ROM(0x1106, 0x3106, "via-rhine-6105",  "VIA 6105", 0),
+PCI_ROM(0x1106, 0x3043, "dlink-530tx-old", "VIA 3043", 0),		/* Rhine-I 86c100a */
+PCI_ROM(0x1106, 0x3053, "via6105m",        "VIA 6105M", 0),
+PCI_ROM(0x1106, 0x6100, "via-rhine-old",   "VIA 86C100A", 0),	/* Rhine-II */
 };
 
 PCI_DRIVER ( rhine_driver, rhine_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/via-velocity.c b/gpxe/src/drivers/net/via-velocity.c
index 4473ed1..19625aa 100644
--- a/gpxe/src/drivers/net/via-velocity.c
+++ b/gpxe/src/drivers/net/via-velocity.c
@@ -1930,7 +1930,7 @@ int pci_set_power_state(struct pci_device *dev, int state)
 }
 
 static struct pci_device_id velocity_nics[] = {
-	PCI_ROM(0x1106, 0x3119, "via-velocity", "VIA Networking Velocity Family Gigabit Ethernet Adapter"),
+	PCI_ROM(0x1106, 0x3119, "via-velocity", "VIA Networking Velocity Family Gigabit Ethernet Adapter", 0),
 };
 
 PCI_DRIVER ( velocity_driver, velocity_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/virtio-net.c b/gpxe/src/drivers/net/virtio-net.c
index f0afd3f..49fcc1c 100644
--- a/gpxe/src/drivers/net/virtio-net.c
+++ b/gpxe/src/drivers/net/virtio-net.c
@@ -298,7 +298,7 @@ static int virtnet_probe(struct nic *nic, struct pci_device *pci)
 }
 
 static struct pci_device_id virtnet_nics[] = {
-PCI_ROM(0x1af4, 0x1000, "virtio-net",              "Virtio Network Interface"),
+PCI_ROM(0x1af4, 0x1000, "virtio-net",              "Virtio Network Interface", 0),
 };
 
 PCI_DRIVER ( virtnet_driver, virtnet_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/w89c840.c b/gpxe/src/drivers/net/w89c840.c
index 5abc0b3..fa6188a 100644
--- a/gpxe/src/drivers/net/w89c840.c
+++ b/gpxe/src/drivers/net/w89c840.c
@@ -29,6 +29,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /*
  *              date       version  by   what
  *  Written:    Aug 20 2000  V0.10  iko  Initial revision.
@@ -610,8 +612,8 @@ static struct nic_operations w89c840_operations = {
 };
 
 static struct pci_device_id w89c840_nics[] = {
-PCI_ROM(0x1050, 0x0840, "winbond840",     "Winbond W89C840F"),
-PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX"),
+PCI_ROM(0x1050, 0x0840, "winbond840",     "Winbond W89C840F", 0),
+PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX", 0),
 };
 
 PCI_DRIVER ( w89c840_driver, w89c840_nics, PCI_NO_CLASS );
diff --git a/gpxe/src/drivers/net/wlan_compat.h b/gpxe/src/drivers/net/wlan_compat.h
index a4f75e3..9b7693b 100644
--- a/gpxe/src/drivers/net/wlan_compat.h
+++ b/gpxe/src/drivers/net/wlan_compat.h
@@ -45,6 +45,8 @@
 * --------------------------------------------------------------------
 */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 #ifndef _WLAN_COMPAT_H
 #define _WLAN_COMPAT_H
 
diff --git a/gpxe/src/drivers/nvs/nvs.c b/gpxe/src/drivers/nvs/nvs.c
index 8e94b87..7252808 100644
--- a/gpxe/src/drivers/nvs/nvs.c
+++ b/gpxe/src/drivers/nvs/nvs.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
diff --git a/gpxe/src/drivers/nvs/spi.c b/gpxe/src/drivers/nvs/spi.c
index dbdc32f..793080a 100644
--- a/gpxe/src/drivers/nvs/spi.c
+++ b/gpxe/src/drivers/nvs/spi.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <errno.h>
 #include <unistd.h>
diff --git a/gpxe/src/drivers/nvs/threewire.c b/gpxe/src/drivers/nvs/threewire.c
index dbecf90..8e52138 100644
--- a/gpxe/src/drivers/nvs/threewire.c
+++ b/gpxe/src/drivers/nvs/threewire.c
@@ -16,7 +16,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
+#include <string.h>
 #include <assert.h>
 #include <unistd.h>
 #include <gpxe/threewire.h>
@@ -40,13 +43,21 @@ int threewire_read ( struct nvs_device *nvs, unsigned int address,
 		     void *data, size_t len ) {
 	struct spi_device *device = nvs_to_spi ( nvs );
 	struct spi_bus *bus = device->bus;
+	int rc;
 
 	assert ( bus->mode == SPI_MODE_THREEWIRE );
 
-	DBG ( "3wire %p reading %zd bytes at %04x\n", device, len, address );
+	DBGC ( device, "3wire %p reading %zd bytes at %04x\n",
+	       device, len, address );
 
-	return bus->rw ( bus, device, THREEWIRE_READ, address,
-			 NULL, data, len );
+	if ( ( rc = bus->rw ( bus, device, THREEWIRE_READ, address,
+			      NULL, data, len ) ) != 0 ) {
+		DBGC ( device, "3wire %p could not read: %s\n",
+		       device, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
 }
 
 /**
@@ -66,17 +77,24 @@ int threewire_write ( struct nvs_device *nvs, unsigned int address,
 
 	assert ( bus->mode == SPI_MODE_THREEWIRE );
 
-	DBG ( "3wire %p writing %zd bytes at %04x\n", device, len, address );
+	DBGC ( device, "3wire %p writing %zd bytes at %04x\n",
+	       device, len, address );
 
 	/* Enable device for writing */
 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_EWEN,
-			      THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 )
+			      THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 ){
+		DBGC ( device, "3wire %p could not enable writing: %s\n",
+		       device, strerror ( rc ) );
 		return rc;
+	}
 
 	/* Write data */
 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_WRITE, address,
-			      data, NULL, len ) ) != 0 )
+			      data, NULL, len ) ) != 0 ) {
+		DBGC ( device, "3wire %p could not write: %s\n",
+		       device, strerror ( rc ) );
 		return rc;
+	}
 
 	/* Our model of an SPI bus doesn't provide a mechanism for
 	 * "assert CS, wait for MISO to become high, so just wait for
@@ -86,3 +104,28 @@ int threewire_write ( struct nvs_device *nvs, unsigned int address,
 
 	return 0;
 }
+
+/**
+ * Autodetect device address length
+ *
+ * @v device		SPI device
+ * @ret rc		Return status code
+ */
+int threewire_detect_address_len ( struct spi_device *device ) {
+	struct nvs_device *nvs = &device->nvs;
+	int rc;
+
+	DBGC ( device, "3wire %p autodetecting address length\n", device );
+
+	device->address_len = SPI_AUTODETECT_ADDRESS_LEN;
+	if ( ( rc = threewire_read ( nvs, 0, NULL,
+				     ( 1 << nvs->word_len_log2 ) ) ) != 0 ) {
+		DBGC ( device, "3wire %p could not autodetect address "
+		       "length: %s\n", device, strerror ( rc ) );
+		return rc;
+	}
+
+	DBGC ( device, "3wire %p autodetected address length %d\n",
+	       device, device->address_len );
+	return 0;
+}
diff --git a/gpxe/src/hci/commands/autoboot_cmd.c b/gpxe/src/hci/commands/autoboot_cmd.c
index 0e6f294..95b172d 100644
--- a/gpxe/src/hci/commands/autoboot_cmd.c
+++ b/gpxe/src/hci/commands/autoboot_cmd.c
@@ -2,6 +2,8 @@
 #include <gpxe/command.h>
 #include <usr/autoboot.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static int autoboot_exec ( int argc, char **argv ) {
 
 	if ( argc != 1 ) {
diff --git a/gpxe/src/hci/commands/config_cmd.c b/gpxe/src/hci/commands/config_cmd.c
index 87abb05..a9e1f16 100644
--- a/gpxe/src/hci/commands/config_cmd.c
+++ b/gpxe/src/hci/commands/config_cmd.c
@@ -4,6 +4,8 @@
 #include <gpxe/settings.h>
 #include <gpxe/settings_ui.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static int config_exec ( int argc, char **argv ) {
 	char *settings_name;
 	struct settings *settings;
diff --git a/gpxe/src/hci/commands/dhcp_cmd.c b/gpxe/src/hci/commands/dhcp_cmd.c
index 9b577c8..96aac8d 100644
--- a/gpxe/src/hci/commands/dhcp_cmd.c
+++ b/gpxe/src/hci/commands/dhcp_cmd.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
diff --git a/gpxe/src/hci/commands/digest_cmd.c b/gpxe/src/hci/commands/digest_cmd.c
new file mode 100644
index 0000000..bc6e551
--- /dev/null
+++ b/gpxe/src/hci/commands/digest_cmd.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 Daniel Verkamp <daniel at drv.nu>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <gpxe/command.h>
+#include <gpxe/image.h>
+#include <gpxe/crypto.h>
+
+#include <gpxe/md5.h>
+#include <gpxe/sha1.h>
+
+/**
+ * "digest" command syntax message
+ *
+ * @v argv		Argument list
+ */
+static void digest_syntax ( char **argv ) {
+	printf ( "Usage:\n"
+		 "  %s <image name>\n"
+		 "\n"
+		 "Calculate the %s of an image\n",
+		 argv[0], argv[0] );
+}
+
+/**
+ * The "digest" command
+ *
+ * @v argc		Argument count
+ * @v argv		Argument list
+ * @v digest		Digest algorithm
+ * @ret rc		Exit code
+ */
+static int digest_exec ( int argc, char **argv,
+			 struct digest_algorithm *digest ) {
+	const char *image_name;
+	struct image *image;
+	uint8_t digest_ctx[digest->ctxsize];
+	uint8_t digest_out[digest->digestsize];
+	uint8_t buf[128];
+	size_t offset;
+	size_t len;
+	size_t frag_len;
+	int i;
+	unsigned j;
+
+	if ( argc < 2 ||
+	     !strcmp ( argv[1], "--help" ) ||
+	     !strcmp ( argv[1], "-h" ) ) {
+		digest_syntax ( argv );
+		return 1;
+	}
+
+	for ( i = 1 ; i < argc ; i++ ) {
+		image_name = argv[i];
+
+		/* find image */
+		image = find_image ( image_name );
+		if ( ! image ) {
+			printf ( "No such image: %s\n", image_name );
+			continue;
+		}
+		offset = 0;
+		len = image->len;
+
+		/* calculate digest */
+		digest_init ( digest, digest_ctx );
+		while ( len ) {
+			frag_len = len;
+			if ( frag_len > sizeof ( buf ) )
+				frag_len = sizeof ( buf );
+			copy_from_user ( buf, image->data, offset, frag_len );
+			digest_update ( digest, digest_ctx, buf, frag_len );
+			len -= frag_len;
+			offset += frag_len;
+		}
+		digest_final ( digest, digest_ctx, digest_out );
+
+		for ( j = 0 ; j < sizeof ( digest_out ) ; j++ )
+			printf ( "%02x", digest_out[j] );
+
+		printf ( "  %s\n", image->name );
+	}
+
+	return 0;
+}
+
+static int md5sum_exec ( int argc, char **argv ) {
+	return digest_exec ( argc, argv, &md5_algorithm );
+}
+
+static int sha1sum_exec ( int argc, char **argv ) {
+	return digest_exec ( argc, argv, &sha1_algorithm );
+}
+
+struct command md5sum_command __command = {
+	.name = "md5sum",
+	.exec = md5sum_exec,
+};
+
+struct command sha1sum_command __command = {
+	.name = "sha1sum",
+	.exec = sha1sum_exec,
+};
diff --git a/gpxe/src/hci/commands/ifmgmt_cmd.c b/gpxe/src/hci/commands/ifmgmt_cmd.c
index f2508e5..ad069f1 100644
--- a/gpxe/src/hci/commands/ifmgmt_cmd.c
+++ b/gpxe/src/hci/commands/ifmgmt_cmd.c
@@ -16,11 +16,14 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <getopt.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/command.h>
 #include <usr/ifmgmt.h>
+#include <hci/ifmgmt_cmd.h>
 
 /** @file
  *
@@ -95,15 +98,15 @@ static int ifcommon_do_list ( int ( * payload ) ( struct net_device * ),
 /**
  * Execute if<xxx> command
  *
- * @v payload		Command to execute
- * @v verb		Verb describing the action of the command
  * @v argc		Argument count
  * @v argv		Argument list
+ * @v payload		Command to execute
+ * @v verb		Verb describing the action of the command
  * @ret rc		Exit code
  */
-static __attribute__ (( regparm ( 2 ) )) int
-ifcommon_exec ( int ( * payload ) ( struct net_device * ),
-		const char *verb, int argc, char **argv ) {
+int ifcommon_exec ( int argc, char **argv,
+		    int ( * payload ) ( struct net_device * ),
+		    const char *verb ) {
 	int c;
 
 	/* Parse options */
@@ -134,7 +137,7 @@ static int ifopen_payload ( struct net_device *netdev ) {
 }
 
 static int ifopen_exec ( int argc, char **argv ) {
-	return ifcommon_exec ( ifopen_payload, "Open", argc, argv );
+	return ifcommon_exec ( argc, argv, ifopen_payload, "Open" );
 }
 
 /* "ifclose" command */
@@ -145,7 +148,7 @@ static int ifclose_payload ( struct net_device *netdev ) {
 }
 
 static int ifclose_exec ( int argc, char **argv ) {
-	return ifcommon_exec ( ifclose_payload, "Close", argc, argv );
+	return ifcommon_exec ( argc, argv, ifclose_payload, "Close" );
 }
 
 /* "ifstat" command */
@@ -156,8 +159,8 @@ static int ifstat_payload ( struct net_device *netdev ) {
 }
 
 static int ifstat_exec ( int argc, char **argv ) {
-	return ifcommon_exec ( ifstat_payload, "Display status of",
-			       argc, argv );
+	return ifcommon_exec ( argc, argv,
+			       ifstat_payload, "Display status of" );
 }
 
 /** Interface management commands */
diff --git a/gpxe/src/hci/commands/image_cmd.c b/gpxe/src/hci/commands/image_cmd.c
index 6d8b590..33994b5 100644
--- a/gpxe/src/hci/commands/image_cmd.c
+++ b/gpxe/src/hci/commands/image_cmd.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -497,9 +499,9 @@ static int imgstat_exec ( int argc, char **argv ) {
  */
 static void imgfree_syntax ( char **argv ) {
 	printf ( "Usage:\n"
-		 "  %s\n"
+		 "  %s [<image name>]\n"
 		 "\n"
-		 "Free all executable/loadable images\n",
+		 "Free one or all executable/loadable images\n",
 		 argv[0] );
 }
 
@@ -517,6 +519,7 @@ static int imgfree_exec ( int argc, char **argv ) {
 	};
 	struct image *image;
 	struct image *tmp;
+	const char *name = NULL;
 	int c;
 
 	/* Parse options */
@@ -531,15 +534,27 @@ static int imgfree_exec ( int argc, char **argv ) {
 		}
 	}
 
-	/* No arguments */
+	/* Need no more than one image name */
+	if ( optind != argc )
+		name = argv[optind++];
 	if ( optind != argc ) {
 		imgfree_syntax ( argv );
 		return 1;
 	}
 
-	/* Free all images */
-	list_for_each_entry_safe ( image, tmp, &images, list ) {
+	if ( name ) {
+		/* Free specified image (may leak) */
+		image = find_image ( name );
+		if ( ! image ) {
+			printf ( "No such image: %s\n", name );
+			return 1;
+		}
 		imgfree ( image );
+	} else {
+		/* Free all images */
+		list_for_each_entry_safe ( image, tmp, &images, list ) {
+			imgfree ( image );
+		}
 	}
 	return 0;
 }
diff --git a/gpxe/src/hci/commands/iwmgmt_cmd.c b/gpxe/src/hci/commands/iwmgmt_cmd.c
new file mode 100644
index 0000000..006c9f1
--- /dev/null
+++ b/gpxe/src/hci/commands/iwmgmt_cmd.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/netdevice.h>
+#include <gpxe/net80211.h>
+#include <gpxe/command.h>
+#include <usr/iwmgmt.h>
+#include <hci/ifmgmt_cmd.h>
+
+/* "iwstat" command */
+
+static int iwstat_payload ( struct net_device *netdev ) {
+	struct net80211_device *dev = net80211_get ( netdev );
+
+	if ( dev )
+		iwstat ( dev );
+
+	return 0;
+}
+
+static int iwstat_exec ( int argc, char **argv ) {
+	return ifcommon_exec ( argc, argv,
+			       iwstat_payload, "Display wireless status of" );
+}
+
+/* "iwlist" command */
+
+static int iwlist_payload ( struct net_device *netdev ) {
+	struct net80211_device *dev = net80211_get ( netdev );
+
+	if ( dev )
+		return iwlist ( dev );
+
+	return 0;
+}
+
+static int iwlist_exec ( int argc, char **argv ) {
+	return ifcommon_exec ( argc, argv, iwlist_payload,
+			       "List wireless networks available via" );
+}
+
+/** Wireless interface management commands */
+struct command iwmgmt_commands[] __command = {
+	{
+		.name = "iwstat",
+		.exec = iwstat_exec,
+	},
+	{
+		.name = "iwlist",
+		.exec = iwlist_exec,
+	},
+};
diff --git a/gpxe/src/hci/commands/login_cmd.c b/gpxe/src/hci/commands/login_cmd.c
index e425247..0da2497 100644
--- a/gpxe/src/hci/commands/login_cmd.c
+++ b/gpxe/src/hci/commands/login_cmd.c
@@ -3,6 +3,8 @@
 #include <gpxe/command.h>
 #include <gpxe/login_ui.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static int login_exec ( int argc, char **argv ) {
 	int rc;
 
diff --git a/gpxe/src/hci/commands/nvo_cmd.c b/gpxe/src/hci/commands/nvo_cmd.c
index c0c0728..5eb2f06 100644
--- a/gpxe/src/hci/commands/nvo_cmd.c
+++ b/gpxe/src/hci/commands/nvo_cmd.c
@@ -7,6 +7,8 @@
 #include <gpxe/settings.h>
 #include <gpxe/command.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static int show_exec ( int argc, char **argv ) {
 	char buf[256];
 	int rc;
diff --git a/gpxe/src/hci/commands/route_cmd.c b/gpxe/src/hci/commands/route_cmd.c
index 227682c..4372e34 100644
--- a/gpxe/src/hci/commands/route_cmd.c
+++ b/gpxe/src/hci/commands/route_cmd.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <getopt.h>
 #include <gpxe/command.h>
diff --git a/gpxe/src/hci/commands/sanboot_cmd.c b/gpxe/src/hci/commands/sanboot_cmd.c
index d5bbfb8..783b747 100644
--- a/gpxe/src/hci/commands/sanboot_cmd.c
+++ b/gpxe/src/hci/commands/sanboot_cmd.c
@@ -4,6 +4,8 @@
 #include <gpxe/command.h>
 #include <usr/autoboot.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * "sanboot" command syntax message
  *
diff --git a/gpxe/src/hci/commands/time_cmd.c b/gpxe/src/hci/commands/time_cmd.c
new file mode 100644
index 0000000..947410e
--- /dev/null
+++ b/gpxe/src/hci/commands/time_cmd.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009 Daniel Verkamp <daniel at drv.nu>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * March-19-2009 @ 02:44: Added sleep command.
+ * Shao Miller <shao.miller at yrdsb.edu.on.ca>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <gpxe/command.h>
+#include <gpxe/nap.h>
+#include <gpxe/timer.h>
+
+static int time_exec ( int argc, char **argv ) {
+	unsigned long start;
+	int rc, secs;
+
+	if ( argc == 1 ||
+	     !strcmp ( argv[1], "--help" ) ||
+	     !strcmp ( argv[1], "-h" ) )
+	{
+		printf ( "Usage:\n"
+			 "  %s <command>\n"
+			 "\n"
+			 "Time a command\n",
+			 argv[0] );
+		return 1;
+	}
+
+	start = currticks();
+	rc = execv ( argv[1], argv + 1 );
+	secs = (currticks() - start) / ticks_per_sec();
+
+	printf ( "%s: %ds\n", argv[0], secs );
+
+	return rc;
+}
+
+struct command time_command __command = {
+	.name = "time",
+	.exec = time_exec,
+};
+
+static int sleep_exec ( int argc, char **argv ) {
+	unsigned long start, delay;
+
+	if ( argc == 1 ||
+	     !strcmp ( argv[1], "--help" ) ||
+	     !strcmp ( argv[1], "-h" ))
+	{
+		printf ( "Usage:\n"
+			 "  %s <seconds>\n"
+			 "\n"
+			 "Sleep for <seconds> seconds\n",
+			 argv[0] );
+		return 1;
+	}
+	start = currticks();
+	delay = strtoul ( argv[1], NULL, 0 ) * ticks_per_sec();
+	while ( ( currticks() - start ) <= delay )
+		cpu_nap();
+	return 0;
+}
+
+struct command sleep_command __command = {
+	.name = "sleep",
+	.exec = sleep_exec,
+};
diff --git a/gpxe/src/hci/editstring.c b/gpxe/src/hci/editstring.c
index 347249f..648f338 100644
--- a/gpxe/src/hci/editstring.c
+++ b/gpxe/src/hci/editstring.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <assert.h>
 #include <string.h>
 #include <gpxe/keys.h>
diff --git a/gpxe/src/hci/mucurses/ansi_screen.c b/gpxe/src/hci/mucurses/ansi_screen.c
index 468bac0..51fc7c9 100644
--- a/gpxe/src/hci/mucurses/ansi_screen.c
+++ b/gpxe/src/hci/mucurses/ansi_screen.c
@@ -2,6 +2,8 @@
 #include <curses.h>
 #include <console.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static void ansiscr_reset(struct _curses_screen *scr) __nonnull;
 static void ansiscr_movetoyx(struct _curses_screen *scr,
                                unsigned int y, unsigned int x) __nonnull;
diff --git a/gpxe/src/hci/mucurses/clear.c b/gpxe/src/hci/mucurses/clear.c
index 1813939..79b296c 100644
--- a/gpxe/src/hci/mucurses/clear.c
+++ b/gpxe/src/hci/mucurses/clear.c
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * Clear a window to the bottom from current cursor position
  *
diff --git a/gpxe/src/hci/mucurses/colour.c b/gpxe/src/hci/mucurses/colour.c
index 2310641..c1359c8 100644
--- a/gpxe/src/hci/mucurses/colour.c
+++ b/gpxe/src/hci/mucurses/colour.c
@@ -1,5 +1,7 @@
 #include <curses.h>
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct colour_pair {
 	short fcol;
 	short bcol;
diff --git a/gpxe/src/hci/mucurses/cursor.h b/gpxe/src/hci/mucurses/cursor.h
index af86519..16b7d27 100644
--- a/gpxe/src/hci/mucurses/cursor.h
+++ b/gpxe/src/hci/mucurses/cursor.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct cursor_pos {
 	unsigned int y, x;
 };
diff --git a/gpxe/src/hci/mucurses/mucurses.c b/gpxe/src/hci/mucurses/mucurses.c
index 3620d08..087ebcc 100644
--- a/gpxe/src/hci/mucurses/mucurses.c
+++ b/gpxe/src/hci/mucurses/mucurses.c
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 static void _wupdcurs ( WINDOW *win ) __nonnull;
 void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull;
 void _wputc ( WINDOW *win, char c, int wrap ) __nonnull;
diff --git a/gpxe/src/hci/mucurses/mucurses.h b/gpxe/src/hci/mucurses/mucurses.h
index 1476733..7ac1086 100644
--- a/gpxe/src/hci/mucurses/mucurses.h
+++ b/gpxe/src/hci/mucurses/mucurses.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define WRAP 0
 #define NOWRAP 1
 
diff --git a/gpxe/src/hci/mucurses/print.c b/gpxe/src/hci/mucurses/print.c
index 9fca308..1608c0a 100644
--- a/gpxe/src/hci/mucurses/print.c
+++ b/gpxe/src/hci/mucurses/print.c
@@ -10,6 +10,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * Add a single-byte character and rendition to a window and advance
  * the cursor
diff --git a/gpxe/src/hci/mucurses/widgets/editbox.c b/gpxe/src/hci/mucurses/widgets/editbox.c
index a52089c..ee7d609 100644
--- a/gpxe/src/hci/mucurses/widgets/editbox.c
+++ b/gpxe/src/hci/mucurses/widgets/editbox.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <assert.h>
 #include <gpxe/editbox.h>
diff --git a/gpxe/src/hci/mucurses/winattrs.c b/gpxe/src/hci/mucurses/winattrs.c
index 15f9732..f549d75 100644
--- a/gpxe/src/hci/mucurses/winattrs.c
+++ b/gpxe/src/hci/mucurses/winattrs.c
@@ -6,6 +6,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * Get the background rendition attributes for a window
  *
diff --git a/gpxe/src/hci/mucurses/wininit.c b/gpxe/src/hci/mucurses/wininit.c
index cd27f9f..782e7b5 100644
--- a/gpxe/src/hci/mucurses/wininit.c
+++ b/gpxe/src/hci/mucurses/wininit.c
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * Initialise console environment
  *
diff --git a/gpxe/src/hci/readline.c b/gpxe/src/hci/readline.c
index ff7a767..e5699d5 100644
--- a/gpxe/src/hci/readline.c
+++ b/gpxe/src/hci/readline.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
diff --git a/gpxe/src/hci/shell.c b/gpxe/src/hci/shell.c
index 18b1068..5bedbdc 100644
--- a/gpxe/src/hci/shell.c
+++ b/gpxe/src/hci/shell.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -29,11 +31,6 @@
  *
  */
 
-static struct command commands[0]
-	__table_start ( struct command, commands );
-static struct command commands_end[0]
-	__table_end ( struct command, commands );
-
 /** The shell prompt string */
 static const char shell_prompt[] = "gPXE> ";
 
@@ -65,7 +62,7 @@ static int help_exec ( int argc __unused, char **argv __unused ) {
 	unsigned int hpos = 0;
 
 	printf ( "\nAvailable commands:\n\n" );
-	for ( command = commands ; command < commands_end ; command++ ) {
+	for_each_table_entry ( command, COMMANDS ) {
 		hpos += printf ( "  %s", command->name );
 		if ( hpos > ( 16 * 4 ) ) {
 			printf ( "\n" );
diff --git a/gpxe/src/hci/shell_banner.c b/gpxe/src/hci/shell_banner.c
index 3271c48..8afefe3 100644
--- a/gpxe/src/hci/shell_banner.c
+++ b/gpxe/src/hci/shell_banner.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <console.h>
 #include <unistd.h>
diff --git a/gpxe/src/hci/strerror.c b/gpxe/src/hci/strerror.c
index 74995e8..94547dd 100644
--- a/gpxe/src/hci/strerror.c
+++ b/gpxe/src/hci/strerror.c
@@ -18,24 +18,19 @@
  *
  */
 
-static struct errortab errortab_start[0]
-	__table_start ( struct errortab, errortab );
-static struct errortab errortab_end[0]
-	__table_end ( struct errortab, errortab );
+FILE_LICENCE ( GPL2_OR_LATER );
 
 /**
  * Find error description
  *
  * @v errno		Error number
- * @v mask		Mask of bits that we care about
  * @ret errortab	Error description, or NULL
  */
-static struct errortab * find_error ( int errno, int mask ) {
+static struct errortab * find_error ( int errno ) {
 	struct errortab *errortab;
 
-	for ( errortab = errortab_start ; errortab < errortab_end ;
-	      errortab++ ) {
-		if ( ( ( errortab->errno ^ errno ) & mask ) == 0 )
+	for_each_table_entry ( errortab, ERRORTAB ) {
+		if ( errortab->errno == errno )
 			return errortab;
 	}
 
@@ -54,13 +49,13 @@ static struct errortab * find_closest_error ( int errno ) {
 	struct errortab *errortab;
 
 	/* First, look for an exact match */
-	if ( ( errortab = find_error ( errno, 0x7fffffff ) ) != NULL )
+	if ( ( errortab = find_error ( errno ) ) != NULL )
 		return errortab;
 
 	/* Second, try masking off the gPXE-specific bit and seeing if
 	 * we have an entry for the generic POSIX error message.
 	 */
-	if ( ( errortab = find_error ( errno, 0x4f0000ff ) ) != NULL )
+	if ( ( errortab = find_error ( errno & 0x7f0000ff ) ) != NULL )
 		return errortab;
 
 	return NULL;
@@ -119,7 +114,9 @@ struct errortab common_errors[] __errortab = {
 	{ ENOEXEC, "Not an executable image" },
 	{ ENOMEM, "Out of memory" },
 	{ ENOSPC, "No space left on device" },
+	{ ENOTCONN, "Not connected" },
 	{ ENOTSUP, "Not supported" },
 	{ EPERM, "Operation not permitted" },
+	{ ERANGE, "Out of range" },
 	{ ETIMEDOUT, "Connection timed out" },
 };
diff --git a/gpxe/src/hci/tui/login_ui.c b/gpxe/src/hci/tui/login_ui.c
index c14a2c0..b80bf27 100644
--- a/gpxe/src/hci/tui/login_ui.c
+++ b/gpxe/src/hci/tui/login_ui.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** @file
  *
  * Login UI
diff --git a/gpxe/src/hci/tui/settings_ui.c b/gpxe/src/hci/tui/settings_ui.c
index 4ab3827..74ce6af 100644
--- a/gpxe/src/hci/tui/settings_ui.c
+++ b/gpxe/src/hci/tui/settings_ui.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <stdarg.h>
 #include <unistd.h>
@@ -77,12 +79,8 @@ struct setting_widget {
 	char value[256]; /* enough size for a DHCP string */
 };
 
-/** Registered configuration settings */
-static struct setting all_settings[0]
-	__table_start ( struct setting, settings );
-static struct setting all_settings_end[0]
-	__table_end ( struct setting, settings );
-#define NUM_SETTINGS ( ( unsigned ) ( all_settings_end - all_settings ) )
+/** Number of registered configuration settings */
+#define NUM_SETTINGS table_num_entries ( SETTINGS )
 
 static void load_setting ( struct setting_widget *widget ) __nonnull;
 static int save_setting ( struct setting_widget *widget ) __nonnull;
@@ -223,6 +221,8 @@ static int edit_setting ( struct setting_widget *widget, int key ) {
 static void init_setting_index ( struct setting_widget *widget,
 				 struct settings *settings,
 				 unsigned int index ) {
+	struct setting *all_settings = table_start ( SETTINGS );
+
 	init_setting ( widget, settings, &all_settings[index],
 		       ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
 }
diff --git a/gpxe/src/hci/wireless_errors.c b/gpxe/src/hci/wireless_errors.c
new file mode 100644
index 0000000..46006f9
--- /dev/null
+++ b/gpxe/src/hci/wireless_errors.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <errno.h>
+#include <gpxe/errortab.h>
+
+/* Record errors as though they come from the 802.11 stack */
+#undef ERRFILE
+#define ERRFILE ERRFILE_net80211
+
+/** All 802.11 errors
+ *
+ * These follow the 802.11 standard as much as is feasible, but most
+ * have been abbreviated to fit the 50-character limit imposed by
+ * strerror.
+ */
+struct errortab wireless_errors[] __errortab = {
+	/* gPXE 802.11 stack errors */
+	{ EINVAL | EUNIQ_01, "Packet too short" },
+	{ EINVAL | EUNIQ_02, "Packet 802.11 version not supported" },
+	{ EINVAL | EUNIQ_03, "Packet not a data packet" },
+	{ EINVAL | EUNIQ_04, "Packet not from an Access Point" },
+	{ EINVAL | EUNIQ_05, "Packet has invalid LLC header" },
+	{ EINVAL | EUNIQ_06, "Packet decryption error", },
+	{ EINVAL | EUNIQ_07, "Invalid active scan requested" },
+
+	/* 802.11 status codes (IEEE Std 802.11-2007, Table 7-23) */
+	/* Maximum error length: 50 chars                                            | */
+	{ ECONNREFUSED | EUNIQ_01, "Unspecified failure" },
+	{ ECONNREFUSED | EUNIQ_0A, "Cannot support all requested capabilities" },
+	{ ECONNREFUSED | EUNIQ_0B, "Reassociation denied due to lack of association" },
+	{ ECONNREFUSED | EUNIQ_0C, "Association denied for another reason" },
+	{ ECONNREFUSED | EUNIQ_0D, "Authentication algorithm unsupported" },
+	{ ECONNREFUSED | EUNIQ_0E, "Authentication sequence number unexpected" },
+	{ ECONNREFUSED | EUNIQ_0F, "Authentication rejected due to challenge failure" },
+	{ ECONNREFUSED | EUNIQ_10, "Authentication rejected due to timeout" },
+	{ ECONNREFUSED | EUNIQ_11, "Association denied because AP is out of resources" },
+	{ ECONNREFUSED | EUNIQ_12, "Association denied; basic rate support required" },
+	{ ECONNREFUSED | EUNIQ_13, "Association denied; short preamble support req'd" },
+	{ ECONNREFUSED | EUNIQ_14, "Association denied; PBCC modulation support req'd" },
+	{ ECONNREFUSED | EUNIQ_15, "Association denied; Channel Agility support req'd" },
+	{ ECONNREFUSED | EUNIQ_16, "Association denied; Spectrum Management required" },
+	{ ECONNREFUSED | EUNIQ_17, "Association denied; Power Capability unacceptable" },
+	{ ECONNREFUSED | EUNIQ_18, "Association denied; Supported Channels unacceptable" },
+	{ ECONNREFUSED | EUNIQ_19, "Association denied; Short Slot Tume support req'd" },
+	{ ECONNREFUSED | EUNIQ_1A, "Association denied; DSSS-OFDM support required" },
+	{ EHOSTUNREACH,            "Unspecified, QoS-related failure" },
+	{ EHOSTUNREACH | EUNIQ_01, "Association denied; QoS AP out of QoS resources" },
+	{ EHOSTUNREACH | EUNIQ_02, "Association denied due to excessively poor link" },
+	{ EHOSTUNREACH | EUNIQ_03, "Association denied; QoS support required" },
+	{ EHOSTUNREACH | EUNIQ_05, "The request has been declined" },
+	{ EHOSTUNREACH | EUNIQ_06, "Request unsuccessful due to invalid parameters" },
+	{ EHOSTUNREACH | EUNIQ_07, "TS not created due to bad specification" },
+	{ EHOSTUNREACH | EUNIQ_08, "Invalid information element" },
+	{ EHOSTUNREACH | EUNIQ_09, "Invalid group cipher" },
+	{ EHOSTUNREACH | EUNIQ_0A, "Invalid pairwise cipher" },
+	{ EHOSTUNREACH | EUNIQ_0B, "Invalid AKMP" },
+	{ EHOSTUNREACH | EUNIQ_0C, "Unsupported RSN information element version" },
+	{ EHOSTUNREACH | EUNIQ_0D, "Invalid RSN information element capabilities" },
+	{ EHOSTUNREACH | EUNIQ_0E, "Cipher suite rejected because of security policy" },
+	{ EHOSTUNREACH | EUNIQ_0F, "TS not created due to insufficient delay" },
+	{ EHOSTUNREACH | EUNIQ_10, "Direct link is not allowed in the BSS by policy" },
+	{ EHOSTUNREACH | EUNIQ_11, "The Destination STA is not present within the BSS" },
+	{ EHOSTUNREACH | EUNIQ_12, "The Destination STA is not a QoS STA" },
+	{ EHOSTUNREACH | EUNIQ_13, "Association denied; Listen Interval is too large" },
+
+	/* 802.11 reason codes (IEEE Std 802.11-2007, Table 7-22) */
+	/* Maximum error length: 50 chars                                          | */
+	{ ECONNRESET | EUNIQ_01, "Unspecified reason" },
+	{ ECONNRESET | EUNIQ_02, "Previous authentication no longer valid" },
+	{ ECONNRESET | EUNIQ_03, "Deauthenticated due to leaving network" },
+	{ ECONNRESET | EUNIQ_04, "Disassociated due to inactivity" },
+	{ ECONNRESET | EUNIQ_05, "Disassociated because AP is out of resources" },
+	{ ECONNRESET | EUNIQ_06, "Class 2 frame received from nonauthenticated STA" },
+	{ ECONNRESET | EUNIQ_07, "Class 3 frame received from nonassociated STA" },
+	{ ECONNRESET | EUNIQ_08, "Disassociated due to roaming" },
+	{ ECONNRESET | EUNIQ_09, "STA requesting (re)association not authenticated" },
+	{ ECONNRESET | EUNIQ_0A, "Disassociated; Power Capability unacceptable" },
+	{ ECONNRESET | EUNIQ_0B, "Disassociated; Supported Channels unacceptable" },
+	{ ECONNRESET | EUNIQ_0D, "Invalid information element" },
+	{ ECONNRESET | EUNIQ_0E, "Message integrity code (MIC) failure" },
+	{ ECONNRESET | EUNIQ_0F, "4-Way Handshake timeout" },
+	{ ECONNRESET | EUNIQ_10, "Group Key Handshake timeout" },
+	{ ECONNRESET | EUNIQ_11, "4-Way Handshake information element changed unduly" },
+	{ ECONNRESET | EUNIQ_12, "Invalid group cipher" },
+	{ ECONNRESET | EUNIQ_13, "Invalid pairwise cipher" },
+	{ ECONNRESET | EUNIQ_14, "Invalid AKMP" },
+	{ ECONNRESET | EUNIQ_15, "Unsupported RSN information element version" },
+	{ ECONNRESET | EUNIQ_16, "Invalid RSN information element capabilities" },
+	{ ECONNRESET | EUNIQ_17, "IEEE 802.1X authentication failed" },
+	{ ECONNRESET | EUNIQ_18, "Cipher suite rejected because of security policy" },
+	{ ENETRESET,            "Disassociated for unspecified, QoS-related reason" },
+	{ ENETRESET | EUNIQ_01, "Disassociated; QoS AP is out of QoS resources" },
+	{ ENETRESET | EUNIQ_02, "Disassociated due to excessively poor link" },
+	{ ENETRESET | EUNIQ_03, "Disassociated due to TXOP limit violation" },
+	{ ENETRESET | EUNIQ_04, "Requested; STA is leaving the BSS (or resetting)" },
+	{ ENETRESET | EUNIQ_05, "Requested; does not want to use the mechanism" },
+	{ ENETRESET | EUNIQ_06, "Requested; setup is required" },
+	{ ENETRESET | EUNIQ_07, "Requested from peer STA due to timeout" },
+	{ ENETRESET | EUNIQ_0D, "Peer STA does not support requested cipher suite" },
+};
diff --git a/gpxe/src/image/efi_image.c b/gpxe/src/image/efi_image.c
index ae95deb..60d150a 100644
--- a/gpxe/src/image/efi_image.c
+++ b/gpxe/src/image/efi_image.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <errno.h>
 #include <gpxe/efi/efi.h>
 #include <gpxe/image.h>
diff --git a/gpxe/src/image/elf.c b/gpxe/src/image/elf.c
index cb2b0f5..a0ec065 100644
--- a/gpxe/src/image/elf.c
+++ b/gpxe/src/image/elf.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -42,11 +44,14 @@ typedef Elf32_Off	Elf_Off;
  *
  * @v image		ELF file
  * @v phdr		ELF program header
+ * @v ehdr		ELF executable header
  * @ret rc		Return status code
  */
-static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) {
+static int elf_load_segment ( struct image *image, Elf_Phdr *phdr,
+			      Elf_Ehdr *ehdr ) {
 	physaddr_t dest;
 	userptr_t buffer;
+	unsigned long e_offset;
 	int rc;
 
 	/* Do nothing for non-PT_LOAD segments */
@@ -55,7 +60,7 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) {
 
 	/* Check segment lies within image */
 	if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
-		DBG ( "ELF segment outside ELF file\n" );
+		DBGC ( image, "ELF %p segment outside image\n", image );
 		return -ENOEXEC;
 	}
 
@@ -67,26 +72,43 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) {
 	if ( ! dest )
 		dest = phdr->p_vaddr;
 	if ( ! dest ) {
-		DBG ( "ELF segment loads to physical address 0\n" );
+		DBGC ( image, "ELF %p segment loads to physical address 0\n",
+		       image );
 		return -ENOEXEC;
 	}
 	buffer = phys_to_user ( dest );
 
-	DBG ( "ELF loading segment [%x,%x) to [%x,%x,%x)\n",
-	      phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
-	      phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
-	      ( phdr->p_paddr + phdr->p_memsz ) );
+	DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image,
+	       phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
+	       phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
+	       ( phdr->p_paddr + phdr->p_memsz ) );
 
 	/* Verify and prepare segment */
 	if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
 				   phdr->p_memsz ) ) != 0 ) {
-		DBG ( "ELF could not prepare segment: %s\n", strerror ( rc ) );
+		DBGC ( image, "ELF %p could not prepare segment: %s\n",
+		       image, strerror ( rc ) );
 		return rc;
 	}
 
 	/* Copy image to segment */
 	memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
 
+	/* Set execution address, if it lies within this segment */
+	if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) {
+		image->priv.phys = ehdr->e_entry;
+		DBGC ( image, "ELF %p found physical entry point at %lx\n",
+		       image, image->priv.phys );
+	} else if ( ( e_offset = ( ehdr->e_entry - phdr->p_vaddr ) )
+		    < phdr->p_filesz ) {
+		if ( ! image->priv.phys ) {
+			image->priv.phys = ( dest + e_offset );
+			DBGC ( image, "ELF %p found virtual entry point at %lx"
+			       " (virt %lx)\n", image, image->priv.phys,
+			       ( ( unsigned long ) ehdr->e_entry ) );
+		}
+	}
+
 	return 0;
 }
 
@@ -109,25 +131,32 @@ int elf_load ( struct image *image ) {
 	/* Read ELF header */
 	copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
 	if ( memcmp ( &ehdr.e_ident[EI_MAG0], ELFMAG, SELFMAG ) != 0 ) {
-		DBG ( "Invalid ELF signature\n" );
+		DBGC ( image, "ELF %p has invalid signature\n", image );
 		return -ENOEXEC;
 	}
 
+	/* Invalidate execution address */
+	image->priv.phys = 0;
+
 	/* Read ELF program headers */
 	for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ;
 	      phoff += ehdr.e_phentsize, phnum-- ) {
 		if ( phoff > image->len ) {
-			DBG ( "ELF program header %d outside ELF image\n",
-			      phnum );
+			DBGC ( image, "ELF %p program header %d outside "
+			       "image\n", image, phnum );
 			return -ENOEXEC;
 		}
 		copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
-		if ( ( rc = elf_load_segment ( image, &phdr ) ) != 0 )
+		if ( ( rc = elf_load_segment ( image, &phdr, &ehdr ) ) != 0 )
 			return rc;
 	}
 
-	/* Record execution entry point in image private data field */
-	image->priv.phys = ehdr.e_entry;
+	/* Check for a valid execution address */
+	if ( ! image->priv.phys ) {
+		DBGC ( image, "ELF %p entry point %lx outside image\n",
+		       image, ( ( unsigned long ) ehdr.e_entry ) );
+		return -ENOEXEC;
+	}
 
 	return 0;
 }
diff --git a/gpxe/src/image/embedded.c b/gpxe/src/image/embedded.c
index 4f76357..58a14ea 100644
--- a/gpxe/src/image/embedded.c
+++ b/gpxe/src/image/embedded.c
@@ -6,6 +6,8 @@
  * fetching over the network.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <gpxe/image.h>
 #include <gpxe/uaccess.h>
diff --git a/gpxe/src/image/script.c b/gpxe/src/image/script.c
index 2d24274..0835ecb 100644
--- a/gpxe/src/image/script.c
+++ b/gpxe/src/image/script.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -25,6 +27,7 @@
 
 #include <string.h>
 #include <stdlib.h>
+#include <ctype.h>
 #include <errno.h>
 #include <gpxe/image.h>
 
@@ -88,8 +91,8 @@ static int script_exec ( struct image *image ) {
  * @ret rc		Return status code
  */
 static int script_load ( struct image *image ) {
-	static const char magic[] = "#!gpxe\n";
-	char test[ sizeof ( magic ) - 1 ];
+	static const char magic[] = "#!gpxe";
+	char test[ sizeof ( magic ) - 1 /* NUL */ + 1 /* terminating space */];
 
 	/* Sanity check */
 	if ( image->len < sizeof ( test ) ) {
@@ -99,7 +102,8 @@ static int script_load ( struct image *image ) {
 
 	/* Check for magic signature */
 	copy_from_user ( test, image->data, 0, sizeof ( test ) );
-	if ( memcmp ( test, magic, sizeof ( test ) ) != 0 ) {
+	if ( ( memcmp ( test, magic, ( sizeof ( test ) - 1 ) ) != 0 ) ||
+	     ! isspace ( test[ sizeof ( test ) - 1 ] ) ) {
 		DBG ( "Invalid magic signature\n" );
 		return -ENOEXEC;
 	}
diff --git a/gpxe/src/image/segment.c b/gpxe/src/image/segment.c
index 9bd60f9..e247453 100644
--- a/gpxe/src/image/segment.c
+++ b/gpxe/src/image/segment.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -26,6 +28,7 @@
 #include <errno.h>
 #include <gpxe/uaccess.h>
 #include <gpxe/memmap.h>
+#include <gpxe/errortab.h>
 #include <gpxe/segment.h>
 
 /**
@@ -72,3 +75,13 @@ int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) {
 	      start, mid, end );
 	return -ERANGE;
 }
+
+/**
+ * Segment-specific error messages
+ *
+ * This error happens sufficiently often to merit a user-friendly
+ * description.
+ */
+struct errortab segment_errors[] __errortab = {
+	{ ERANGE, "Requested memory not available" },
+};
diff --git a/gpxe/src/include/assert.h b/gpxe/src/include/assert.h
index 93750a1..cc784bc 100644
--- a/gpxe/src/include/assert.h
+++ b/gpxe/src/include/assert.h
@@ -10,6 +10,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef NDEBUG
 #define ASSERTING 0
 #else
diff --git a/gpxe/src/include/byteswap.h b/gpxe/src/include/byteswap.h
index 6c3ced2..466759c 100644
--- a/gpxe/src/include/byteswap.h
+++ b/gpxe/src/include/byteswap.h
@@ -1,6 +1,8 @@
 #ifndef ETHERBOOT_BYTESWAP_H
 #define ETHERBOOT_BYTESWAP_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "endian.h"
 #include "bits/byteswap.h"
 
diff --git a/gpxe/src/include/compiler.h b/gpxe/src/include/compiler.h
index 889c240..ea81fc8 100644
--- a/gpxe/src/include/compiler.h
+++ b/gpxe/src/include/compiler.h
@@ -24,51 +24,201 @@
  *
  */
 
-/* Not quite sure why cpp requires two levels of macro call in order
- * to actually expand OBJECT...
- */
-#undef _H1
-#define _H1( x, y ) x ## y
-#undef _H2
-#define _H2( x, y ) _H1 ( x, y )
-#define PREFIX_OBJECT(prefix) _H2 ( prefix, OBJECT )
-#define OBJECT_SYMBOL PREFIX_OBJECT(obj_)
-#undef _STR
-#define _STR(s) #s
-#undef _XSTR
-#define _XSTR(s) _STR(s)
-#define OBJECT_SYMBOL_STR _XSTR ( OBJECT_SYMBOL )
+/* Force visibility of all symbols to "hidden", i.e. inform gcc that
+ * all symbol references resolve strictly within our final binary.
+ * This avoids unnecessary PLT/GOT entries on x86_64.
+ *
+ * This is a stronger claim than specifying "-fvisibility=hidden",
+ * since it also affects symbols marked with "extern".
+ */
+#ifndef ASSEMBLY
+#if __GNUC__ >= 4
+#pragma GCC visibility push(hidden)
+#endif
+#endif /* ASSEMBLY */
+
+#undef _S1
+#undef _S2
+#undef _C1
+#undef _C2
+
+/** Concatenate non-expanded arguments */
+#define _C1( x, y ) x ## y
+/** Concatenate expanded arguments */
+#define _C2( x, y ) _C1 ( x, y )
+
+/** Stringify non-expanded argument */
+#define _S1( x ) #x
+/** Stringify expanded argument */
+#define _S2( x ) _S1 ( x )
+
+/**
+ * @defgroup symmacros Macros to provide or require explicit symbols
+ * @{
+ */
 
+/** Provide a symbol within this object file */
 #ifdef ASSEMBLY
+#define PROVIDE_SYMBOL( _sym )				\
+	.globl	_sym ;					\
+	.comm	_sym, 0
+#else /* ASSEMBLY */
+#define PROVIDE_SYMBOL( _sym )				\
+	char _sym[0]
+#endif /* ASSEMBLY */
 
-	.globl	OBJECT_SYMBOL
-	.equ	OBJECT_SYMBOL, 0
+/** Require a symbol within this object file
+ *
+ * The symbol is referenced by a relocation in a discarded section, so
+ * if it is not available at link time the link will fail.
+ */
+#ifdef ASSEMBLY
+#define REQUIRE_SYMBOL( _sym )				\
+	.section ".discard", "a", @progbits ;		\
+	.extern	_sym ;					\
+	.long	_sym ;					\
+	.previous
+#else /* ASSEMBLY */
+#define REQUIRE_SYMBOL( _sym )				\
+	extern char _sym;				\
+	static char * _C2 ( _C2 ( __require_, _sym ), _C2 ( _, __LINE__ ) ) \
+		__attribute__ (( section ( ".discard" ), used )) \
+		= &_sym
+#endif
 
+/** Request that a symbol be available at runtime
+ *
+ * The requested symbol is entered as undefined into the symbol table
+ * for this object, so the linker will pull in other object files as
+ * necessary to satisfy the reference. However, the undefined symbol
+ * is not referenced in any relocations, so the link can still succeed
+ * if no file contains it.
+ *
+ * A symbol passed to this macro may not be referenced anywhere
+ * else in the file. If you want to do that, see IMPORT_SYMBOL().
+ */
+#ifdef ASSEMBLY
+#define REQUEST_SYMBOL( _sym )				\
+	.equ	__need_ ## _sym, _sym
 #else /* ASSEMBLY */
+#define REQUEST_SYMBOL( _sym )				\
+	__asm__ ( ".equ\t__need_" #_sym ", " #_sym )
+#endif /* ASSEMBLY */
 
-__asm__ ( ".globl\t" OBJECT_SYMBOL_STR );
-__asm__ ( ".equ\t" OBJECT_SYMBOL_STR ", 0" );
+/** Set up a symbol to be usable in another file by IMPORT_SYMBOL()
+ *
+ * The symbol must already be marked as global.
+ */
+#define EXPORT_SYMBOL( _sym )	PROVIDE_SYMBOL ( __export_ ## _sym )
+
+/** Make a symbol usable to this file if available at link time
+ *
+ * If no file passed to the linker contains the symbol, it will have
+ * @c NULL value to future uses. Keep in mind that the symbol value is
+ * really the @e address of a variable or function; see the code
+ * snippet below.
+ *
+ * In C using IMPORT_SYMBOL, you must specify the declaration as the
+ * second argument, for instance
+ *
+ * @code
+ *   IMPORT_SYMBOL ( my_func, int my_func ( int arg ) );
+ *   IMPORT_SYMBOL ( my_var, int my_var );
+ *
+ *   void use_imports ( void ) {
+ * 	if ( my_func && &my_var )
+ * 	   my_var = my_func ( my_var );
+ *   }
+ * @endcode
+ *
+ * GCC considers a weak declaration to override a strong one no matter
+ * which comes first, so it is safe to include a header file declaring
+ * the imported symbol normally, but providing the declaration to
+ * IMPORT_SYMBOL is still required.
+ *
+ * If no EXPORT_SYMBOL declaration exists for the imported symbol in
+ * another file, the behavior will be most likely be identical to that
+ * for an unavailable symbol.
+ */
+#ifdef ASSEMBLY
+#define IMPORT_SYMBOL( _sym )				\
+	REQUEST_SYMBOL ( __export_ ## _sym ) ;		\
+	.weak	_sym
+#else /* ASSEMBLY */
+#define IMPORT_SYMBOL( _sym, _decl )			\
+	REQUEST_SYMBOL ( __export_ ## _sym ) ;		\
+	extern _decl __attribute__ (( weak ))
+#endif
+
+/** @} */
 
 /**
- * Drag in an object by object name.
+ * @defgroup objmacros Macros to provide or require explicit objects
+ * @{
+ */
+
+#define PREFIX_OBJECT( _prefix ) _C2 ( _prefix, OBJECT )
+#define OBJECT_SYMBOL PREFIX_OBJECT ( obj_ )
+#define REQUEST_EXPANDED( _sym ) REQUEST_SYMBOL ( _sym )
+#define CONFIG_SYMBOL PREFIX_OBJECT ( obj_config_ )
+
+/** Always provide the symbol for the current object (defined by -DOBJECT) */
+PROVIDE_SYMBOL ( OBJECT_SYMBOL );
+
+/** Pull in an object-specific configuration file if available */
+REQUEST_EXPANDED ( CONFIG_SYMBOL );
+
+/** Explicitly require another object */
+#define REQUIRE_OBJECT( _obj ) REQUIRE_SYMBOL ( obj_ ## _obj )
+
+/** Pull in another object if it exists */
+#define REQUEST_OBJECT( _obj ) REQUEST_SYMBOL ( obj_ ## _obj )
+
+/** @} */
+
+/** Select file identifier for errno.h (if used) */
+#define ERRFILE PREFIX_OBJECT ( ERRFILE_ )
+
+/**
+ * @defgroup weakmacros Macros to manage weak symbol definitions
  *
- * Macro to allow objects to explicitly drag in other objects by
- * object name.  Used by config.c.
+ * Weak symbols allow one to reference a function in another file
+ * without necessarily requiring that file to be linked in. In their
+ * native form, the function will be @c NULL if its file is not linked
+ * in; these macros provide an inline wrapper that returns an
+ * appropriate error indication or default value.
  *
+ * @{
  */
-#define REQUIRE_OBJECT(object) \
-	__asm__ ( ".equ\tneed_" #object ", obj_" #object );
+#ifndef ASSEMBLY
 
-/* Force visibility of all symbols to "hidden", i.e. inform gcc that
- * all symbol references resolve strictly within our final binary.
- * This avoids unnecessary PLT/GOT entries on x86_64.
+/** Mangle @a name into its weakly-referenced implementation */
+#define __weak_impl( name )   _w_ ## name
+
+/**
+ * Declare a weak function with inline safety wrapper
  *
- * This is a stronger claim than specifying "-fvisibility=hidden",
- * since it also affects symbols marked with "extern".
+ * @v ret	Return type of weak function
+ * @v name	Name of function to expose
+ * @v proto	Parenthesized list of arguments with types
+ * @v args	Parenthesized list of argument names
+ * @v dfl	Value to return if weak function is not available
  */
-#if __GNUC__ >= 4
-#pragma GCC visibility push(hidden)
+#define __weak_decl( ret, name, proto, args, dfl )		\
+        ret __weak_impl( name ) proto __attribute__ (( weak ));	\
+        static inline ret name proto {				\
+                if ( __weak_impl( name ) )			\
+                        return __weak_impl( name ) args;	\
+                return dfl;					\
+        }
+
 #endif
+/** @} */
+
+/** @defgroup dbg Debugging infrastructure
+ * @{
+ */
+#ifndef ASSEMBLY
 
 /** @def DBG
  *
@@ -117,12 +267,7 @@ __asm__ ( ".equ\t" OBJECT_SYMBOL_STR ", 0" );
  * DEBUG_LEVEL will be inserted into the object file.
  *
  */
-#define DEBUG_SYMBOL PREFIX_OBJECT(debug_)
-
-#if DEBUG_SYMBOL
-#define DEBUG_SYMBOL_STR _XSTR ( DEBUG_SYMBOL )
-__asm__ ( ".equ\tDBGLVL, " DEBUG_SYMBOL_STR );
-#endif
+#define DEBUG_SYMBOL PREFIX_OBJECT ( debug_ )
 
 /** printf() for debugging
  *
@@ -212,7 +357,8 @@ int __debug_disable;
  * @v len		Length of data
  */
 #define DBG_HD_IF( level, data, len ) do {			\
-		DBG_HDA_IF ( level, data, data, len );		\
+		const void *_data = data;			\
+		DBG_HDA_IF ( level, _data, _data, len );	\
 	} while ( 0 )
 
 /**
@@ -304,8 +450,13 @@ int __debug_disable;
 #define NDEBUG
 #endif
 
-/** Select file identifier for errno.h (if used) */
-#define ERRFILE PREFIX_OBJECT ( ERRFILE_ )
+#endif /* ASSEMBLY */
+/** @} */
+
+/** @defgroup attrs Miscellaneous attributes
+ * @{
+ */
+#ifndef ASSEMBLY
 
 /** Declare a data structure as packed. */
 #define PACKED __attribute__ (( packed ))
@@ -373,13 +524,127 @@ int __debug_disable;
  */
 #define __shared __asm__ ( "_shared_bss" ) __aligned
 
+#endif /* ASSEMBLY */
+/** @} */
+
 /**
  * Optimisation barrier
  */
+#ifndef ASSEMBLY
 #define barrier() __asm__ __volatile__ ( "" : : : "memory" )
-
 #endif /* ASSEMBLY */
 
+/**
+ * @defgroup licences Licence declarations
+ *
+ * For reasons that are partly historical, various different files
+ * within the gPXE codebase have differing licences.
+ *
+ * @{
+ */
+
+/** Declare a file as being in the public domain
+ *
+ * This licence declaration is applicable when a file states itself to
+ * be in the public domain.
+ */
+#define FILE_LICENCE_PUBLIC_DOMAIN \
+	PROVIDE_SYMBOL ( __licence_public_domain )
+
+/** Declare a file as being under version 2 (or later) of the GNU GPL
+ *
+ * This licence declaration is applicable when a file states itself to
+ * be licensed under the GNU GPL; "either version 2 of the License, or
+ * (at your option) any later version".
+ */
+#define FILE_LICENCE_GPL2_OR_LATER \
+	PROVIDE_SYMBOL ( __licence_gpl2_or_later )
+
+/** Declare a file as being under version 2 of the GNU GPL
+ *
+ * This licence declaration is applicable when a file states itself to
+ * be licensed under version 2 of the GPL, and does not include the
+ * "or, at your option, any later version" clause.
+ */
+#define FILE_LICENCE_GPL2_ONLY \
+	PROVIDE_SYMBOL ( __licence_gpl2_only )
+
+/** Declare a file as being under any version of the GNU GPL
+ *
+ * This licence declaration is applicable when a file states itself to
+ * be licensed under the GPL, but does not specify a version.
+ *
+ * According to section 9 of the GPLv2, "If the Program does not
+ * specify a version number of this License, you may choose any
+ * version ever published by the Free Software Foundation".
+ */
+#define FILE_LICENCE_GPL_ANY \
+	PROVIDE_SYMBOL ( __licence_gpl_any )
+
+/** Declare a file as being under the three-clause BSD licence
+ *
+ * This licence declaration is applicable when a file states itself to
+ * be licensed under terms allowing redistribution in source and
+ * binary forms (with or without modification) provided that:
+ *
+ *     redistributions of source code retain the copyright notice,
+ *     list of conditions and any attached disclaimers
+ *
+ *     redistributions in binary form reproduce the copyright notice,
+ *     list of conditions and any attached disclaimers in the
+ *     documentation and/or other materials provided with the
+ *     distribution
+ *
+ *     the name of the author is not used to endorse or promote
+ *     products derived from the software without specific prior
+ *     written permission
+ *
+ * It is not necessary for the file to explicitly state that it is
+ * under a "BSD" licence; only that the licensing terms be
+ * functionally equivalent to the standard three-clause BSD licence.
+ */
+#define FILE_LICENCE_BSD3 \
+	PROVIDE_SYMBOL ( __licence_bsd3 )
+
+/** Declare a file as being under the two-clause BSD licence
+ *
+ * This licence declaration is applicable when a file states itself to
+ * be licensed under terms allowing redistribution in source and
+ * binary forms (with or without modification) provided that:
+ *
+ *     redistributions of source code retain the copyright notice,
+ *     list of conditions and any attached disclaimers
+ *
+ *     redistributions in binary form reproduce the copyright notice,
+ *     list of conditions and any attached disclaimers in the
+ *     documentation and/or other materials provided with the
+ *     distribution
+ *
+ * It is not necessary for the file to explicitly state that it is
+ * under a "BSD" licence; only that the licensing terms be
+ * functionally equivalent to the standard two-clause BSD licence.
+ */
+#define FILE_LICENCE_BSD2 \
+	PROVIDE_SYMBOL ( __licence_bsd2 )
+
+/** Declare a file as being under the one-clause MIT-style licence
+ *
+ * This licence declaration is applicable when a file states itself to
+ * be licensed under terms allowing redistribution for any purpose
+ * with or without fee, provided that the copyright notice and
+ * permission notice appear in all copies.
+ */
+#define FILE_LICENCE_MIT \
+	PROVIDE_SYMBOL ( __licence_mit )
+
+/** Declare a particular licence as applying to a file */
+#define FILE_LICENCE( _licence ) FILE_LICENCE_ ## _licence
+
+/** @} */
+
+/* This file itself is under GPLv2-or-later */
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <bits/compiler.h>
 
 #endif /* COMPILER_H */
diff --git a/gpxe/src/include/console.h b/gpxe/src/include/console.h
index 9addd52..62fedf5 100644
--- a/gpxe/src/include/console.h
+++ b/gpxe/src/include/console.h
@@ -14,6 +14,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * A console driver
  *
@@ -85,6 +87,9 @@ struct console_driver {
 	int ( *iskey ) ( void );
 };
 
+/** Console driver table */
+#define CONSOLES __table ( struct console_driver, "consoles" )
+
 /**
  * Mark a <tt> struct console_driver </tt> as being part of the
  * console drivers table.
@@ -102,7 +107,7 @@ struct console_driver {
  * @endcode
  *
  */
-#define __console_driver __table ( struct console_driver, console, 01 )
+#define __console_driver __table_entry ( CONSOLES, 01 )
 
 /* Function prototypes */
 
diff --git a/gpxe/src/include/ctype.h b/gpxe/src/include/ctype.h
index 7740443..ed4d884 100644
--- a/gpxe/src/include/ctype.h
+++ b/gpxe/src/include/ctype.h
@@ -6,6 +6,8 @@
  * Character types
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define isdigit(c)	((c) >= '0' && (c) <= '9')
 #define islower(c)	((c) >= 'a' && (c) <= 'z')
 #define isupper(c)	((c) >= 'A' && (c) <= 'Z')
@@ -24,4 +26,6 @@ static inline unsigned char toupper(unsigned char c)
 	return c;
 }
 
+extern int isspace ( int c );
+
 #endif /* _CTYPE_H */
diff --git a/gpxe/src/include/curses.h b/gpxe/src/include/curses.h
index 6b1c42d..e2c5af2 100644
--- a/gpxe/src/include/curses.h
+++ b/gpxe/src/include/curses.h
@@ -10,6 +10,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #undef  ERR
 #define ERR	(-1)
 
diff --git a/gpxe/src/include/debug.h b/gpxe/src/include/debug.h
deleted file mode 100644
index bb5d33f..0000000
--- a/gpxe/src/include/debug.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef DEBUG_H
-#define DEBUG_H
-
-//#include <lib.h>
-extern int last_putchar;
-
-/* Defining DEBUG_THIS before including this file enables debug() macro
- * for the file. DEBUG_ALL is for global control. */
-
-#if DEBUG_THIS || DEBUG_ALL
-#define DEBUG 1
-#else
-#undef DEBUG
-#endif
-
-#if DEBUG
-# define debug(...) \
-    ((last_putchar=='\n' ? printf("%s: ", __FUNCTION__) : 0), \
-    printf(__VA_ARGS__))
-# define debug_hexdump hexdump
-#else
-# define debug(...) /* nothing */
-# define debug_hexdump(...) /* nothing */
-#endif
-
-#define debugx debug
-
-#endif /* DEBUG_H */
diff --git a/gpxe/src/include/dhcp.h b/gpxe/src/include/dhcp.h
deleted file mode 100644
index deba219..0000000
--- a/gpxe/src/include/dhcp.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef DHCP_H
-#define DHCP_H
-
-#include "stdint.h"
-
-struct dhcp_dev_id {
-	uint8_t		bus_type;
-	uint16_t	vendor_id;
-	uint16_t	device_id;
-} __attribute__ (( packed ));
-
-#endif /* DHCP_H */
diff --git a/gpxe/src/include/elf.h b/gpxe/src/include/elf.h
index fadc0bd..04022b6 100644
--- a/gpxe/src/include/elf.h
+++ b/gpxe/src/include/elf.h
@@ -1,6 +1,8 @@
 #ifndef ELF_H
 #define ELF_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define EI_NIDENT	16	/* Size of e_ident array. */
 
 /* Values for e_type. */
diff --git a/gpxe/src/include/endian.h b/gpxe/src/include/endian.h
index 3200622..9682cf9 100644
--- a/gpxe/src/include/endian.h
+++ b/gpxe/src/include/endian.h
@@ -1,6 +1,8 @@
 #ifndef ETHERBOOT_ENDIAN_H
 #define ETHERBOOT_ENDIAN_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* Definitions for byte order, according to significance of bytes,
    from low addresses to high addresses.  The value is what you get by
    putting '4' in the most significant byte, '3' in the second most
diff --git a/gpxe/src/include/errno.h b/gpxe/src/include/errno.h
index 58dff1f..56095ec 100644
--- a/gpxe/src/include/errno.h
+++ b/gpxe/src/include/errno.h
@@ -1,6 +1,8 @@
 #ifndef ERRNO_H
 #define ERRNO_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** @file
  *
  * Error codes
diff --git a/gpxe/src/include/etherboot.h b/gpxe/src/include/etherboot.h
index 2a46595..ad44e8a 100644
--- a/gpxe/src/include/etherboot.h
+++ b/gpxe/src/include/etherboot.h
@@ -6,6 +6,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
diff --git a/gpxe/src/include/getopt.h b/gpxe/src/include/getopt.h
index 2505223..0fe4356 100644
--- a/gpxe/src/include/getopt.h
+++ b/gpxe/src/include/getopt.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 
 enum getopt_argument_requirement {
diff --git a/gpxe/src/include/gpxe/acpi.h b/gpxe/src/include/gpxe/acpi.h
index 33b1b2b..12edda9 100644
--- a/gpxe/src/include/gpxe/acpi.h
+++ b/gpxe/src/include/gpxe/acpi.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /**
diff --git a/gpxe/src/include/gpxe/aes.h b/gpxe/src/include/gpxe/aes.h
index bdb4b35..fc1694b 100644
--- a/gpxe/src/include/gpxe/aes.h
+++ b/gpxe/src/include/gpxe/aes.h
@@ -1,8 +1,30 @@
 #ifndef _GPXE_AES_H
 #define _GPXE_AES_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct cipher_algorithm;
 
+/** Basic AES blocksize */
+#define AES_BLOCKSIZE 16
+
+#include "crypto/axtls/crypto.h"
+
+/** AES context */
+struct aes_context {
+	/** AES context for AXTLS */
+	AES_CTX axtls_ctx;
+	/** Cipher is being used for decrypting */
+	int decrypting;
+};
+
+/** AES context size */
+#define AES_CTX_SIZE sizeof ( struct aes_context )
+
+extern struct cipher_algorithm aes_algorithm;
 extern struct cipher_algorithm aes_cbc_algorithm;
 
+int aes_wrap ( const void *kek, const void *src, void *dest, int nblk );
+int aes_unwrap ( const void *kek, const void *src, void *dest, int nblk );
+
 #endif /* _GPXE_AES_H */
diff --git a/gpxe/src/include/gpxe/ansiesc.h b/gpxe/src/include/gpxe/ansiesc.h
index ccc4ca6..85f7a9f 100644
--- a/gpxe/src/include/gpxe/ansiesc.h
+++ b/gpxe/src/include/gpxe/ansiesc.h
@@ -26,6 +26,8 @@
  * 
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** A handler for an escape sequence */
 struct ansiesc_handler {
 	/** The control function identifier
diff --git a/gpxe/src/include/gpxe/aoe.h b/gpxe/src/include/gpxe/aoe.h
index 6de6b96..6b42fd5 100644
--- a/gpxe/src/include/gpxe/aoe.h
+++ b/gpxe/src/include/gpxe/aoe.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/list.h>
 #include <gpxe/if_ether.h>
diff --git a/gpxe/src/include/gpxe/api.h b/gpxe/src/include/gpxe/api.h
index df5d1ae..ff2ba59 100644
--- a/gpxe/src/include/gpxe/api.h
+++ b/gpxe/src/include/gpxe/api.h
@@ -11,6 +11,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** @defgroup Single-implementation APIs
  *
  * These are APIs for which only a single implementation may be
diff --git a/gpxe/src/include/gpxe/arc4.h b/gpxe/src/include/gpxe/arc4.h
new file mode 100644
index 0000000..9917628
--- /dev/null
+++ b/gpxe/src/include/gpxe/arc4.h
@@ -0,0 +1,22 @@
+#ifndef _GPXE_ARC4_H
+#define _GPXE_ARC4_H
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct cipher_algorithm;
+
+#include <stdint.h>
+
+struct arc4_ctx {
+	int i, j;
+	u8 state[256];
+};
+
+#define ARC4_CTX_SIZE sizeof ( struct arc4_ctx )
+
+extern struct cipher_algorithm arc4_algorithm;
+
+void arc4_skip ( const void *key, size_t keylen, size_t skip,
+		 const void *src, void *dst, size_t msglen );
+
+#endif /* _GPXE_ARC4_H */
diff --git a/gpxe/src/include/gpxe/arp.h b/gpxe/src/include/gpxe/arp.h
index 6464ce0..0623d35 100644
--- a/gpxe/src/include/gpxe/arp.h
+++ b/gpxe/src/include/gpxe/arp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/tables.h>
 
 struct net_device;
@@ -26,9 +28,12 @@ struct arp_net_protocol {
 			  const void *net_addr );
 };
 
+/** ARP protocol table */
+#define ARP_NET_PROTOCOLS \
+	__table ( struct arp_net_protocol, "arp_net_protocols" )
+
 /** Declare an ARP protocol */
-#define __arp_net_protocol \
-	__table ( struct arp_net_protocol, arp_net_protocols, 01 )
+#define __arp_net_protocol __table_entry ( ARP_NET_PROTOCOLS, 01 )
 
 extern struct net_protocol arp_protocol;
 
diff --git a/gpxe/src/include/gpxe/asn1.h b/gpxe/src/include/gpxe/asn1.h
index 5440c48..477c209 100644
--- a/gpxe/src/include/gpxe/asn1.h
+++ b/gpxe/src/include/gpxe/asn1.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define ASN1_INTEGER 0x02
 #define ASN1_BIT_STRING 0x03
 #define ASN1_OCTET_STRING 0x04
diff --git a/gpxe/src/include/gpxe/ata.h b/gpxe/src/include/gpxe/ata.h
index b6da393..3c56584 100644
--- a/gpxe/src/include/gpxe/ata.h
+++ b/gpxe/src/include/gpxe/ata.h
@@ -12,6 +12,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * An ATA Logical Block Address
  *
@@ -152,6 +154,8 @@ struct ata_command {
 	 * sectors in size.
 	 */
 	userptr_t data_in;
+	/** Command status code */
+	int rc;
 };
 
 /**
diff --git a/gpxe/src/include/gpxe/base64.h b/gpxe/src/include/gpxe/base64.h
index 3321971..e38bef0 100644
--- a/gpxe/src/include/gpxe/base64.h
+++ b/gpxe/src/include/gpxe/base64.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /**
diff --git a/gpxe/src/include/gpxe/bitbash.h b/gpxe/src/include/gpxe/bitbash.h
index 62bdce0..f2ba9f7 100644
--- a/gpxe/src/include/gpxe/bitbash.h
+++ b/gpxe/src/include/gpxe/bitbash.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct bit_basher;
 
 /** Bit-bashing operations */
diff --git a/gpxe/src/include/gpxe/bitmap.h b/gpxe/src/include/gpxe/bitmap.h
index 0c2f53c..d6911a5 100644
--- a/gpxe/src/include/gpxe/bitmap.h
+++ b/gpxe/src/include/gpxe/bitmap.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stddef.h>
 #include <stdlib.h>
diff --git a/gpxe/src/include/gpxe/bitops.h b/gpxe/src/include/gpxe/bitops.h
index 5405c85..8db3431 100644
--- a/gpxe/src/include/gpxe/bitops.h
+++ b/gpxe/src/include/gpxe/bitops.h
@@ -19,6 +19,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/include/gpxe/blockdev.h b/gpxe/src/include/gpxe/blockdev.h
index 8222984..cf28524 100644
--- a/gpxe/src/include/gpxe/blockdev.h
+++ b/gpxe/src/include/gpxe/blockdev.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/uaccess.h>
 
 struct block_device;
diff --git a/gpxe/src/include/gpxe/cbc.h b/gpxe/src/include/gpxe/cbc.h
index fcc115e..1262f1d 100644
--- a/gpxe/src/include/gpxe/cbc.h
+++ b/gpxe/src/include/gpxe/cbc.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/crypto.h>
 
 /**
diff --git a/gpxe/src/include/gpxe/chap.h b/gpxe/src/include/gpxe/chap.h
index 87e5484..e86ede3 100644
--- a/gpxe/src/include/gpxe/chap.h
+++ b/gpxe/src/include/gpxe/chap.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/md5.h>
 
diff --git a/gpxe/src/include/gpxe/command.h b/gpxe/src/include/gpxe/command.h
index 5d8057a..51ca6d6 100644
--- a/gpxe/src/include/gpxe/command.h
+++ b/gpxe/src/include/gpxe/command.h
@@ -1,6 +1,8 @@
 #ifndef _GPXE_COMMAND_H
 #define _GPXE_COMMAND_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/tables.h>
 
 /** A command-line command */
@@ -17,6 +19,8 @@ struct command {
 	int ( * exec ) ( int argc, char **argv );
 };
 
-#define __command __table ( struct command, commands, 01 )
+#define COMMANDS __table ( struct command, "commands" )
+
+#define __command __table_entry ( COMMANDS, 01 )
 
 #endif /* _GPXE_COMMAND_H */
diff --git a/gpxe/src/include/gpxe/cpio.h b/gpxe/src/include/gpxe/cpio.h
index ba6f844..f462cec 100644
--- a/gpxe/src/include/gpxe/cpio.h
+++ b/gpxe/src/include/gpxe/cpio.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** A CPIO archive header
  *
  * All field are hexadecimal ASCII numbers padded with '0' on the
diff --git a/gpxe/src/include/gpxe/crc32.h b/gpxe/src/include/gpxe/crc32.h
new file mode 100644
index 0000000..d1c7437
--- /dev/null
+++ b/gpxe/src/include/gpxe/crc32.h
@@ -0,0 +1,10 @@
+#ifndef _GPXE_CRC32_H
+#define _GPXE_CRC32_H
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+
+u32 crc32_le ( u32 seed, const void *data, size_t len );
+
+#endif
diff --git a/gpxe/src/include/gpxe/crypto.h b/gpxe/src/include/gpxe/crypto.h
index 10882d3..ecda5f6 100644
--- a/gpxe/src/include/gpxe/crypto.h
+++ b/gpxe/src/include/gpxe/crypto.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stddef.h>
 
@@ -127,7 +129,7 @@ static inline void cipher_encrypt ( struct cipher_algorithm *cipher,
 	cipher->encrypt ( ctx, src, dst, len );
 }
 #define cipher_encrypt( cipher, ctx, src, dst, len ) do {		\
-	assert ( ( len & ( (cipher)->blocksize - 1 ) ) == 0 );		\
+	assert ( ( (len) & ( (cipher)->blocksize - 1 ) ) == 0 );	\
 	cipher_encrypt ( (cipher), (ctx), (src), (dst), (len) );	\
 	} while ( 0 )
 
@@ -137,7 +139,7 @@ static inline void cipher_decrypt ( struct cipher_algorithm *cipher,
 	cipher->decrypt ( ctx, src, dst, len );
 }
 #define cipher_decrypt( cipher, ctx, src, dst, len ) do {		\
-	assert ( ( len & ( (cipher)->blocksize - 1 ) ) == 0 );		\
+	assert ( ( (len) & ( (cipher)->blocksize - 1 ) ) == 0 );	\
 	cipher_decrypt ( (cipher), (ctx), (src), (dst), (len) );	\
 	} while ( 0 )
 
@@ -149,4 +151,6 @@ extern struct digest_algorithm digest_null;
 extern struct cipher_algorithm cipher_null;
 extern struct pubkey_algorithm pubkey_null;
 
+void get_random_bytes ( void *buf, size_t len );
+
 #endif /* _GPXE_CRYPTO_H */
diff --git a/gpxe/src/include/gpxe/device.h b/gpxe/src/include/gpxe/device.h
index f40cc95..1db3ff9 100644
--- a/gpxe/src/include/gpxe/device.h
+++ b/gpxe/src/include/gpxe/device.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/list.h>
 #include <gpxe/tables.h>
 
@@ -102,7 +104,10 @@ struct root_driver {
 	void ( * remove ) ( struct root_device *rootdev );
 };
 
+/** Root device table */
+#define ROOT_DEVICES __table ( struct root_device, "root_devices" )
+
 /** Declare a root device */
-#define __root_device __table ( struct root_device, root_devices, 01 )
+#define __root_device __table_entry ( ROOT_DEVICES, 01 )
 
 #endif /* _GPXE_DEVICE_H */
diff --git a/gpxe/src/include/gpxe/dhcp.h b/gpxe/src/include/gpxe/dhcp.h
index 33e0c5d..ebfe8ed 100644
--- a/gpxe/src/include/gpxe/dhcp.h
+++ b/gpxe/src/include/gpxe/dhcp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/in.h>
 #include <gpxe/list.h>
@@ -14,6 +16,7 @@
 #include <gpxe/tables.h>
 #include <gpxe/uuid.h>
 #include <gpxe/netdevice.h>
+#include <gpxe/uaccess.h>
 
 struct job_interface;
 struct dhcp_options;
@@ -330,6 +333,16 @@ struct dhcp_netdev_desc {
 	uint16_t device;
 } __attribute__ (( packed ));
 
+/** Use cached network settings
+ *
+ * Cached network settings may be available from a prior DHCP request
+ * (if running as a PXE NBP), non-volatile storage on the NIC, or
+ * settings set via the command line or an embedded image. If this
+ * flag is not set, it will be assumed that those sources are
+ * insufficient and that DHCP should still be run when autobooting.
+ */
+#define DHCP_EB_USE_CACHED DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb2 )
+
 /** BIOS drive number
  *
  * This is the drive number for a drive emulated via INT 13.  0x80 is
@@ -599,6 +612,8 @@ struct dhcphdr {
 /** Setting block name used for BootServerDHCP responses */
 #define PXEBS_SETTINGS_NAME "pxebs"
 
+extern void * dhcp_chaddr ( struct net_device *netdev, uint8_t *hlen,
+			    uint16_t *flags );
 extern int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
 				struct net_device *netdev, uint8_t msgtype,
 				const void *options, size_t options_len,
@@ -611,4 +626,12 @@ extern int start_dhcp ( struct job_interface *job, struct net_device *netdev );
 extern int start_pxebs ( struct job_interface *job, struct net_device *netdev,
 			 unsigned int pxe_type );
 
+/* In environments that can provide cached DHCP packets, this function
+ * should look for such a packet and call store_cached_dhcpack() with
+ * it if it exists.
+ */
+__weak_decl ( void, get_cached_dhcpack, ( void ), (), );
+
+extern void store_cached_dhcpack ( userptr_t data, size_t len );
+
 #endif /* _GPXE_DHCP_H */
diff --git a/gpxe/src/include/gpxe/dhcpopts.h b/gpxe/src/include/gpxe/dhcpopts.h
index 8391a9d..3d90f41 100644
--- a/gpxe/src/include/gpxe/dhcpopts.h
+++ b/gpxe/src/include/gpxe/dhcpopts.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /** A DHCP options block */
diff --git a/gpxe/src/include/gpxe/dhcppkt.h b/gpxe/src/include/gpxe/dhcppkt.h
index e8f8faf..6007cca 100644
--- a/gpxe/src/include/gpxe/dhcppkt.h
+++ b/gpxe/src/include/gpxe/dhcppkt.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/dhcp.h>
 #include <gpxe/dhcpopts.h>
 #include <gpxe/refcnt.h>
diff --git a/gpxe/src/include/gpxe/dns.h b/gpxe/src/include/gpxe/dns.h
index 3e3cff1..9e5e874 100644
--- a/gpxe/src/include/gpxe/dns.h
+++ b/gpxe/src/include/gpxe/dns.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/in.h>
 
diff --git a/gpxe/src/include/gpxe/downloader.h b/gpxe/src/include/gpxe/downloader.h
index 33aa769..7f21e07 100644
--- a/gpxe/src/include/gpxe/downloader.h
+++ b/gpxe/src/include/gpxe/downloader.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct job_interface;
 struct image;
 
diff --git a/gpxe/src/include/gpxe/eapol.h b/gpxe/src/include/gpxe/eapol.h
new file mode 100644
index 0000000..c9855d0
--- /dev/null
+++ b/gpxe/src/include/gpxe/eapol.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _GPXE_EAPOL_H
+#define _GPXE_EAPOL_H
+
+/** @file
+ *
+ * Definitions for EAPOL (Extensible Authentication Protocol over
+ * LANs) frames. Definitions for the packets usually encapsulated in
+ * them are elsewhere.
+ */
+
+#include <gpxe/tables.h>
+#include <stdint.h>
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+
+/**
+ * @defgroup eapol_type EAPOL archetype identifiers
+ * @{
+ */
+#define EAPOL_TYPE_EAP		0 /**< EAP authentication handshake packet */
+#define EAPOL_TYPE_START	1 /**< Request by Peer to begin (no data) */
+#define EAPOL_TYPE_LOGOFF	2 /**< Request by Peer to terminate (no data) */
+#define EAPOL_TYPE_KEY		3 /**< EAPOL-Key packet */
+/** @} */
+
+/** Expected EAPOL version field value
+ *
+ * Version 2 is often seen and has no format differences from version 1;
+ * however, many older APs will completely drop version-2 packets, so
+ * we advertise ourselves as version 1.
+ */
+#define EAPOL_THIS_VERSION	1
+
+/** Length of an EAPOL frame header */
+#define EAPOL_HDR_LEN		4
+
+/** An EAPOL frame
+ *
+ * This may encapsulate an eap_pkt, an eapol_key_pkt, or a Start or
+ * Logoff request with no data attached. It is transmitted directly in
+ * an Ethernet frame, with no IP packet header.
+ */
+struct eapol_frame
+{
+	/** EAPOL version identifier, always 1 */
+	u8 version;
+
+	/** EAPOL archetype identifier indicating format of payload */
+	u8 type;
+
+	/** Length of payload, in network byte order */
+	u16 length;
+
+	/** Payload, if @a type is EAP or EAPOL-Key */
+	u8 data[0];
+} __attribute__ (( packed ));
+
+
+/** An EAPOL frame type handler
+ *
+ * Normally there will be at most two of these, one for EAP and one
+ * for EAPOL-Key frames. The EAPOL interface code handles Start and
+ * Logoff directly.
+ */
+struct eapol_handler
+{
+	/** EAPOL archetype identifier for payload this handler will handle */
+	u8 type;
+
+	/** Receive EAPOL-encapsulated packet of specified type
+	 *
+	 * @v iob	I/O buffer containing packet payload
+	 * @v netdev	Network device from which packet was received
+	 * @v ll_source	Source link-layer address from which packet was received
+	 * @ret rc	Return status code
+	 *
+	 * The I/O buffer will have the EAPOL header pulled off it, so
+	 * @c iob->data points to the first byte of the payload.
+	 *
+	 * This function takes ownership of the I/O buffer passed to it.
+	 */
+	int ( * rx ) ( struct io_buffer *iob, struct net_device *netdev,
+		       const void *ll_source );
+};
+
+#define EAPOL_HANDLERS	__table ( struct eapol_handler, "eapol_handlers" )
+#define __eapol_handler	__table_entry ( EAPOL_HANDLERS, 01 )
+
+
+extern struct net_protocol eapol_protocol __net_protocol;
+
+
+#endif /* _GPXE_EAPOL_H */
diff --git a/gpxe/src/include/gpxe/editbox.h b/gpxe/src/include/gpxe/editbox.h
index 007b042..b7cc411 100644
--- a/gpxe/src/include/gpxe/editbox.h
+++ b/gpxe/src/include/gpxe/editbox.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <curses.h>
 #include <gpxe/editstring.h>
 
diff --git a/gpxe/src/include/gpxe/editstring.h b/gpxe/src/include/gpxe/editstring.h
index fad8bd5..48c1baa 100644
--- a/gpxe/src/include/gpxe/editstring.h
+++ b/gpxe/src/include/gpxe/editstring.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** An editable string */
 struct edit_string {
 	/** Buffer for string */
diff --git a/gpxe/src/include/gpxe/efi/efi.h b/gpxe/src/include/gpxe/efi/efi.h
index c7f63b6..f4459b7 100644
--- a/gpxe/src/include/gpxe/efi/efi.h
+++ b/gpxe/src/include/gpxe/efi/efi.h
@@ -31,11 +31,17 @@
 /* EFI headers rudely redefine NULL */
 #undef NULL
 
+/* EFI headers expect ICC to define __GNUC__ */
+#if defined ( __ICC ) && ! defined ( __GNUC__ )
+#define __GNUC__ 1
+#endif
+
 /* Include the top-level EFI header files */
 #include <gpxe/efi/Uefi.h>
 #include <gpxe/efi/PiDxe.h>
 
 /* Reset any trailing #pragma pack directives */
+#pragma pack(1)
 #pragma pack()
 
 #include <gpxe/tables.h>
@@ -54,9 +60,11 @@ struct efi_protocol {
 	void **protocol;
 };
 
+/** EFI protocol table */
+#define EFI_PROTOCOLS __table ( struct efi_protocol, "efi_protocols" )
+
 /** Declare an EFI protocol used by gPXE */
-#define __efi_protocol \
-	__table ( struct efi_protocol, efi_protocols, 01 )
+#define __efi_protocol __table_entry ( EFI_PROTOCOLS, 01 )
 
 /** Declare an EFI protocol to be required by gPXE
  *
@@ -67,7 +75,7 @@ struct efi_protocol {
 	struct efi_protocol __ ## _protocol __efi_protocol = {		     \
 		.u.guid = _protocol ## _GUID,				     \
 		.protocol = ( ( void ** ) ( void * )			     \
-			      ( ( (_ptr) == ( ( _protocol ** ) NULL ) ) ?    \
+			      ( ( (_ptr) == ( ( _protocol ** ) (_ptr) ) ) ?  \
 				(_ptr) : (_ptr) ) ),			     \
 	}
 
@@ -86,9 +94,12 @@ struct efi_config_table {
 	int required;
 };
 
+/** EFI configuration table table */
+#define EFI_CONFIG_TABLES \
+	__table ( struct efi_config_table, "efi_config_tables" )
+
 /** Declare an EFI configuration table used by gPXE */
-#define __efi_config_table \
-	__table ( struct efi_config_table, efi_config_tables, 01 )
+#define __efi_config_table __table_entry ( EFI_CONFIG_TABLES, 01 )
 
 /** Declare an EFI configuration table to be used by gPXE
  *
diff --git a/gpxe/src/include/gpxe/efi/efi_io.h b/gpxe/src/include/gpxe/efi/efi_io.h
index 93f559d..7ad5ffe 100644
--- a/gpxe/src/include/gpxe/efi/efi_io.h
+++ b/gpxe/src/include/gpxe/efi/efi_io.h
@@ -10,6 +10,8 @@
  * no-ops.  I/O is handled using the EFI_CPU_IO_PROTOCOL.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef IOAPI_EFI
 #define IOAPI_PREFIX_efi
 #else
diff --git a/gpxe/src/include/gpxe/efi/efi_pci.h b/gpxe/src/include/gpxe/efi/efi_pci.h
index 8be331a..59c0eb1 100644
--- a/gpxe/src/include/gpxe/efi/efi_pci.h
+++ b/gpxe/src/include/gpxe/efi/efi_pci.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef PCIAPI_EFI
 #define PCIAPI_PREFIX_efi
 #else
diff --git a/gpxe/src/include/gpxe/efi/efi_smbios.h b/gpxe/src/include/gpxe/efi/efi_smbios.h
index df947de..01631e5 100644
--- a/gpxe/src/include/gpxe/efi/efi_smbios.h
+++ b/gpxe/src/include/gpxe/efi/efi_smbios.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef SMBIOS_EFI
 #define SMBIOS_PREFIX_efi
 #else
diff --git a/gpxe/src/include/gpxe/efi/efi_timer.h b/gpxe/src/include/gpxe/efi/efi_timer.h
index c332c9d..e0531d5 100644
--- a/gpxe/src/include/gpxe/efi/efi_timer.h
+++ b/gpxe/src/include/gpxe/efi/efi_timer.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef TIMER_EFI
 #define TIMER_PREFIX_efi
 #else
diff --git a/gpxe/src/include/gpxe/efi/efi_uaccess.h b/gpxe/src/include/gpxe/efi/efi_uaccess.h
index bae5fb4..a6b61c5 100644
--- a/gpxe/src/include/gpxe/efi/efi_uaccess.h
+++ b/gpxe/src/include/gpxe/efi/efi_uaccess.h
@@ -10,6 +10,8 @@
  * no-ops.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef UACCESS_EFI
 #define UACCESS_PREFIX_efi
 #else
diff --git a/gpxe/src/include/gpxe/efi/efi_umalloc.h b/gpxe/src/include/gpxe/efi/efi_umalloc.h
index def17b2..0388dd1 100644
--- a/gpxe/src/include/gpxe/efi/efi_umalloc.h
+++ b/gpxe/src/include/gpxe/efi/efi_umalloc.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef UMALLOC_EFI
 #define UMALLOC_PREFIX_efi
 #else
diff --git a/gpxe/src/include/gpxe/eisa.h b/gpxe/src/include/gpxe/eisa.h
index e9d890e..f76e4b9 100644
--- a/gpxe/src/include/gpxe/eisa.h
+++ b/gpxe/src/include/gpxe/eisa.h
@@ -1,6 +1,8 @@
 #ifndef EISA_H
 #define EISA_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/isa_ids.h>
 #include <gpxe/device.h>
@@ -79,8 +81,11 @@ struct eisa_driver {
 	void ( * remove ) ( struct eisa_device *eisa );
 };
 
+/** EISA driver table */
+#define EISA_DRIVERS __table ( struct eisa_driver, "eisa_drivers" )
+
 /** Declare an EISA driver */
-#define __eisa_driver __table ( struct eisa_driver, eisa_drivers, 01 )
+#define __eisa_driver __table_entry ( EISA_DRIVERS, 01 )
 
 extern void eisa_device_enabled ( struct eisa_device *eisa, int enabled );
 
diff --git a/gpxe/src/include/gpxe/elf.h b/gpxe/src/include/gpxe/elf.h
index db28a60..da9d2fc 100644
--- a/gpxe/src/include/gpxe/elf.h
+++ b/gpxe/src/include/gpxe/elf.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <elf.h>
 
 extern int elf_load ( struct image *image );
diff --git a/gpxe/src/include/gpxe/errfile.h b/gpxe/src/include/gpxe/errfile.h
index dcfd4e4..def8f35 100644
--- a/gpxe/src/include/gpxe/errfile.h
+++ b/gpxe/src/include/gpxe/errfile.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <bits/errfile.h>
 
 /**
@@ -108,11 +110,20 @@
 #define ERRFILE_phantom		     ( ERRFILE_DRIVER | 0x004b0000 )
 #define ERRFILE_ne2k_isa	     ( ERRFILE_DRIVER | 0x004c0000 )
 #define ERRFILE_b44		     ( ERRFILE_DRIVER | 0x004d0000 )
+#define ERRFILE_rtl818x		     ( ERRFILE_DRIVER | 0x004e0000 )
+#define ERRFILE_sky2                 ( ERRFILE_DRIVER | 0x004f0000 )
+#define ERRFILE_ath5k		     ( ERRFILE_DRIVER | 0x00500000 )
+#define ERRFILE_atl1e		     ( ERRFILE_DRIVER | 0x00510000 )
+#define ERRFILE_sis190		     ( ERRFILE_DRIVER | 0x00520000 )
+#define ERRFILE_myri10ge	     ( ERRFILE_DRIVER | 0x00530000 )
+#define ERRFILE_skge		     ( ERRFILE_DRIVER | 0x00540000 )
 
 #define ERRFILE_scsi		     ( ERRFILE_DRIVER | 0x00700000 )
 #define ERRFILE_arbel		     ( ERRFILE_DRIVER | 0x00710000 )
 #define ERRFILE_hermon		     ( ERRFILE_DRIVER | 0x00720000 )
 #define ERRFILE_linda		     ( ERRFILE_DRIVER | 0x00730000 )
+#define ERRFILE_ata		     ( ERRFILE_DRIVER | 0x00740000 )
+#define ERRFILE_srp		     ( ERRFILE_DRIVER | 0x00750000 )
 
 #define ERRFILE_aoe			( ERRFILE_NET | 0x00000000 )
 #define ERRFILE_arp			( ERRFILE_NET | 0x00010000 )
@@ -140,6 +151,22 @@
 #define ERRFILE_ib_sma			( ERRFILE_NET | 0x00170000 )
 #define ERRFILE_ib_packet		( ERRFILE_NET | 0x00180000 )
 #define ERRFILE_icmp			( ERRFILE_NET | 0x00190000 )
+#define ERRFILE_ib_qset			( ERRFILE_NET | 0x001a0000 )
+#define ERRFILE_ib_gma			( ERRFILE_NET | 0x001b0000 )
+#define ERRFILE_ib_pathrec		( ERRFILE_NET | 0x001c0000 )
+#define ERRFILE_ib_mcast		( ERRFILE_NET | 0x001d0000 )
+#define ERRFILE_ib_cm			( ERRFILE_NET | 0x001e0000 )
+#define ERRFILE_net80211		( ERRFILE_NET | 0x001f0000 )
+#define ERRFILE_ib_mi			( ERRFILE_NET | 0x00200000 )
+#define ERRFILE_ib_cmrc			( ERRFILE_NET | 0x00210000 )
+#define ERRFILE_ib_srp			( ERRFILE_NET | 0x00220000 )
+#define ERRFILE_sec80211		( ERRFILE_NET | 0x00230000 )
+#define ERRFILE_wep			( ERRFILE_NET | 0x00240000 )
+#define ERRFILE_eapol			( ERRFILE_NET | 0x00250000 )
+#define ERRFILE_wpa			( ERRFILE_NET | 0x00260000 )
+#define ERRFILE_wpa_psk			( ERRFILE_NET | 0x00270000 )
+#define ERRFILE_wpa_tkip		( ERRFILE_NET | 0x00280000 )
+#define ERRFILE_wpa_ccmp		( ERRFILE_NET | 0x00290000 )
 
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )
@@ -172,6 +199,8 @@
 #define ERRFILE_pxemenu		      ( ERRFILE_OTHER | 0x00150000 )
 #define ERRFILE_x509		      ( ERRFILE_OTHER | 0x00160000 )
 #define ERRFILE_login_ui	      ( ERRFILE_OTHER | 0x00170000 )
+#define ERRFILE_ib_srpboot	      ( ERRFILE_OTHER | 0x00180000 )
+#define ERRFILE_iwmgmt		      ( ERRFILE_OTHER | 0x00190000 )
 
 /** @} */
 
diff --git a/gpxe/src/include/gpxe/errortab.h b/gpxe/src/include/gpxe/errortab.h
index e9a5676..35765d4 100644
--- a/gpxe/src/include/gpxe/errortab.h
+++ b/gpxe/src/include/gpxe/errortab.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/tables.h>
 
 struct errortab {
@@ -14,6 +16,8 @@ struct errortab {
 	const char *text;
 };
 
-#define __errortab __table ( struct errortab, errortab, 01 )
+#define ERRORTAB __table ( struct errortab, "errortab" )
+
+#define __errortab __table_entry ( ERRORTAB, 01 )
 
 #endif /* _GPXE_ERRORTAB_H */
diff --git a/gpxe/src/include/gpxe/ethernet.h b/gpxe/src/include/gpxe/ethernet.h
index ff0fd6c..4dfc24d 100644
--- a/gpxe/src/include/gpxe/ethernet.h
+++ b/gpxe/src/include/gpxe/ethernet.h
@@ -7,29 +7,15 @@
  *
  */
 
-#include <stdint.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/if_ether.h>
+FILE_LICENCE ( GPL2_OR_LATER );
 
-extern struct ll_protocol ethernet_protocol;
+#include <stdint.h>
 
+extern void eth_init_addr ( const void *hw_addr, void *ll_addr );
 extern const char * eth_ntoa ( const void *ll_addr );
-
-/**
- * Allocate Ethernet device
- *
- * @v priv_size		Size of driver private data
- * @ret netdev		Network device, or NULL
- */
-static inline struct net_device * alloc_etherdev ( size_t priv_size ) {
-	struct net_device *netdev;
-
-	netdev = alloc_netdev ( priv_size );
-	if ( netdev ) {
-		netdev->ll_protocol = &ethernet_protocol;
-		netdev->max_pkt_len = ETH_FRAME_LEN;
-	}
-	return netdev;
-}
+extern int eth_mc_hash ( unsigned int af, const void *net_addr,
+			 void *ll_addr );
+extern int eth_eth_addr ( const void *ll_addr, void *eth_addr );
+extern struct net_device * alloc_etherdev ( size_t priv_size );
 
 #endif /* _GPXE_ETHERNET_H */
diff --git a/gpxe/src/include/gpxe/fakedhcp.h b/gpxe/src/include/gpxe/fakedhcp.h
index 550b74f..c603bdc 100644
--- a/gpxe/src/include/gpxe/fakedhcp.h
+++ b/gpxe/src/include/gpxe/fakedhcp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 struct net_device;
diff --git a/gpxe/src/include/gpxe/features.h b/gpxe/src/include/gpxe/features.h
index 32c3169..3443186 100644
--- a/gpxe/src/include/gpxe/features.h
+++ b/gpxe/src/include/gpxe/features.h
@@ -11,6 +11,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @defgroup featurecat Feature categories
  * @{
@@ -42,6 +44,7 @@
 #define DHCP_EB_FEATURE_BZIMAGE		0x18 /**< bzImage format */
 #define DHCP_EB_FEATURE_MULTIBOOT	0x19 /**< Multiboot format */
 #define DHCP_EB_FEATURE_SLAM		0x1a /**< SLAM protocol */
+#define DHCP_EB_FEATURE_SRP		0x1b /**< SRP protocol */
 #define DHCP_EB_FEATURE_NBI		0x20 /**< NBI format */
 #define DHCP_EB_FEATURE_PXE		0x21 /**< PXE format */
 #define DHCP_EB_FEATURE_ELF		0x22 /**< ELF format */
@@ -50,8 +53,11 @@
 
 /** @} */
 
+/** DHCP feature table */
+#define DHCP_FEATURES __table ( uint8_t, "dhcp_features" )
+
 /** Declare a feature code for DHCP */
-#define __dhcp_feature __table ( uint8_t, dhcp_features, 01 )
+#define __dhcp_feature __table_entry ( DHCP_FEATURES, 01 )
 
 /** Construct a DHCP feature table entry */
 #define DHCP_FEATURE( feature_opt, ... )				    \
@@ -69,9 +75,11 @@ struct feature {
 	char *name;
 };
 
+/** Named feature table */
+#define FEATURES __table ( struct feature, "features" )
+
 /** Declare a named feature */
-#define __feature_name( category )					    \
-	__table ( struct feature, features, category )
+#define __feature_name( category ) __table_entry ( FEATURES, category )
 
 /** Construct a named feature */
 #define FEATURE_NAME( category, text )					    \
diff --git a/gpxe/src/include/gpxe/filter.h b/gpxe/src/include/gpxe/filter.h
index 126f634..1f59fcc 100644
--- a/gpxe/src/include/gpxe/filter.h
+++ b/gpxe/src/include/gpxe/filter.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <gpxe/xfer.h>
 
diff --git a/gpxe/src/include/gpxe/ftp.h b/gpxe/src/include/gpxe/ftp.h
index 370285c..93194f6 100644
--- a/gpxe/src/include/gpxe/ftp.h
+++ b/gpxe/src/include/gpxe/ftp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** FTP default port */
 #define FTP_PORT 21
 
diff --git a/gpxe/src/include/gpxe/gdbserial.h b/gpxe/src/include/gpxe/gdbserial.h
index 1863e90..2613ab4 100644
--- a/gpxe/src/include/gpxe/gdbserial.h
+++ b/gpxe/src/include/gpxe/gdbserial.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct gdb_transport;
 
 /**
diff --git a/gpxe/src/include/gpxe/gdbstub.h b/gpxe/src/include/gpxe/gdbstub.h
index bf5d24d..8f9b7c1 100644
--- a/gpxe/src/include/gpxe/gdbstub.h
+++ b/gpxe/src/include/gpxe/gdbstub.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/tables.h>
 #include <gdbmach.h>
@@ -45,7 +47,9 @@ struct gdb_transport {
 	void ( * send ) ( const char *buf, size_t len );
 };
 
-#define __gdb_transport __table ( struct gdb_transport, gdb_transports, 01 )
+#define GDB_TRANSPORTS __table ( struct gdb_transport, "gdb_transports" )
+
+#define __gdb_transport __table_entry ( GDB_TRANSPORTS, 01 )
 
 /**
  * Look up GDB transport by name
diff --git a/gpxe/src/include/gpxe/gdbudp.h b/gpxe/src/include/gpxe/gdbudp.h
index 1a99093..5f02faa 100644
--- a/gpxe/src/include/gpxe/gdbudp.h
+++ b/gpxe/src/include/gpxe/gdbudp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct sockaddr_in;
 struct gdb_transport;
 
diff --git a/gpxe/src/include/gpxe/hidemem.h b/gpxe/src/include/gpxe/hidemem.h
index 010fdb5..01b3fc2 100644
--- a/gpxe/src/include/gpxe/hidemem.h
+++ b/gpxe/src/include/gpxe/hidemem.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 extern void hide_umalloc ( physaddr_t start, physaddr_t end );
diff --git a/gpxe/src/include/gpxe/hmac.h b/gpxe/src/include/gpxe/hmac.h
index 67aefdc..cb001b9 100644
--- a/gpxe/src/include/gpxe/hmac.h
+++ b/gpxe/src/include/gpxe/hmac.h
@@ -6,6 +6,8 @@
  * Keyed-Hashing for Message Authentication
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/crypto.h>
 
 /**
diff --git a/gpxe/src/include/gpxe/http.h b/gpxe/src/include/gpxe/http.h
index fa92a95..baa6d83 100644
--- a/gpxe/src/include/gpxe/http.h
+++ b/gpxe/src/include/gpxe/http.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** HTTP default port */
 #define HTTP_PORT 80
 
diff --git a/gpxe/src/include/gpxe/i2c.h b/gpxe/src/include/gpxe/i2c.h
index 9d22954..87b89d4 100644
--- a/gpxe/src/include/gpxe/i2c.h
+++ b/gpxe/src/include/gpxe/i2c.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/bitbash.h>
 
diff --git a/gpxe/src/include/gpxe/ib_cm.h b/gpxe/src/include/gpxe/ib_cm.h
new file mode 100644
index 0000000..670fffd
--- /dev/null
+++ b/gpxe/src/include/gpxe/ib_cm.h
@@ -0,0 +1,72 @@
+#ifndef _GPXE_IB_CM_H
+#define _GPXE_IB_CM_H
+
+/** @file
+ *
+ * Infiniband communication management
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/infiniband.h>
+#include <gpxe/retry.h>
+
+struct ib_mad_transaction;
+struct ib_connection;
+
+/** Infiniband connection operations */
+struct ib_connection_operations {
+	/** Handle change of connection status
+	 *
+	 * @v ibdev		Infiniband device
+	 * @v qp		Queue pair
+	 * @v conn		Connection
+	 * @v rc		Connection status code
+	 * @v private_data	Private data, if available
+	 * @v private_data_len	Length of private data
+	 */
+	void ( * changed ) ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+			     struct ib_connection *conn, int rc,
+			     void *private_data, size_t private_data_len );
+};
+
+/** An Infiniband connection */
+struct ib_connection {
+	/** Infiniband device */
+	struct ib_device *ibdev;
+	/** Queue pair */
+	struct ib_queue_pair *qp;
+	/** Local communication ID */
+	uint32_t local_id;
+	/** Remote communication ID */
+	uint32_t remote_id;
+	/** Target service ID */
+	struct ib_gid_half service_id;
+	/** Connection operations */
+	struct ib_connection_operations *op;
+
+	/** List of connections */
+	struct list_head list;
+
+	/** Path to target */
+	struct ib_path *path;
+	/** Connection request management transaction */
+	struct ib_mad_transaction *madx;
+
+	/** Length of connection request private data */
+	size_t private_data_len;
+	/** Connection request private data */
+	uint8_t private_data[0];
+};
+
+extern struct ib_connection *
+ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+		 struct ib_gid *dgid, struct ib_gid_half *service_id,
+		 void *req_private_data, size_t req_private_data_len,
+		 struct ib_connection_operations *op );
+extern void ib_destroy_conn ( struct ib_device *ibdev,
+			      struct ib_queue_pair *qp,
+			      struct ib_connection *conn );
+
+#endif /* _GPXE_IB_CM_H */
diff --git a/gpxe/src/include/gpxe/ib_cmrc.h b/gpxe/src/include/gpxe/ib_cmrc.h
new file mode 100644
index 0000000..efa741a
--- /dev/null
+++ b/gpxe/src/include/gpxe/ib_cmrc.h
@@ -0,0 +1,20 @@
+#ifndef _GPXE_IB_CMRC_H
+#define _GPXE_IB_CMRC_H
+
+/** @file
+ *
+ * Infiniband Communication-managed Reliable Connections
+ *
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <gpxe/infiniband.h>
+#include <gpxe/xfer.h>
+
+extern int ib_cmrc_open ( struct xfer_interface *xfer,
+			  struct ib_device *ibdev,
+			  struct ib_gid *dgid,
+			  struct ib_gid_half *service_id );
+
+#endif /* _GPXE_IB_CMRC_H */
diff --git a/gpxe/src/include/gpxe/ib_mad.h b/gpxe/src/include/gpxe/ib_mad.h
index 6c4e95b..8b49718 100644
--- a/gpxe/src/include/gpxe/ib_mad.h
+++ b/gpxe/src/include/gpxe/ib_mad.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/ib_packet.h>
 
@@ -73,9 +75,9 @@ struct ib_node_info {
 	uint8_t class_version;
 	uint8_t node_type;
 	uint8_t num_ports;
-	uint8_t sys_guid[8];
-	uint8_t node_guid[8];
-	uint8_t port_guid[8];
+	struct ib_gid_half sys_guid;
+	struct ib_gid_half node_guid;
+	struct ib_gid_half port_guid;
 	uint16_t partition_cap;
 	uint16_t device_id;
 	uint32_t revision;
@@ -199,6 +201,10 @@ struct ib_smp_class_specific {
  *****************************************************************************
  */
 
+#define IB_SA_CLASS_VERSION			2
+
+#define IB_SA_METHOD_DELETE_RESP		0x95
+
 struct ib_rmpp_hdr {
 	uint32_t raw[3];
 } __attribute__ (( packed ));
@@ -275,6 +281,188 @@ union ib_sa_data {
 
 /*****************************************************************************
  *
+ * Communication management MADs
+ *
+ *****************************************************************************
+ */
+
+/** Communication management class version */
+#define IB_CM_CLASS_VERSION			2
+
+/* Communication management attributes */
+#define IB_CM_ATTR_CLASS_PORT_INFO		0x0001
+#define IB_CM_ATTR_CONNECT_REQUEST		0x0010
+#define IB_CM_ATTR_MSG_RCPT_ACK			0x0011
+#define IB_CM_ATTR_CONNECT_REJECT		0x0012
+#define IB_CM_ATTR_CONNECT_REPLY		0x0013
+#define IB_CM_ATTR_READY_TO_USE			0x0014
+#define IB_CM_ATTR_DISCONNECT_REQUEST		0x0015
+#define IB_CM_ATTR_DISCONNECT_REPLY		0x0016
+#define IB_CM_ATTR_SERVICE_ID_RES_REQ		0x0016
+#define IB_CM_ATTR_SERVICE_ID_RES_REQ_RESP	0x0018
+#define IB_CM_ATTR_LOAD_ALTERNATE_PATH		0x0019
+#define IB_CM_ATTR_ALTERNATE_PATH_RESPONSE	0x001a
+
+/** Communication management common fields */
+struct ib_cm_common {
+	/** Local communication ID */
+	uint32_t local_id;
+	/** Remote communication ID */
+	uint32_t remote_id;
+	/** Reserved */
+	uint8_t reserved[224];
+} __attribute__ (( packed ));
+
+/** A communication management path */
+struct ib_cm_path {
+	/** Local port LID */
+	uint16_t local_lid;
+	/** Remote port LID */
+	uint16_t remote_lid;
+	/** Local port GID */
+	struct ib_gid local_gid;
+	/** Remote port GID */
+	struct ib_gid remote_gid;
+	/** Flow label and rate */
+	uint32_t flow_label__rate;
+	/** Traffic class */
+	uint8_t tc;
+	/** Hop limit */
+	uint8_t hop_limit;
+	/** SL and subnet local*/
+	uint8_t sl__subnet_local;
+	/** Local ACK timeout */
+	uint8_t local_ack_timeout;
+} __attribute__ (( packed ));
+
+/** A communication management connection request
+ *
+ * Defined in section 12.6.5 of the IBA.
+ */
+struct ib_cm_connect_request {
+	/** Local communication ID */
+	uint32_t local_id;
+	/** Reserved */
+	uint32_t reserved0[1];
+	/** Service ID */
+	struct ib_gid_half service_id;
+	/** Local CA GUID */
+	struct ib_gid_half local_ca;
+	/** Reserved */
+	uint32_t reserved1[1];
+	/** Local queue key */
+	uint32_t local_qkey;
+	/** Local QPN and responder resources*/
+	uint32_t local_qpn__responder_resources;
+	/** Local EECN and initiator depth */
+	uint32_t local_eecn__initiator_depth;
+	/** Remote EECN, remote CM response timeout, transport service
+	 * type, EE flow control
+	 */
+	uint32_t remote_eecn__remote_timeout__service_type__ee_flow_ctrl;
+	/** Starting PSN, local CM response timeout and retry count */
+	uint32_t starting_psn__local_timeout__retry_count;
+	/** Partition key */
+	uint16_t pkey;
+	/** Path packet payload MTU, RDC exists, RNR retry count */
+	uint8_t payload_mtu__rdc_exists__rnr_retry;
+	/** Max CM retries and SRQ */
+	uint8_t max_cm_retries__srq;
+	/** Primary path */
+	struct ib_cm_path primary;
+	/** Alternate path */
+	struct ib_cm_path alternate;
+	/** Private data */
+	uint8_t private_data[92];
+} __attribute__ (( packed ));
+
+/** CM transport types */
+#define IB_CM_TRANSPORT_RC		0
+#define IB_CM_TRANSPORT_UC		1
+#define IB_CM_TRANSPORT_RD		2
+
+/** A communication management connection rejection
+ *
+ * Defined in section 12.6.7 of the IBA.
+ */
+struct ib_cm_connect_reject {
+	/** Local communication ID */
+	uint32_t local_id;
+	/** Remote communication ID */
+	uint32_t remote_id;
+	/** Message rejected */
+	uint8_t message;
+	/** Reject information length */
+	uint8_t info_len;
+	/** Rejection reason */
+	uint16_t reason;
+	/** Additional rejection information */
+	uint8_t info[72];
+	/** Private data */
+	uint8_t private_data[148];
+} __attribute__ (( packed ));
+
+/** CM rejection reasons */
+#define IB_CM_REJECT_BAD_SERVICE_ID	8
+#define IB_CM_REJECT_STALE_CONN		10
+#define IB_CM_REJECT_CONSUMER		28
+
+/** A communication management connection reply
+ *
+ * Defined in section 12.6.8 of the IBA.
+ */
+struct ib_cm_connect_reply {
+	/** Local communication ID */
+	uint32_t local_id;
+	/** Remote communication ID */
+	uint32_t remote_id;
+	/** Local queue key */
+	uint32_t local_qkey;
+	/** Local QPN */
+	uint32_t local_qpn;
+	/** Local EECN */
+	uint32_t local_eecn;
+	/** Starting PSN */
+	uint32_t starting_psn;
+	/** Responder resources */
+	uint8_t responder_resources;
+	/** Initiator depth */
+	uint8_t initiator_depth;
+	/** Target ACK delay, failover accepted, and end-to-end flow control */
+	uint8_t target_ack_delay__failover_accepted__ee_flow_ctrl;
+	/** RNR retry count, SRQ */
+	uint8_t rnr_retry__srq;
+	/** Local CA GUID */
+	struct ib_gid_half local_ca;
+	/** Private data */
+	uint8_t private_data[196];
+} __attribute__ (( packed ));
+
+/** A communication management ready to use reply
+ *
+ * Defined in section 12.6.9 of the IBA.
+ */
+struct ib_cm_ready_to_use {
+	/** Local communication ID */
+	uint32_t local_id;
+	/** Remote communication ID */
+	uint32_t remote_id;
+	/** Private data */
+	uint8_t private_data[224];
+} __attribute__ (( packed ));
+
+/** A communication management attribute */
+union ib_cm_data {
+	struct ib_cm_common common;
+	struct ib_cm_connect_request connect_request;
+	struct ib_cm_connect_reject connect_reject;
+	struct ib_cm_connect_reply connect_reply;
+	struct ib_cm_ready_to_use ready_to_use;
+	uint8_t bytes[232];
+} __attribute__ (( packed ));
+
+/*****************************************************************************
+ *
  * MADs
  *
  *****************************************************************************
@@ -316,7 +504,9 @@ struct ib_mad_hdr {
 #define IB_MGMT_CLASS_CM			0x07
 #define IB_MGMT_CLASS_SNMP			0x08
 #define IB_MGMT_CLASS_VENDOR_RANGE2_START	0x30
-#define IB_MGMT_CLASS_VENDOR_RANGE2_END		0x4F
+#define IB_MGMT_CLASS_VENDOR_RANGE2_END		0x4f
+
+#define IB_MGMT_CLASS_MASK			0x7f
 
 /* Management methods */
 #define IB_MGMT_METHOD_GET			0x01
@@ -353,11 +543,18 @@ struct ib_mad_sa {
 	union ib_sa_data sa_data;
 } __attribute__ (( packed ));
 
+/** A communication management MAD */
+struct ib_mad_cm {
+	struct ib_mad_hdr mad_hdr;
+	union ib_cm_data cm_data;
+} __attribute__ (( packed ));
+
 /** A management datagram */
 union ib_mad {
 	struct ib_mad_hdr hdr;
 	struct ib_mad_smp smp;
 	struct ib_mad_sa sa;
+	struct ib_mad_cm cm;
 	uint8_t bytes[256];
 } __attribute__ (( packed ));
 
diff --git a/gpxe/src/include/gpxe/ib_mcast.h b/gpxe/src/include/gpxe/ib_mcast.h
new file mode 100644
index 0000000..74eccd0
--- /dev/null
+++ b/gpxe/src/include/gpxe/ib_mcast.h
@@ -0,0 +1,48 @@
+#ifndef _GPXE_IB_MCAST_H
+#define _GPXE_IB_MCAST_H
+
+/** @file
+ *
+ * Infiniband multicast groups
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/infiniband.h>
+
+struct ib_mad_transaction;
+
+/** An Infiniband multicast group membership */
+struct ib_mc_membership {
+	/** Queue pair */
+	struct ib_queue_pair *qp;
+	/** Multicast GID */
+	struct ib_gid gid;
+	/** Multicast group join transaction */
+	struct ib_mad_transaction *madx;
+	/** Handle join success/failure
+	 *
+	 * @v ibdev		Infiniband device
+	 * @v qp		Queue pair
+	 * @v membership	Multicast group membership
+	 * @v rc		Status code
+	 * @v mad		Response MAD (or NULL on error)
+	 */
+	void ( * complete ) ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+			      struct ib_mc_membership *membership, int rc,
+			      union ib_mad *mad );
+};
+
+extern int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+			   struct ib_mc_membership *membership,
+			   struct ib_gid *gid,
+			   void ( * joined ) ( struct ib_device *ibdev,
+					       struct ib_queue_pair *qp,
+					       struct ib_mc_membership *memb,
+					       int rc, union ib_mad *mad ) );
+
+extern void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+			     struct ib_mc_membership *membership );
+
+#endif /* _GPXE_IB_MCAST_H */
diff --git a/gpxe/src/include/gpxe/ib_mi.h b/gpxe/src/include/gpxe/ib_mi.h
new file mode 100644
index 0000000..b1cf686
--- /dev/null
+++ b/gpxe/src/include/gpxe/ib_mi.h
@@ -0,0 +1,135 @@
+#ifndef _GPXE_IB_MI_H
+#define _GPXE_IB_MI_H
+
+/** @file
+ *
+ * Infiniband management interfaces
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/list.h>
+#include <gpxe/retry.h>
+#include <gpxe/tables.h>
+#include <gpxe/infiniband.h>
+
+struct ib_mad_interface;
+struct ib_mad_transaction;
+
+/** An Infiniband management agent */
+struct ib_mad_agent {
+	/** Management class */
+	uint8_t mgmt_class;
+	/** Class version */
+	uint8_t class_version;
+	/** Attribute (in network byte order) */
+	uint16_t attr_id;
+	/** Handle MAD
+	 *
+	 * @v ibdev		Infiniband device
+	 * @v mi		Management interface
+	 * @v mad		Received MAD
+	 * @v av		Source address vector
+	 * @ret rc		Return status code
+	 */
+	void ( * handle ) ( struct ib_device *ibdev,
+			    struct ib_mad_interface *mi,
+			    union ib_mad *mad,
+			    struct ib_address_vector *av );
+};
+
+/** Infiniband management agents */
+#define IB_MAD_AGENTS __table ( struct ib_mad_agent, "ib_mad_agents" )
+
+/** Declare an Infiniband management agent */
+#define __ib_mad_agent __table_entry ( IB_MAD_AGENTS, 01 )
+
+/** Infiniband management transaction operations */
+struct ib_mad_transaction_operations {
+	/** Handle transaction completion
+	 *
+	 * @v ibdev		Infiniband device
+	 * @v mi		Management interface
+	 * @v madx		Management transaction
+	 * @v rc		Status code
+	 * @v mad		Received MAD (or NULL on error)
+	 * @v av		Source address vector (or NULL on error)
+	 *
+	 * The completion handler should in most cases call
+	 * ib_destroy_madx() to free up the completed transaction.
+	 */
+	void ( * complete ) ( struct ib_device *ibdev,
+			      struct ib_mad_interface *mi,
+			      struct ib_mad_transaction *madx,
+			      int rc, union ib_mad *mad,
+			      struct ib_address_vector *av );
+};
+
+/** An Infiniband management transaction */
+struct ib_mad_transaction {
+	/** Associated management interface */
+	struct ib_mad_interface *mi;
+	/** List of transactions */
+	struct list_head list;
+	/** Retry timer */
+	struct retry_timer timer;
+	/** Destination address vector */
+	struct ib_address_vector av;
+	/** MAD being sent */
+	union ib_mad mad;
+	/** Transaction operations */
+	struct ib_mad_transaction_operations *op;
+	/** Owner private data */
+	void *owner_priv;
+};
+
+/** An Infiniband management interface */
+struct ib_mad_interface {
+	/** Infiniband device */
+	struct ib_device *ibdev;
+	/** Completion queue */
+	struct ib_completion_queue *cq;
+	/** Queue pair */
+	struct ib_queue_pair *qp;
+	/** List of management transactions */
+	struct list_head madx;
+};
+
+/**
+ * Set Infiniband management transaction owner-private data
+ *
+ * @v madx		Management transaction
+ * @v priv		Private data
+ */
+static inline __always_inline void
+ib_madx_set_ownerdata ( struct ib_mad_transaction *madx, void *priv ) {
+	madx->owner_priv = priv;
+}
+
+/**
+ * Get Infiniband management transaction owner-private data
+ *
+ * @v madx		Management transaction
+ * @ret priv		Private data
+ */
+static inline __always_inline void *
+ib_madx_get_ownerdata ( struct ib_mad_transaction *madx ) {
+	return madx->owner_priv;
+}
+
+extern int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi,
+			union ib_mad *mad, struct ib_address_vector *av );
+extern struct ib_mad_transaction *
+ib_create_madx ( struct ib_device *ibdev, struct ib_mad_interface *mi,
+		 union ib_mad *mad, struct ib_address_vector *av,
+		 struct ib_mad_transaction_operations *op );
+extern void ib_destroy_madx ( struct ib_device *ibdev,
+			      struct ib_mad_interface *mi,
+			      struct ib_mad_transaction *madx );
+extern struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev,
+						enum ib_queue_pair_type type );
+extern void ib_destroy_mi ( struct ib_device *ibdev,
+			    struct ib_mad_interface *mi );
+
+#endif /* _GPXE_IB_MI_H */
diff --git a/gpxe/src/include/gpxe/ib_packet.h b/gpxe/src/include/gpxe/ib_packet.h
index 5374802..d468859 100644
--- a/gpxe/src/include/gpxe/ib_packet.h
+++ b/gpxe/src/include/gpxe/ib_packet.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct ib_device;
 struct ib_queue_pair;
 struct ib_address_vector;
@@ -14,7 +16,11 @@ struct io_buffer;
 
 /** Half of an Infiniband Global Identifier */
 struct ib_gid_half {
-	uint8_t bytes[8];
+	union {
+		uint8_t bytes[8];
+		uint16_t words[4];
+		uint32_t dwords[2];
+	} u;
 };
 
 /** An Infiniband Global Identifier */
@@ -104,12 +110,6 @@ enum ib_bth_opcode {
 	BTH_OPCODE_UD_SEND = 0x64,
 };
 
-/** Default Infiniband partition key */
-#define IB_PKEY_NONE 0xffff
-
-/** Subnet management queue pair number */
-#define IB_QPN_SMP 0
-
 /** An Infiniband Datagram Extended Transport Header */
 struct ib_datagram_extended_transport_header {
 	/** Queue key */
diff --git a/gpxe/src/include/gpxe/ib_pathrec.h b/gpxe/src/include/gpxe/ib_pathrec.h
new file mode 100644
index 0000000..5884d53
--- /dev/null
+++ b/gpxe/src/include/gpxe/ib_pathrec.h
@@ -0,0 +1,76 @@
+#ifndef _GPXE_IB_PATHREC_H
+#define _GPXE_IB_PATHREC_H
+
+/** @file
+ *
+ * Infiniband path records
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/infiniband.h>
+
+struct ib_mad_transaction;
+struct ib_path;
+
+/** Infiniband path operations */
+struct ib_path_operations {
+	/** Handle path transaction completion
+	 *
+	 * @v ibdev		Infiniband device
+	 * @v path		Path
+	 * @v rc		Status code
+	 * @v av		Address vector, or NULL on error
+	 */
+	void ( * complete ) ( struct ib_device *ibdev,
+			      struct ib_path *path, int rc,
+			      struct ib_address_vector *av );
+};
+
+/** An Infiniband path */
+struct ib_path {
+	/** Infiniband device */
+	struct ib_device *ibdev;
+	/** Address vector */
+	struct ib_address_vector av;
+	/** Management transaction */
+	struct ib_mad_transaction *madx;
+	/** Path operations */
+	struct ib_path_operations *op;
+	/** Owner private data */
+	void *owner_priv;
+};
+
+/**
+ * Set Infiniband path owner-private data
+ *
+ * @v path		Path
+ * @v priv		Private data
+ */
+static inline __always_inline void
+ib_path_set_ownerdata ( struct ib_path *path, void *priv ) {
+	path->owner_priv = priv;
+}
+
+/**
+ * Get Infiniband path owner-private data
+ *
+ * @v path		Path
+ * @ret priv		Private data
+ */
+static inline __always_inline void *
+ib_path_get_ownerdata ( struct ib_path *path ) {
+	return path->owner_priv;
+}
+
+extern struct ib_path *
+ib_create_path ( struct ib_device *ibdev, struct ib_address_vector *av,
+		 struct ib_path_operations *op );
+extern void ib_destroy_path ( struct ib_device *ibdev,
+			      struct ib_path *path );
+
+extern int ib_resolve_path ( struct ib_device *ibdev,
+			     struct ib_address_vector *av );
+
+#endif /* _GPXE_IB_PATHREC_H */
diff --git a/gpxe/src/include/gpxe/ib_sma.h b/gpxe/src/include/gpxe/ib_sma.h
index 835ed4e..78fc672 100644
--- a/gpxe/src/include/gpxe/ib_sma.h
+++ b/gpxe/src/include/gpxe/ib_sma.h
@@ -3,61 +3,18 @@
 
 /** @file
  *
- * Infiniband Subnet Management Agent
+ * Infiniband subnet management agent
  *
  */
 
-#include <gpxe/infiniband.h>
-#include <gpxe/process.h>
+FILE_LICENCE ( GPL2_OR_LATER );
 
-/** Infiniband Subnet Management Agent operations */
-struct ib_sma_operations {
-	/** Set port information
-	 *
-	 * @v ibdev		Infiniband device
-	 * @v port_info		New port information
-	 */
-	int ( * set_port_info ) ( struct ib_device *ibdev,
-				  const struct ib_port_info *port_info );
-};
+struct ib_device;
+struct ib_mad_interface;
 
-/** An Infiniband Subnet Management Agent */
-struct ib_sma {
-	/** Infiniband device */
-	struct ib_device *ibdev;
-	/** SMA operations */
-	struct ib_sma_operations *op;
-	/** SMA completion queue */
-	struct ib_completion_queue *cq;
-	/** SMA queue pair */
-	struct ib_queue_pair *qp;
-	/** Poll process */
-	struct process poll;
-};
-
-/** SMA payload size allocated for received packets */
-#define IB_SMA_PAYLOAD_LEN 2048
-
-/** SMA number of send WQEs
- *
- * This is a policy decision.
- */
-#define IB_SMA_NUM_SEND_WQES 4
-
-/** SMA number of receive WQEs
- *
- * This is a policy decision.
- */
-#define IB_SMA_NUM_RECV_WQES 2
-
-/** SMA number of completion queue entries
- *
- * This is a policy decision
- */
-#define IB_SMA_NUM_CQES 8
-
-extern int ib_create_sma ( struct ib_sma *sma, struct ib_device *ibdev,
-			   struct ib_sma_operations *op );
-extern void ib_destroy_sma ( struct ib_sma *sma );
+extern int ib_create_sma ( struct ib_device *ibdev,
+			   struct ib_mad_interface *mi );
+extern void ib_destroy_sma ( struct ib_device *ibdev,
+			     struct ib_mad_interface *mi );
 
 #endif /* _GPXE_IB_SMA_H */
diff --git a/gpxe/src/include/gpxe/ib_smc.h b/gpxe/src/include/gpxe/ib_smc.h
index bb9020b..fdd1c9c 100644
--- a/gpxe/src/include/gpxe/ib_smc.h
+++ b/gpxe/src/include/gpxe/ib_smc.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/infiniband.h>
 
 typedef int ( * ib_local_mad_t ) ( struct ib_device *ibdev,
diff --git a/gpxe/src/include/gpxe/ib_srp.h b/gpxe/src/include/gpxe/ib_srp.h
new file mode 100644
index 0000000..cf705b3
--- /dev/null
+++ b/gpxe/src/include/gpxe/ib_srp.h
@@ -0,0 +1,79 @@
+#ifndef _GPXE_IB_SRP_H
+#define _GPXE_IB_SRP_H
+
+/** @file
+ *
+ * SCSI RDMA Protocol over Infiniband
+ *
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdint.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/srp.h>
+
+/** SRP initiator port identifier for Infiniband */
+struct ib_srp_initiator_port_id {
+	/** Identifier extension */
+	struct ib_gid_half id_ext;
+	/** IB channel adapter GUID */
+	struct ib_gid_half hca_guid;
+} __attribute__ (( packed ));
+
+/** SRP target port identifier for Infiniband */
+struct ib_srp_target_port_id {
+	/** Identifier extension */
+	struct ib_gid_half id_ext;
+	/** I/O controller GUID */
+	struct ib_gid_half ioc_guid;
+} __attribute__ (( packed ));
+
+/**
+ * Get Infiniband-specific initiator port ID
+ *
+ * @v port_ids		SRP port IDs
+ * @ret initiator_port_id  Infiniband-specific initiator port ID
+ */
+static inline __always_inline struct ib_srp_initiator_port_id *
+ib_srp_initiator_port_id ( struct srp_port_ids *port_ids ) {
+	return ( ( struct ib_srp_initiator_port_id * ) &port_ids->initiator );
+}
+
+/**
+ * Get Infiniband-specific target port ID
+ *
+ * @v port_ids		SRP port IDs
+ * @ret target_port_id	Infiniband-specific target port ID
+ */
+static inline __always_inline struct ib_srp_target_port_id *
+ib_srp_target_port_id ( struct srp_port_ids *port_ids ) {
+	return ( ( struct ib_srp_target_port_id * ) &port_ids->target );
+}
+
+/** Infiniband-specific SRP parameters */
+struct ib_srp_parameters {
+	/** Source GID */
+	struct ib_gid sgid;
+	/** Destination GID */
+	struct ib_gid dgid;
+	/** Service ID */
+	struct ib_gid_half service_id;
+	/** Partition key */
+	uint16_t pkey;
+};
+
+/**
+ * Get Infiniband-specific transport parameters
+ *
+ * @v srp		SRP device
+ * @ret ib_params	Infiniband-specific transport parameters
+ */
+static inline __always_inline struct ib_srp_parameters *
+ib_srp_params ( struct srp_device *srp ) {
+	return srp_transport_priv ( srp );
+}
+
+extern struct srp_transport_type ib_srp_transport;
+
+#endif /* _GPXE_IB_SRP_H */
diff --git a/gpxe/src/include/gpxe/icmp.h b/gpxe/src/include/gpxe/icmp.h
index 49ade2f..bb8fce8 100644
--- a/gpxe/src/include/gpxe/icmp.h
+++ b/gpxe/src/include/gpxe/icmp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** An ICMP header */
 struct icmp_header {
 	/** Type */
diff --git a/gpxe/src/include/gpxe/icmp6.h b/gpxe/src/include/gpxe/icmp6.h
index 3409361..e8fd1eb 100644
--- a/gpxe/src/include/gpxe/icmp6.h
+++ b/gpxe/src/include/gpxe/icmp6.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/ip6.h>
 #include <gpxe/ndp.h>
 
diff --git a/gpxe/src/include/gpxe/ieee80211.h b/gpxe/src/include/gpxe/ieee80211.h
new file mode 100644
index 0000000..e5b10c3
--- /dev/null
+++ b/gpxe/src/include/gpxe/ieee80211.h
@@ -0,0 +1,1160 @@
+#ifndef _GPXE_IEEE80211_H
+#define _GPXE_IEEE80211_H
+
+#include <gpxe/if_ether.h>	/* for ETH_ALEN */
+#include <endian.h>
+
+/** @file
+ * Constants and data structures defined in IEEE 802.11, subsetted
+ * according to what gPXE knows how to use.
+ */
+
+FILE_LICENCE(GPL2_OR_LATER);
+
+/* ---------- Maximum lengths of things ---------- */
+
+/**
+ * @defgroup ieee80211_maxlen Maximum lengths in the 802.11 protocol
+ * @{
+ */
+
+/** Maximum length of frame payload
+ *
+ * This does not include cryptographic overhead, which can be up to 20
+ * bytes, but it DOES include the 802.2 LLC/SNAP headers that are used
+ * on data frames (but not management frames).
+ */
+#define IEEE80211_MAX_DATA_LEN          2304
+
+/** Length of LLC/SNAP headers on data frames */
+#define IEEE80211_LLC_HEADER_LEN	8
+
+/** Maximum cryptographic overhead before encrypted data */
+#define IEEE80211_MAX_CRYPTO_HEADER	8
+
+/** Maximum cryptographic overhead after encrypted data
+ *
+ * This does not count the MIC in TKIP frames, since that is
+ * considered to be part of the MSDU and thus contributes to the size
+ * of the data field.
+ *
+ * It @e does count the MIC in CCMP frames, which is considered part
+ * of the MPDU (outside the data field).
+ */
+#define IEEE80211_MAX_CRYPTO_TRAILER    8
+
+/** Total maximum cryptographic overhead */
+#define IEEE80211_MAX_CRYPTO_OVERHEAD	16
+
+/** Bytes of network-layer data that can go into a regular data frame */
+#define IEEE80211_MAX_FRAME_DATA	2296
+
+/** Frame header length for frames we might work with
+ *
+ * QoS adds a two-byte field on top of this, and APs communicating
+ * with each other in Wireless Distribution System (WDS) mode add an
+ * extra 6-byte MAC address field, but we do not work with such
+ * frames.
+ */
+#define IEEE80211_TYP_FRAME_HEADER_LEN	24
+
+/** Theoretical maximum frame header length
+ *
+ * This includes the QoS and WDS Addr4 fields that we should never
+ * see.
+ */
+#define IEEE80211_MAX_FRAME_HEADER_LEN	32
+
+/** Maximum combined frame length
+ *
+ * The biggest frame will include 32 frame header bytes, 16 bytes of
+ * crypto overhead, and 2304 data bytes.
+ */
+#define IEEE80211_MAX_FRAME_LEN         2352
+
+/** Maximum length of an ESSID */
+#define IEEE80211_MAX_SSID_LEN          32
+
+/** @} */
+
+
+/* ---------- Frame Control defines ---------- */
+
+/**
+ * @defgroup ieee80211_fc 802.11 Frame Control field bits
+ * @{
+ */
+
+/** 802.11 Frame Control field, Version bitmask */
+#define IEEE80211_FC_VERSION	0x0003
+
+/** Expected value of Version bits in Frame Control */
+#define  IEEE80211_THIS_VERSION  0x0000
+
+
+/** 802.11 Frame Control field, Frame Type bitmask */
+#define IEEE80211_FC_TYPE	0x000C
+
+/** Type value for management (layer-2) frames */
+#define  IEEE80211_TYPE_MGMT     0x0000
+
+/** Type value for control (layer-1, hardware-managed) frames */
+#define  IEEE80211_TYPE_CTRL     0x0004
+
+/** Type value for data frames */
+#define  IEEE80211_TYPE_DATA     0x0008
+
+
+/** 802.11 Frame Control field, Frame Subtype bitmask */
+#define IEEE80211_FC_SUBTYPE	0x00F0
+
+/** Subtype value for association-request management frames
+ *
+ * Association request frames are sent after authentication from the
+ * client to the Access Point to establish the client as part of the
+ * Access Point's network.
+ */
+#define  IEEE80211_STYPE_ASSOC_REQ    0x0000
+
+/** Subtype value for association-response management frames
+ *
+ * Association response frames are sent by the Access Point to confirm
+ * or deny the association requested in an association request frame.
+ */
+#define  IEEE80211_STYPE_ASSOC_RESP   0x0010
+
+/** Subtype value for reassociation-request management frames
+ *
+ * Reassociation request frames are sent by clients wishing to change
+ * from one Access Point to another while roaming within the same
+ * extended network (same ESSID).
+ */
+#define  IEEE80211_STYPE_REASSOC_REQ  0x0020
+
+/** Subtype value for reassociation-response management frames
+ *
+ * Reassociation response frames are sent by the Access Point to
+ * confirm or deny the swap requested in a reassociation request
+ * frame.
+ */
+#define  IEEE80211_STYPE_REASSOC_RESP 0x0030
+
+/** Subtype value for probe-request management frames
+ *
+ * Probe request frames are sent by clients to request that all Access
+ * Points on the sending channel, or all belonging to a particular
+ * ESSID, identify themselves by BSSID, supported transfer rates, RF
+ * configuration, and other capabilities.
+ */
+#define  IEEE80211_STYPE_PROBE_REQ    0x0040
+
+/** Subtype value for probe-response management frames
+ *
+ * Probe response frames are sent by Access Points in response to
+ * probe request frames, providing the requested information.
+ */
+#define  IEEE80211_STYPE_PROBE_RESP   0x0050
+
+/** Subtype value for beacon management frames
+ *
+ * Beacon frames are sent by Access Points at regular intervals,
+ * usually ten per second, on the channel on which they communicate.
+ * They can be used to probe passively for access points on a channel
+ * where local regulatory restrictions prohibit active scanning, or
+ * due to their regularity as a mechanism to determine the fraction of
+ * packets that are being dropped.
+ */
+#define  IEEE80211_STYPE_BEACON       0x0080
+
+/** Subtype value for disassociation management frames
+ *
+ * Disassociation frames are sent by either a client or an Access
+ * Point to unequivocally terminate the association between the two.
+ * They may be sent by clients upon leaving the network, or by an
+ * Access Point upon reconfiguration, among other reasons; they are
+ * usually more "polite" than deauthentication frames.
+ */
+#define  IEEE80211_STYPE_DISASSOC     0x00A0
+
+/** Subtype value for authentication management frames
+ *
+ * Authentication frames are exchanged between a client and an Access
+ * Point before association may be performed. Confusingly, in the most
+ * common authentication method (Open System) no security tokens are
+ * exchanged at all. Modern 802.11 security handshaking takes place
+ * after association.
+ */
+#define  IEEE80211_STYPE_AUTH         0x00B0
+
+/** Subtype value for deauthentication management frames
+ *
+ * Deauthentication frames are sent by either a client or an Access
+ * Point to terminate the authentication (and therefore also the
+ * association) between the two. They are generally more forceful than
+ * disassociation frames, sent for such reasons as a failure to
+ * set up security properly after associating.
+ */
+#define  IEEE80211_STYPE_DEAUTH       0x00C0
+
+/** Subtype value for action management frames
+ *
+ * Action frames are used to implement spectrum management and QoS
+ * features that gPXE currently does not support.
+ */
+#define  IEEE80211_STYPE_ACTION	      0x00D0
+
+
+/** Subtype value for RTS (request to send) control frames */
+#define  IEEE80211_STYPE_RTS          0x00B0
+
+/** Subtype value for CTS (clear to send) control frames */
+#define  IEEE80211_STYPE_CTS          0x00C0
+
+/** Subtype value for ACK (acknowledgement) control frames */
+#define  IEEE80211_STYPE_ACK          0x00D0
+
+
+/** Subtype value for ordinary data frames, with no QoS or CF add-ons */
+#define  IEEE80211_STYPE_DATA         0x0000
+
+/** Subtype value for data frames containing no data */
+#define  IEEE80211_STYPE_NODATA       0x0040
+
+
+/** 802.11 Frame Control field: To Data System flag
+ *
+ * This is set on data frames sent to an Access Point.
+ */
+#define IEEE80211_FC_TODS       0x0100
+
+/** 802.11 Frame Control field: From Data System flag
+ *
+ * This is set on data frames sent from an Access Point. If both TODS
+ * and FROMDS are set, the frame header is a 4-address format used for
+ * inter-Access Point communication.
+ */
+#define IEEE80211_FC_FROMDS     0x0200
+
+/** 802.11 Frame Control field: More Fragments flag */
+#define IEEE80211_FC_MORE_FRAG  0x0400
+
+/** 802.11 Frame Control field: Retransmission flag */
+#define IEEE80211_FC_RETRY      0x0800
+
+/** 802.11 Frame Control field: Power Managed flag
+ *
+ * This is set on any frame sent by a low-power station that will go
+ * into a power-saving mode immediately after this frame. Access
+ * Points are not allowed to act as low-power stations.
+ */
+#define IEEE80211_FC_PWR_MGMT   0x1000
+
+/** 802.11 Frame Control field: More Data flag
+ *
+ * This is set on any frame sent by a station that has more data
+ * queued to be sent than is in the frame.
+ */
+#define IEEE80211_FC_MORE_DATA  0x2000
+
+/** 802.11 Frame Control field: Protected flag
+ *
+ * This is set on frames in which data is encrypted (by any method).
+ */
+#define IEEE80211_FC_PROTECTED  0x4000
+
+/** 802.11 Frame Control field: Ordered flag [?] */
+#define IEEE80211_FC_ORDER      0x8000
+
+/** @} */
+
+
+/* ---------- Sequence Control defines ---------- */
+
+/**
+ * @defgroup ieee80211_seq 802.11 Sequence Control field handling
+ * @{
+ */
+
+/** Extract sequence number from 802.11 Sequence Control field */
+#define IEEE80211_SEQNR( seq )		( ( seq ) >> 4 )
+
+/** Extract fragment number from 802.11 Sequence Control field */
+#define IEEE80211_FRAG( seq )		( ( seq ) & 0x000F )
+
+/** Make 802.11 Sequence Control field from sequence and fragment numbers */
+#define IEEE80211_MAKESEQ( seqnr, frag )	\
+	( ( ( ( seqnr ) & 0xFFF ) << 4 ) | ( ( frag ) & 0xF ) )
+
+/** @} */
+
+
+/* ---------- Frame header formats ---------- */
+
+/**
+ * @defgroup ieee80211_hdr 802.11 frame header formats
+ * @{
+ */
+
+/** An 802.11 data or management frame without QoS or WDS header fields */
+struct ieee80211_frame
+{
+	u16 fc;			/**< 802.11 Frame Control field */
+	u16 duration;		/**< Microseconds to reserve link */
+	u8 addr1[ETH_ALEN];	/**< Address 1 (immediate receiver) */
+	u8 addr2[ETH_ALEN];	/**< Address 2 (immediate sender) */
+	u8 addr3[ETH_ALEN];	/**< Address 3 (often "forward to") */
+	u16 seq;		/**< 802.11 Sequence Control field */
+	u8 data[0];		/**< Beginning of frame data */
+} __attribute__((packed));
+
+/** The 802.2 LLC/SNAP header sent before actual data in a data frame
+ *
+ * This header is not acknowledged in the 802.11 standard at all; it
+ * is treated just like data for MAC-layer purposes, including
+ * fragmentation and encryption. It is actually two headers
+ * concatenated: a three-byte 802.2 LLC header indicating Subnetwork
+ * Accesss Protocol (SNAP) in both source and destination Service
+ * Access Point (SAP) fields, and a five-byte SNAP header indicating a
+ * zero OUI and two-byte Ethernet protocol type field.
+ *
+ * Thus, an eight-byte header in which six of the bytes are redundant.
+ * Lovely, isn't it?
+ */
+struct ieee80211_llc_snap_header
+{
+	/* LLC part: */
+	u8 dsap;		/**< Destination SAP ID */
+	u8 ssap;		/**< Source SAP ID */
+	u8 ctrl;		/**< Control information */
+
+	/* SNAP part: */
+	u8 oui[3];		/**< Organization code, usually 0 */
+	u16 ethertype;		/**< Ethernet Type field */
+} __attribute__((packed));
+
+/** Value for DSAP field in 802.2 LLC header for 802.11 frames: SNAP */
+#define IEEE80211_LLC_DSAP	0xAA
+
+/** Value for SSAP field in 802.2 LLC header for 802.11 frames: SNAP */
+#define IEEE80211_LLC_SSAP	0xAA
+
+/** Value for control field in 802.2 LLC header for 802.11 frames
+ *
+ * "Unnumbered Information".
+ */
+#define IEEE80211_LLC_CTRL	0x03
+
+
+/** 16-byte RTS frame format, with abbreviated header */
+struct ieee80211_rts
+{
+	u16 fc;			/**< 802.11 Frame Control field */
+	u16 duration;		/**< Microseconds to reserve link */
+	u8 addr1[ETH_ALEN];	/**< Address 1 (immediate receiver) */
+	u8 addr2[ETH_ALEN];	/**< Address 2 (immediate sender) */
+} __attribute__((packed));
+
+/** Length of 802.11 RTS control frame */
+#define IEEE80211_RTS_LEN	16
+
+/** 10-byte CTS or ACK frame format, with abbreviated header */
+struct ieee80211_cts_or_ack
+{
+	u16 fc;			/**< 802.11 Frame Control field */
+	u16 duration;		/**< Microseconds to reserve link */
+	u8 addr1[ETH_ALEN];	/**< Address 1 (immediate receiver) */
+} __attribute__((packed));
+
+#define ieee80211_cts ieee80211_cts_or_ack
+#define ieee80211_ack ieee80211_cts_or_ack
+
+/** Length of 802.11 CTS control frame */
+#define IEEE80211_CTS_LEN	10
+
+/** Length of 802.11 ACK control frame */
+#define IEEE80211_ACK_LEN	10
+
+/** @} */
+
+
+/* ---------- Capability bits, status and reason codes ---------- */
+
+/**
+ * @defgroup ieee80211_capab 802.11 management frame capability field bits
+ * @{
+ */
+
+/** Set if using an Access Point (managed mode) */
+#define IEEE80211_CAPAB_MANAGED       0x0001
+
+/** Set if operating in IBSS (no-AP, "Ad-Hoc") mode */
+#define IEEE80211_CAPAB_ADHOC         0x0002
+
+/** Set if we support Contention-Free Period operation */
+#define IEEE80211_CAPAB_CFPOLL        0x0004
+
+/** Set if we wish to be polled for Contention-Free operation */
+#define IEEE80211_CAPAB_CFPR          0x0008
+
+/** Set if the network is encrypted (by any method) */
+#define IEEE80211_CAPAB_PRIVACY       0x0010
+
+/** Set if PHY supports short preambles on 802.11b */
+#define IEEE80211_CAPAB_SHORT_PMBL    0x0020
+
+/** Set if PHY supports PBCC modulation */
+#define IEEE80211_CAPAB_PBCC          0x0040
+
+/** Set if we support Channel Agility */
+#define IEEE80211_CAPAB_CHAN_AGILITY  0x0080
+
+/** Set if we support spectrum management (DFS and TPC) on the 5GHz band */
+#define IEEE80211_CAPAB_SPECTRUM_MGMT 0x0100
+
+/** Set if we support Quality of Service enhancements */
+#define IEEE80211_CAPAB_QOS           0x0200
+
+/** Set if PHY supports short slot time on 802.11g */
+#define IEEE80211_CAPAB_SHORT_SLOT    0x0400
+
+/** Set if PHY supports APSD option */
+#define IEEE80211_CAPAB_APSD          0x0800
+
+/** Set if PHY supports DSSS/OFDM modulation (one way of 802.11 b/g mixing) */
+#define IEEE80211_CAPAB_DSSS_OFDM     0x2000
+
+/** Set if we support delayed block ACK */
+#define IEEE80211_CAPAB_DELAYED_BACK  0x4000
+
+/** Set if we support immediate block ACK */
+#define IEEE80211_CAPAB_IMMED_BACK    0x8000
+
+/** @} */
+
+
+/**
+ * @defgroup ieee80211_status 802.11 status codes
+ *
+ * These are returned to indicate an immediate denial of
+ * authentication or association. In gPXE, the lower 5 bits of the
+ * status code are encoded into the file-unique portion of an error
+ * code, the ERRFILE portion is always @c ERRFILE_net80211, and the
+ * POSIX error code is @c ECONNREFUSED for status 0-31 or @c
+ * EHOSTUNREACH for status 32-63.
+ *
+ * For a complete table with non-abbreviated error messages, see IEEE
+ * Std 802.11-2007, Table 7-23, p.94.
+ *
+ * @{
+ */
+
+#define IEEE80211_STATUS_SUCCESS		0
+#define IEEE80211_STATUS_FAILURE		1
+#define IEEE80211_STATUS_CAPAB_UNSUPP		10
+#define IEEE80211_STATUS_REASSOC_INVALID	11
+#define IEEE80211_STATUS_ASSOC_DENIED		12
+#define IEEE80211_STATUS_AUTH_ALGO_UNSUPP	13
+#define IEEE80211_STATUS_AUTH_SEQ_INVALID	14
+#define IEEE80211_STATUS_AUTH_CHALL_INVALID	15
+#define IEEE80211_STATUS_AUTH_TIMEOUT		16
+#define IEEE80211_STATUS_ASSOC_NO_ROOM		17
+#define IEEE80211_STATUS_ASSOC_NEED_RATE	18
+#define IEEE80211_STATUS_ASSOC_NEED_SHORT_PMBL	19
+#define IEEE80211_STATUS_ASSOC_NEED_PBCC	20
+#define IEEE80211_STATUS_ASSOC_NEED_CHAN_AGILITY 21
+#define IEEE80211_STATUS_ASSOC_NEED_SPECTRUM_MGMT 22
+#define IEEE80211_STATUS_ASSOC_BAD_POWER	23
+#define IEEE80211_STATUS_ASSOC_BAD_CHANNELS	24
+#define IEEE80211_STATUS_ASSOC_NEED_SHORT_SLOT	25
+#define IEEE80211_STATUS_ASSOC_NEED_DSSS_OFDM	26
+#define IEEE80211_STATUS_QOS_FAILURE		32
+#define IEEE80211_STATUS_QOS_NO_ROOM		33
+#define IEEE80211_STATUS_LINK_IS_HORRIBLE	34
+#define IEEE80211_STATUS_ASSOC_NEED_QOS		35
+#define IEEE80211_STATUS_REQUEST_DECLINED	37
+#define IEEE80211_STATUS_REQUEST_INVALID	38
+#define IEEE80211_STATUS_TS_NOT_CREATED_AGAIN	39
+#define IEEE80211_STATUS_INVALID_IE		40
+#define IEEE80211_STATUS_GROUP_CIPHER_INVALID	41
+#define IEEE80211_STATUS_PAIR_CIPHER_INVALID	42
+#define IEEE80211_STATUS_AKMP_INVALID		43
+#define IEEE80211_STATUS_RSN_VERSION_UNSUPP	44
+#define IEEE80211_STATUS_RSN_CAPAB_INVALID	45
+#define IEEE80211_STATUS_CIPHER_REJECTED	46
+#define IEEE80211_STATUS_TS_NOT_CREATED_WAIT	47
+#define IEEE80211_STATUS_DIRECT_LINK_FORBIDDEN	48
+#define IEEE80211_STATUS_DEST_NOT_PRESENT	49
+#define IEEE80211_STATUS_DEST_NOT_QOS		50
+#define IEEE80211_STATUS_ASSOC_LISTEN_TOO_HIGH	51
+
+/** @} */
+
+
+
+/**
+ * @defgroup ieee80211_reason 802.11 reason codes
+ *
+ * These are returned to indicate the reason for a deauthentication or
+ * disassociation sent (usually) after authentication or association
+ * had succeeded.  In gPXE, the lower 5 bits of the reason code are
+ * encoded into the file-unique portion of an error code, the ERRFILE
+ * portion is always @c ERRFILE_net80211, and the POSIX error code is
+ * @c ECONNRESET for reason 0-31 or @c ENETRESET for reason 32-63.
+ *
+ * For a complete table with non-abbreviated error messages, see IEEE
+ * Std 802.11-2007, Table 7-22, p.92.
+ *
+ * @{
+ */
+
+#define IEEE80211_REASON_NONE			0
+#define IEEE80211_REASON_UNSPECIFIED		1
+#define IEEE80211_REASON_AUTH_NO_LONGER_VALID	2
+#define IEEE80211_REASON_LEAVING		3
+#define IEEE80211_REASON_INACTIVITY		4
+#define IEEE80211_REASON_OUT_OF_RESOURCES	5
+#define IEEE80211_REASON_NEED_AUTH		6
+#define IEEE80211_REASON_NEED_ASSOC		7
+#define IEEE80211_REASON_LEAVING_TO_ROAM	8
+#define IEEE80211_REASON_REASSOC_INVALID	9
+#define IEEE80211_REASON_BAD_POWER		10
+#define IEEE80211_REASON_BAD_CHANNELS		11
+#define IEEE80211_REASON_INVALID_IE		13
+#define IEEE80211_REASON_MIC_FAILURE		14
+#define IEEE80211_REASON_4WAY_TIMEOUT		15
+#define IEEE80211_REASON_GROUPKEY_TIMEOUT	16
+#define IEEE80211_REASON_4WAY_INVALID		17
+#define IEEE80211_REASON_GROUP_CIPHER_INVALID	18
+#define IEEE80211_REASON_PAIR_CIPHER_INVALID	19
+#define IEEE80211_REASON_AKMP_INVALID		20
+#define IEEE80211_REASON_RSN_VERSION_INVALID	21
+#define IEEE80211_REASON_RSN_CAPAB_INVALID	22
+#define IEEE80211_REASON_8021X_FAILURE		23
+#define IEEE80211_REASON_CIPHER_REJECTED	24
+#define IEEE80211_REASON_QOS_UNSPECIFIED	32
+#define IEEE80211_REASON_QOS_OUT_OF_RESOURCES	33
+#define IEEE80211_REASON_LINK_IS_HORRIBLE	34
+#define IEEE80211_REASON_INVALID_TXOP		35
+#define IEEE80211_REASON_REQUESTED_LEAVING	36
+#define IEEE80211_REASON_REQUESTED_NO_USE	37
+#define IEEE80211_REASON_REQUESTED_NEED_SETUP	38
+#define IEEE80211_REASON_REQUESTED_TIMEOUT	39
+#define IEEE80211_REASON_CIPHER_UNSUPPORTED	45
+
+/** @} */
+
+/* ---------- Information element declarations ---------- */
+
+/**
+ * @defgroup ieee80211_ie 802.11 information elements
+ *
+ * Many management frames include a section that amounts to a
+ * concatenation of these information elements, so that the sender can
+ * choose which information to send and the receiver can ignore the
+ * parts it doesn't understand. Each IE contains a two-byte header,
+ * one byte ID and one byte length, followed by IE-specific data. The
+ * length does not include the two-byte header. Information elements
+ * are required to be sorted by ID, but gPXE does not require that in
+ * those it receives.
+ *
+ * This group also includes a few inline functions to simplify common
+ * tasks in IE processing.
+ *
+ * @{
+ */
+
+/** Generic 802.11 information element header */
+struct ieee80211_ie_header {
+	u8 id;			/**< Information element ID */
+	u8 len;			/**< Information element length */
+} __attribute__ ((packed));
+
+
+/** 802.11 SSID information element */
+struct ieee80211_ie_ssid {
+	u8 id;			/**< SSID ID: 0 */
+	u8 len;			/**< SSID length */
+	char ssid[0];		/**< SSID data, not NUL-terminated */
+} __attribute__ ((packed));
+
+/** Information element ID for SSID information element */
+#define IEEE80211_IE_SSID	0
+
+
+/** 802.11 rates information element
+ *
+ * The first 8 rates go in an IE of type RATES (1), and any more rates
+ * go in one of type EXT_RATES (50). Each rate is a byte with the low
+ * 7 bits equal to the rate in units of 500 kbps, and the high bit set
+ * if and only if the rate is "basic" (must be supported by all
+ * connected stations).
+ */
+struct ieee80211_ie_rates {
+	u8 id;			/**< Rates ID: 1 or 50 */
+	u8 len;			/**< Number of rates */
+	u8 rates[0];		/**< Rates data, one rate per byte */
+} __attribute__ ((packed));
+
+/** Information element ID for rates information element */
+#define IEEE80211_IE_RATES	1
+
+/** Information element ID for extended rates information element */
+#define IEEE80211_IE_EXT_RATES	50
+
+
+/** 802.11 Direct Spectrum parameter information element
+ *
+ * This just contains the channel number. It has the fancy name
+ * because IEEE 802.11 also defines a frequency-hopping PHY that
+ * changes channels at regular intervals following a predetermined
+ * pattern; in practice nobody uses the FH PHY.
+ */
+struct ieee80211_ie_ds_param {
+	u8 id;			/**< DS parameter ID: 3 */
+	u8 len;			/**< DS parameter length: 1 */
+	u8 current_channel;	/**< Current channel number, 1-14 */
+} __attribute__ ((packed));
+
+/** Information element ID for Direct Spectrum parameter information element */
+#define IEEE80211_IE_DS_PARAM	3
+
+
+/** 802.11 Country information element regulatory extension triplet */
+struct ieee80211_ie_country_ext_triplet {
+	u8 reg_ext_id;		/**< Regulatory extension ID */
+	u8 reg_class_id;	/**< Regulatory class ID */
+	u8 coverage_class;	/**< Coverage class */
+} __attribute__ ((packed));
+
+/** 802.11 Country information element regulatory band triplet */
+struct ieee80211_ie_country_band_triplet {
+	u8 first_channel;	/**< Channel number for first channel in band */
+	u8 nr_channels;		/**< Number of contiguous channels in band */
+	u8 max_txpower;		/**< Maximum TX power in dBm */
+} __attribute__ ((packed));
+
+/** 802.11 Country information element regulatory triplet
+ *
+ * It is a band triplet if the first byte is 200 or less, and a
+ * regulatory extension triplet otherwise.
+ */
+union ieee80211_ie_country_triplet {
+	/** Differentiator between band and ext triplets */
+	u8 first;
+
+	/** Information about a band of channels */
+	struct ieee80211_ie_country_band_triplet band;
+
+	/** Regulatory extension information */
+	struct ieee80211_ie_country_ext_triplet ext;
+};
+
+/** 802.11 Country information element
+ *
+ * This contains some data about RF regulations.
+ */
+struct ieee80211_ie_country {
+	u8 id;			/**< Country information ID: 7 */
+	u8 len;			/**< Country information length: varies */
+	char name[2];		/**< ISO Alpha2 country code */
+	char in_out;		/**< 'I' for indoor, 'O' for outdoor */
+
+	/** List of regulatory triplets */
+	union ieee80211_ie_country_triplet triplet[0];
+} __attribute__ ((packed));
+
+/** Information element ID for Country information element */
+#define IEEE80211_IE_COUNTRY	7
+
+
+/** 802.11 Request information element
+ *
+ * This contains a list of information element types we would like to
+ * be included in probe response frames.
+ */
+struct ieee80211_ie_request {
+	u8 id;			/**< Request ID: 10 */
+	u8 len;			/**< Number of IEs requested */
+	u8 request[0];		/**< List of IEs requested */
+} __attribute__ ((packed));
+
+/** Information element ID for Request information element */
+#define IEEE80211_IE_REQUEST	10
+
+
+/** 802.11 Challenge Text information element
+ *
+ * This is used in authentication frames under Shared Key
+ * authentication.
+ */
+struct ieee80211_ie_challenge_text {
+	u8 id;			/**< Challenge Text ID: 16 */
+	u8 len;			/**< Challenge Text length: usually 128 */
+	u8 challenge_text[0];	/**< Challenge Text data */
+} __attribute__ ((packed));
+
+/** Information element ID for Challenge Text information element */
+#define IEEE80211_IE_CHALLENGE_TEXT	16
+
+
+/** 802.11 Power Constraint information element
+ *
+ * This is used to specify an additional power limitation on top of
+ * the Country requirements.
+ */
+struct ieee80211_ie_power_constraint {
+	u8 id;			/**< Power Constraint ID: 52 */
+	u8 len;			/**< Power Constraint length: 1 */
+	u8 power_constraint;	/**< Decrease in allowed TX power, dBm */
+} __attribute__ ((packed));
+
+/** Information element ID for Power Constraint information element */
+#define IEEE80211_IE_POWER_CONSTRAINT	52
+
+
+/** 802.11 Power Capability information element
+ *
+ * This is used in association request frames to indicate the extremes
+ * of our TX power abilities. It is required only if we indicate
+ * support for spectrum management.
+ */
+struct ieee80211_ie_power_capab {
+	u8 id;			/**< Power Capability ID: 33 */
+	u8 len;			/**< Power Capability length: 2 */
+	u8 min_txpower;		/**< Minimum possible TX power, dBm */
+	u8 max_txpower;		/**< Maximum possible TX power, dBm */
+} __attribute__ ((packed));
+
+/** Information element ID for Power Capability information element */
+#define IEEE80211_IE_POWER_CAPAB	33
+
+
+/** 802.11 Channels information element channel band tuple */
+struct ieee80211_ie_channels_channel_band {
+	u8 first_channel;	/**< Channel number of first channel in band */
+	u8 nr_channels;		/**< Number of channels in band */
+} __attribute__ ((packed));
+
+/** 802.11 Channels information element
+ *
+ * This is used in association frames to indicate the channels we can
+ * use. It is required only if we indicate support for spectrum
+ * management.
+ */
+struct ieee80211_ie_channels {
+	u8 id;			/**< Channels ID: 36 */
+	u8 len;			/**< Channels length: 2 */
+
+	/** List of (start, length) channel bands we can use */
+	struct ieee80211_ie_channels_channel_band channels[0];
+} __attribute__ ((packed));
+
+/** Information element ID for Channels information element */
+#define IEEE80211_IE_CHANNELS	36
+
+
+/** 802.11 ERP Information information element
+ *
+ * This is used to communicate some PHY-level flags.
+ */
+struct ieee80211_ie_erp_info {
+	u8 id;			/**< ERP Information ID: 42 */
+	u8 len;			/**< ERP Information length: 1 */
+	u8 erp_info;		/**< ERP flags */
+} __attribute__ ((packed));
+
+/** Information element ID for ERP Information information element */
+#define IEEE80211_IE_ERP_INFO	42
+
+/** ERP information element: Flag set if 802.11b stations are present */
+#define  IEEE80211_ERP_NONERP_PRESENT	0x01
+
+/** ERP information element: Flag set if CTS protection must be used */
+#define  IEEE80211_ERP_USE_PROTECTION	0x02
+
+/** ERP information element: Flag set if long preambles must be used */
+#define  IEEE80211_ERP_BARKER_LONG	0x04
+
+
+/** 802.11 Robust Security Network ("WPA") information element
+ *
+ * Showing once again a striking clarity of design, the IEEE folks put
+ * dynamically-sized data in the middle of this structure. As such,
+ * the below structure definition only works for IEs we create
+ * ourselves, which always have one pairwise cipher and one AKM;
+ * received IEs should be parsed piecemeal.
+ *
+ * Also inspired was IEEE's choice of 16-bit fields to count the
+ * number of 4-byte elements in a structure with a maximum length of
+ * 255 bytes.
+ *
+ * Many fields reference a cipher or authentication-type ID; this is a
+ * three-byte OUI followed by one byte identifying the cipher with
+ * respect to that OUI. For all standard ciphers the OUI is 00:0F:AC,
+ * except in old-style WPA IEs encapsulated in vendor-specific IEs,
+ * where it's 00:50:F2.
+ */
+struct ieee80211_ie_rsn {
+	/** Information element ID */
+	u8 id;
+
+	/** Information element length */
+	u8 len;
+
+	/** RSN information element version */
+	u16 version;
+
+	/** Cipher ID for the cipher used in multicast/broadcast frames */
+	u32 group_cipher;
+
+	/** Number of unicast ciphers supported */
+	u16 pairwise_count;
+
+	/** List of cipher IDs for supported unicast frame ciphers */
+	u32 pairwise_cipher[1];
+
+	/** Number of authentication types supported */
+	u16 akm_count;
+
+	/** List of authentication type IDs for supported types */
+	u32 akm_list[1];
+
+	/** Security capabilities field (RSN only) */
+	u16 rsn_capab;
+
+	/** Number of PMKIDs included (present only in association frames) */
+	u16 pmkid_count;
+
+	/** List of PMKIDs included, each a 16-byte SHA1 hash */
+	u8 pmkid_list[0];
+} __attribute__((packed));
+
+/** Information element ID for Robust Security Network information element */
+#define IEEE80211_IE_RSN	48
+
+/** Calculate necessary size of RSN information element
+ *
+ * @v npair	Number of pairwise ciphers supported
+ * @v nauth	Number of authentication types supported
+ * @v npmkid	Number of PMKIDs to include
+ * @v is_rsn	If TRUE, calculate RSN IE size; if FALSE, calculate WPA IE size
+ * @ret size	Necessary size of IE, including header bytes
+ */
+static inline size_t ieee80211_rsn_size ( int npair, int nauth, int npmkid,
+					  int rsn_ie ) {
+	return 16 + 4 * ( npair + nauth ) + 16 * npmkid - 4 * ! rsn_ie;
+}
+
+/** Make OUI plus type byte into 32-bit integer for easy comparison */
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define _MKOUI( a, b, c, t )	\
+		( ( ( a ) << 24 ) | ( ( b ) << 16 ) | ( ( c ) << 8 ) | ( d ) )
+#define  OUI_ORG_MASK		0xFFFFFF00
+#define  OUI_TYPE_MASK		0x000000FF
+#else
+#define _MKOUI( a, b, c, t )	\
+		( ( ( t ) << 24 ) | ( ( c ) << 16 ) | ( ( b ) << 8 ) | ( a ) )
+#define  OUI_ORG_MASK		0x00FFFFFF
+#define  OUI_TYPE_MASK		0xFF000000
+#endif
+
+/** Organization part for OUIs in standard RSN IE */
+#define  IEEE80211_RSN_OUI	_MKOUI ( 0x00, 0x0F, 0xAC, 0 )
+
+/** Organization part for OUIs in old WPA IE */
+#define  IEEE80211_WPA_OUI	_MKOUI ( 0x00, 0x50, 0xF2, 0 )
+
+/** Old vendor-type WPA IE OUI type + subtype */
+#define  IEEE80211_WPA_OUI_VEN	_MKOUI ( 0x00, 0x50, 0xF2, 0x01 )
+
+
+/** 802.11 RSN IE: expected version number */
+#define  IEEE80211_RSN_VERSION		1
+
+/** 802.11 RSN IE: cipher type for 40-bit WEP */
+#define  IEEE80211_RSN_CTYPE_WEP40	_MKOUI ( 0, 0, 0, 0x01 )
+
+/** 802.11 RSN IE: cipher type for 104-bit WEP */
+#define  IEEE80211_RSN_CTYPE_WEP104	_MKOUI ( 0, 0, 0, 0x05 )
+
+/** 802.11 RSN IE: cipher type for TKIP ("WPA") */
+#define  IEEE80211_RSN_CTYPE_TKIP	_MKOUI ( 0, 0, 0, 0x02 )
+
+/** 802.11 RSN IE: cipher type for CCMP ("WPA2") */
+#define  IEEE80211_RSN_CTYPE_CCMP	_MKOUI ( 0, 0, 0, 0x04 )
+
+/** 802.11 RSN IE: cipher type for "use group"
+ *
+ * This can only appear as a pairwise cipher, and means unicast frames
+ * should be encrypted in the same way as broadcast/multicast frames.
+ */
+#define  IEEE80211_RSN_CTYPE_USEGROUP	_MKOUI ( 0, 0, 0, 0x00 )
+
+/** 802.11 RSN IE: auth method type for using an 802.1X server */
+#define  IEEE80211_RSN_ATYPE_8021X	_MKOUI ( 0, 0, 0, 0x01 )
+
+/** 802.11 RSN IE: auth method type for using a pre-shared key */
+#define  IEEE80211_RSN_ATYPE_PSK	_MKOUI ( 0, 0, 0, 0x02 )
+
+/** 802.11 RSN IE capabilities: AP supports pre-authentication */
+#define  IEEE80211_RSN_CAPAB_PREAUTH	0x001
+
+/** 802.11 RSN IE capabilities: Node has conflict between TKIP and WEP
+ *
+ * This is a legacy issue; APs always set it to 0, and gPXE sets it to
+ * 0.
+ */
+#define  IEEE80211_RSN_CAPAB_NO_PAIRWISE 0x002
+
+/** 802.11 RSN IE capabilities: Number of PTKSA replay counters
+ *
+ * A value of 0 means one replay counter, 1 means two, 2 means four,
+ * and 3 means sixteen.
+ */
+#define  IEEE80211_RSN_CAPAB_PTKSA_REPLAY 0x00C
+
+/** 802.11 RSN IE capabilities: Number of GTKSA replay counters
+ *
+ * A value of 0 means one replay counter, 1 means two, 2 means four,
+ * and 3 means sixteen.
+ */
+#define  IEEE80211_RSN_CAPAB_GTKSA_REPLAY 0x030
+
+/** 802.11 RSN IE capabilities: PeerKey Handshaking is suported */
+#define  IEEE80211_RSN_CAPAB_PEERKEY	0x200
+
+
+/** 802.11 RSN IE capabilities: One replay counter
+ *
+ * This should be AND'ed with @c IEEE80211_RSN_CAPAB_PTKSA_REPLAY or
+ * @c IEEE80211_RSN_CAPAB_GTKSA_REPLAY (or both) to produce a value
+ * which can be OR'ed into the capabilities field.
+ */
+#define IEEE80211_RSN_1_CTR		0x000
+
+/** 802.11 RSN IE capabilities: Two replay counters */
+#define IEEE80211_RSN_2_CTR		0x014
+
+/** 802.11 RSN IE capabilities: Four replay counters */
+#define IEEE80211_RSN_4_CTR		0x028
+
+/** 802.11 RSN IE capabilities: 16 replay counters */
+#define IEEE80211_RSN_16_CTR		0x03C
+
+
+/** 802.11 Vendor Specific information element
+ *
+ * One often sees the RSN IE masquerading as vendor-specific on
+ * devices that were produced prior to 802.11i (the WPA amendment)
+ * being finalized.
+ */
+struct ieee80211_ie_vendor {
+	u8 id;			/**< Vendor-specific ID: 221 */
+	u8 len;			/**< Vendor-specific length: variable */
+	u32 oui;		/**< OUI and vendor-specific type byte */
+	u8 data[0];		/**< Vendor-specific data */
+} __attribute__ ((packed));
+
+/** Information element ID for Vendor Specific information element */
+#define IEEE80211_IE_VENDOR	221
+
+
+
+
+/** Any 802.11 information element
+ *
+ * This is formatted for ease of use, so IEs with complex structures
+ * get referenced in full, while those with only one byte of data or a
+ * simple array are pulled in to avoid a layer of indirection like
+ * ie->channels.channels[0].
+ */
+union ieee80211_ie
+{
+	/** Generic and simple information element info */
+	struct {
+		u8 id;		/**< Information element ID */
+		u8 len;		/**< Information element data length */
+		union {
+			char ssid[0];	/**< SSID text */
+			u8 rates[0];	/**< Rates data */
+			u8 request[0];	/**< Request list */
+			u8 challenge_text[0]; /**< Challenge text data */
+			u8 power_constraint; /**< Power constraint, dBm */
+			u8 erp_info;	/**< ERP information flags */
+			/** List of channels */
+			struct ieee80211_ie_channels_channel_band channels[0];
+		};
+	};
+
+	/** DS parameter set */
+	struct ieee80211_ie_ds_param ds_param;
+
+	/** Country information */
+	struct ieee80211_ie_country country;
+
+	/** Power capability */
+	struct ieee80211_ie_power_capab power_capab;
+
+	/** Security information */
+	struct ieee80211_ie_rsn rsn;
+
+	/** Vendor-specific */
+	struct ieee80211_ie_vendor vendor;
+};
+
+/** Check that 802.11 information element is bounded by buffer
+ *
+ * @v ie	Information element
+ * @v end	End of buffer in which information element is stored
+ * @ret ok	TRUE if the IE is completely contained within the buffer
+ */
+static inline int ieee80211_ie_bound ( union ieee80211_ie *ie, void *end )
+{
+	void *iep = ie;
+	return ( iep + 2 <= end && iep + 2 + ie->len <= end );
+}
+
+/** Advance to next 802.11 information element
+ *
+ * @v ie	Current information element pointer
+ * @v end	Pointer to first byte not in information element space
+ * @ret next	Pointer to next information element, or NULL if no more
+ *
+ * When processing received IEs, @a end should be set to the I/O
+ * buffer tail pointer; when marshalling IEs for sending, @a end
+ * should be NULL.
+ */
+static inline union ieee80211_ie * ieee80211_next_ie ( union ieee80211_ie *ie,
+						       void *end )
+{
+	void *next_ie_byte = ( void * ) ie + ie->len + 2;
+	union ieee80211_ie *next_ie = next_ie_byte;
+
+	if ( ! end )
+		return next_ie;
+
+	if ( ieee80211_ie_bound ( next_ie, end ) )
+		return next_ie;
+
+	return NULL;
+}
+
+/** @} */
+
+
+/* ---------- Management frame data formats ---------- */
+
+/**
+ * @defgroup ieee80211_mgmt_data Management frame data payloads
+ * @{
+ */
+
+/** Beacon or probe response frame data */
+struct ieee80211_beacon_or_probe_resp
+{
+	/** 802.11 TSFT value at frame send */
+	u64 timestamp;
+
+	/** Interval at which beacons are sent, in units of 1024 us */
+	u16 beacon_interval;
+
+	/** Capability flags */
+	u16 capability;
+
+	/** List of information elements */
+	union ieee80211_ie info_element[0];
+} __attribute__((packed));
+
+#define ieee80211_beacon	ieee80211_beacon_or_probe_resp
+#define ieee80211_probe_resp	ieee80211_beacon_or_probe_resp
+
+/** Disassociation or deauthentication frame data */
+struct ieee80211_disassoc_or_deauth
+{
+	/** Reason code */
+	u16 reason;
+} __attribute__((packed));
+
+#define ieee80211_disassoc	ieee80211_disassoc_or_deauth
+#define ieee80211_deauth	ieee80211_disassoc_or_deauth
+
+/** Association request frame data */
+struct ieee80211_assoc_req
+{
+	/** Capability flags */
+	u16 capability;
+
+	/** Interval at which we wake up, in units of the beacon interval */
+	u16 listen_interval;
+
+	/** List of information elements */
+	union ieee80211_ie info_element[0];
+} __attribute__((packed));
+
+/** Association or reassociation response frame data */
+struct ieee80211_assoc_or_reassoc_resp
+{
+	/** Capability flags */
+	u16 capability;
+
+	/** Status code */
+	u16 status;
+
+	/** Association ID */
+	u16 aid;
+
+	/** List of information elements */
+	union ieee80211_ie info_element[0];
+} __attribute__((packed));
+
+#define ieee80211_assoc_resp	ieee80211_assoc_or_reassoc_resp
+#define ieee80211_reassoc_resp	ieee80211_assoc_or_reassoc_resp
+
+/** Reassociation request frame data */
+struct ieee80211_reassoc_req
+{
+	/** Capability flags */
+	u16 capability;
+
+	/** Interval at which we wake up, in units of the beacon interval */
+	u16 listen_interval;
+
+	/** MAC address of current Access Point */
+	u8 current_addr[ETH_ALEN];
+
+	/** List of information elements */
+	union ieee80211_ie info_element[0];
+} __attribute__((packed));
+
+/** Probe request frame data */
+struct ieee80211_probe_req
+{
+	/** List of information elements */
+	union ieee80211_ie info_element[0];
+} __attribute__((packed));
+
+/** Authentication frame data */
+struct ieee80211_auth
+{
+	/** Authentication algorithm (Open System or Shared Key) */
+	u16 algorithm;
+
+	/** Sequence number of this frame; first from client to AP is 1 */
+	u16 tx_seq;
+
+	/** Status code */
+	u16 status;
+
+	/** List of information elements */
+	union ieee80211_ie info_element[0];
+} __attribute__((packed));
+
+/** Open System authentication algorithm */
+#define IEEE80211_AUTH_OPEN_SYSTEM  0
+
+/** Shared Key authentication algorithm */
+#define IEEE80211_AUTH_SHARED_KEY   1
+
+/** @} */
+
+#endif
diff --git a/gpxe/src/include/gpxe/if_arp.h b/gpxe/src/include/gpxe/if_arp.h
index 5b25033..932bb3b 100644
--- a/gpxe/src/include/gpxe/if_arp.h
+++ b/gpxe/src/include/gpxe/if_arp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /* ARP protocol HARDWARE identifiers. */
diff --git a/gpxe/src/include/gpxe/if_ether.h b/gpxe/src/include/gpxe/if_ether.h
index 2f3f33d..b96bee0 100644
--- a/gpxe/src/include/gpxe/if_ether.h
+++ b/gpxe/src/include/gpxe/if_ether.h
@@ -1,6 +1,8 @@
 #ifndef	_GPXE_IF_ETHER_H
 #define	_GPXE_IF_ETHER_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 #define ETH_ALEN		6	/* Size of Ethernet address */
@@ -18,6 +20,7 @@
 #define ETH_P_RARP	0x8035	/* Reverse Address resolution Protocol */
 #define ETH_P_IPV6	0x86DD	/* IPv6 over blueblook */
 #define ETH_P_SLOW	0x8809	/* Ethernet slow protocols */
+#define ETH_P_EAPOL	0x888E	/* 802.1X EAP over LANs */
 #define ETH_P_AOE	0x88A2	/* ATA over Ethernet */
 
 /** An Ethernet link-layer header */
diff --git a/gpxe/src/include/gpxe/image.h b/gpxe/src/include/gpxe/image.h
index b953e15..10db8af 100644
--- a/gpxe/src/include/gpxe/image.h
+++ b/gpxe/src/include/gpxe/image.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/tables.h>
 #include <gpxe/list.h>
 #include <gpxe/uaccess.h>
@@ -123,9 +125,11 @@ struct image_type {
  */
 #define PROBE_PXE 03
 
+/** Executable or loadable image type table */
+#define IMAGE_TYPES __table ( struct image_type, "image_types" )
+
 /** An executable or loadable image type */
-#define __image_type( probe_order ) \
-	 __table ( struct image_type, image_types, probe_order )
+#define __image_type( probe_order ) __table_entry ( IMAGE_TYPES, probe_order )
 
 extern struct list_head images;
 
diff --git a/gpxe/src/include/gpxe/in.h b/gpxe/src/include/gpxe/in.h
index 831a611..c313717 100644
--- a/gpxe/src/include/gpxe/in.h
+++ b/gpxe/src/include/gpxe/in.h
@@ -1,13 +1,14 @@
 #ifndef	_GPXE_IN_H
 #define	_GPXE_IN_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/socket.h>
 
 /* Protocol numbers */
 
 #define IP_ICMP		1
-#define IP_IGMP		2
 #define IP_TCP		6
 #define IP_UDP		17
 #define IP_ICMP6	58
diff --git a/gpxe/src/include/gpxe/infiniband.h b/gpxe/src/include/gpxe/infiniband.h
index 196b595..d90b1c1 100644
--- a/gpxe/src/include/gpxe/infiniband.h
+++ b/gpxe/src/include/gpxe/infiniband.h
@@ -7,25 +7,88 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/refcnt.h>
 #include <gpxe/device.h>
 #include <gpxe/ib_packet.h>
 #include <gpxe/ib_mad.h>
 
-/** Subnet administrator QPN */
-#define IB_SA_QPN 1
+/** Subnet management interface QPN */
+#define IB_QPN_SMI 0
+
+/** Subnet management interface queue key */
+#define IB_QKEY_SMI 0
+
+/** General service interface QPN */
+#define IB_QPN_GSI 1
+
+/** General service interface queue key */
+#define IB_QKEY_GSI 0x80010000UL
 
 /** Broadcast QPN */
-#define IB_BROADCAST_QPN 0xffffffUL
+#define IB_QPN_BROADCAST 0xffffffUL
+
+/** QPN mask */
+#define IB_QPN_MASK 0xffffffUL
+
+/** Default Infiniband partition key */
+#define IB_PKEY_DEFAULT 0xffff
 
-/** Subnet administrator queue key */
-#define IB_GLOBAL_QKEY 0x80010000UL
+/** Infiniband partition key full membership flag */
+#define IB_PKEY_FULL 0x8000
+
+/**
+ * Maximum payload size
+ *
+ * This is currently hard-coded in various places (drivers, subnet
+ * management agent, etc.) to 2048.
+ */
+#define IB_MAX_PAYLOAD_SIZE 2048
 
 struct ib_device;
 struct ib_queue_pair;
 struct ib_address_vector;
 struct ib_completion_queue;
+struct ib_mad_interface;
+
+/** Infiniband transmission rates */
+enum ib_rate {
+	IB_RATE_2_5 = 2,
+	IB_RATE_10 = 3,
+	IB_RATE_30 = 4,
+	IB_RATE_5 = 5,
+	IB_RATE_20 = 6,
+	IB_RATE_40 = 7,
+	IB_RATE_60 = 8,
+	IB_RATE_80 = 9,
+	IB_RATE_120 = 10,
+};
+
+/** An Infiniband Address Vector */
+struct ib_address_vector {
+	/** Queue Pair Number */
+	unsigned long qpn;
+	/** Queue key
+	 *
+	 * Not specified for received packets.
+	 */
+	unsigned long qkey;
+	/** Local ID */
+	unsigned int lid;
+	/** Rate
+	 *
+	 * Not specified for received packets.
+	 */
+	enum ib_rate rate;
+	/** Service level */
+	unsigned int sl;
+	/** GID is present */
+	unsigned int gid_present;
+	/** GID, if present */
+	struct ib_gid gid;
+};
 
 /** An Infiniband Work Queue */
 struct ib_work_queue {
@@ -37,6 +100,8 @@ struct ib_work_queue {
 	struct ib_completion_queue *cq;
 	/** List of work queues on this completion queue */
 	struct list_head list;
+	/** Packet sequence number */
+	uint32_t psn;
 	/** Number of work queue entries */
 	unsigned int num_wqes;
 	/** Number of occupied work queue entries */
@@ -63,14 +128,31 @@ struct ib_multicast_gid {
 	struct ib_gid gid;
 };
 
+/** An Infiniband queue pair type */
+enum ib_queue_pair_type {
+	IB_QPT_SMI,
+	IB_QPT_GSI,
+	IB_QPT_UD,
+	IB_QPT_RC,
+};
+
 /** An Infiniband Queue Pair */
 struct ib_queue_pair {
 	/** Containing Infiniband device */
 	struct ib_device *ibdev;
 	/** List of queue pairs on this Infiniband device */
 	struct list_head list;
-	/** Queue Pair Number */
+	/** Queue pair number */
 	unsigned long qpn;
+	/** Externally-visible queue pair number
+	 *
+	 * This may differ from the real queue pair number (e.g. when
+	 * the HCA cannot use the management QPNs 0 and 1 as hardware
+	 * QPNs and needs to remap them).
+	 */
+	unsigned long ext_qpn;
+	/** Queue pair type */
+	enum ib_queue_pair_type type;
 	/** Queue key */
 	unsigned long qkey;
 	/** Send queue */
@@ -79,41 +161,14 @@ struct ib_queue_pair {
 	struct ib_work_queue recv;
 	/** List of multicast GIDs */
 	struct list_head mgids;
+	/** Address vector */
+	struct ib_address_vector av;
 	/** Driver private data */
 	void *drv_priv;
 	/** Queue owner private data */
 	void *owner_priv;
 };
 
-/** Infiniband queue pair modification flags */
-enum ib_queue_pair_mods {
-	IB_MODIFY_QKEY = 0x0001,
-};
-
-/** An Infiniband Address Vector */
-struct ib_address_vector {
-	/** Queue Pair Number */
-	unsigned long qpn;
-	/** Queue key
-	 *
-	 * Not specified for received packets.
-	 */
-	unsigned long qkey;
-	/** Local ID */
-	unsigned int lid;
-	/** Rate
-	 *
-	 * Not specified for received packets.
-	 */
-	unsigned int rate;
-	/** Service level */
-	unsigned int sl;
-	/** GID is present */
-	unsigned int gid_present;
-	/** GID, if present */
-	struct ib_gid gid;
-};
-
 /** Infiniband completion queue operations */
 struct ib_completion_queue_operations {
 	/**
@@ -144,6 +199,10 @@ struct ib_completion_queue_operations {
 
 /** An Infiniband Completion Queue */
 struct ib_completion_queue {
+	/** Containing Infiniband device */
+	struct ib_device *ibdev;
+	/** List of completion queues on this Infiniband device */
+	struct list_head list;
 	/** Completion queue number */
 	unsigned long cqn;
 	/** Number of completion queue entries */
@@ -197,12 +256,10 @@ struct ib_device_operations {
 	 *
 	 * @v ibdev		Infiniband device
 	 * @v qp		Queue pair
-	 * @v mod_list		Modification list
 	 * @ret rc		Return status code
 	 */
 	int ( * modify_qp ) ( struct ib_device *ibdev,
-			      struct ib_queue_pair *qp,
-			      unsigned long mod_list );
+			      struct ib_queue_pair *qp );
 	/** Destroy queue pair
 	 *
 	 * @v ibdev		Infiniband device
@@ -290,6 +347,25 @@ struct ib_device_operations {
 	void ( * mcast_detach ) ( struct ib_device *ibdev,
 				  struct ib_queue_pair *qp,
 				  struct ib_gid *gid );
+	/** Set port information
+	 *
+	 * @v ibdev		Infiniband device
+	 * @v mad		Set port information MAD
+	 *
+	 * This method is required only by adapters that do not have
+	 * an embedded SMA.
+	 */
+	int ( * set_port_info ) ( struct ib_device *ibdev, union ib_mad *mad );
+	/** Set partition key table
+	 *
+	 * @v ibdev		Infiniband device
+	 * @v mad		Set partition key table MAD
+	 *
+	 * This method is required only by adapters that do not have
+	 * an embedded SMA.
+	 */
+	int ( * set_pkey_table ) ( struct ib_device *ibdev,
+				   union ib_mad *mad );
 };
 
 /** An Infiniband device */
@@ -298,8 +374,12 @@ struct ib_device {
 	struct refcnt refcnt;
 	/** List of Infiniband devices */
 	struct list_head list;
+	/** List of open Infiniband devices */
+	struct list_head open_list;
 	/** Underlying device */
 	struct device *dev;
+	/** List of completion queues */
+	struct list_head cqs;
 	/** List of queue pairs */
 	struct list_head qps;
 	/** Infiniband operations */
@@ -311,10 +391,18 @@ struct ib_device {
 
 	/** Port state */
 	uint8_t port_state;
-	/** Link width */
-	uint8_t link_width;
-	/** Link speed */
-	uint8_t link_speed;
+	/** Link width supported */
+	uint8_t link_width_supported;
+	/** Link width enabled */
+	uint8_t link_width_enabled;
+	/** Link width active */
+	uint8_t link_width_active;
+	/** Link speed supported */
+	uint8_t link_speed_supported;
+	/** Link speed enabled */
+	uint8_t link_speed_enabled;
+	/** Link speed active */
+	uint8_t link_speed_active;
 	/** Port GID */
 	struct ib_gid gid;
 	/** Port LID */
@@ -326,8 +414,17 @@ struct ib_device {
 	/** Partition key */
 	uint16_t pkey;
 
-	/** Outbound packet sequence number */
-	uint32_t psn;
+	/** RDMA key
+	 *
+	 * This is a single key allowing unrestricted access to
+	 * memory.
+	 */
+	uint32_t rdma_key;
+
+	/** Subnet management interface */
+	struct ib_mad_interface *smi;
+	/** General services interface */
+	struct ib_mad_interface *gsi;
 
 	/** Driver private data */
 	void *drv_priv;
@@ -340,12 +437,14 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes,
 	       struct ib_completion_queue_operations *op );
 extern void ib_destroy_cq ( struct ib_device *ibdev,
 			    struct ib_completion_queue *cq );
+extern void ib_poll_cq ( struct ib_device *ibdev,
+			 struct ib_completion_queue *cq );
 extern struct ib_queue_pair *
-ib_create_qp ( struct ib_device *ibdev, unsigned int num_send_wqes,
-	       struct ib_completion_queue *send_cq, unsigned int num_recv_wqes,
-	       struct ib_completion_queue *recv_cq, unsigned long qkey );
-extern int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp,
-			  unsigned long mod_list, unsigned long qkey );
+ib_create_qp ( struct ib_device *ibdev, enum ib_queue_pair_type type,
+	       unsigned int num_send_wqes, struct ib_completion_queue *send_cq,
+	       unsigned int num_recv_wqes,
+	       struct ib_completion_queue *recv_cq );
+extern int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp );
 extern void ib_destroy_qp ( struct ib_device *ibdev,
 			    struct ib_queue_pair *qp );
 extern struct ib_queue_pair * ib_find_qp_qpn ( struct ib_device *ibdev,
@@ -366,16 +465,26 @@ extern void ib_complete_recv ( struct ib_device *ibdev,
 			       struct ib_queue_pair *qp,
 			       struct ib_address_vector *av,
 			       struct io_buffer *iobuf, int rc );
+extern void ib_refill_recv ( struct ib_device *ibdev,
+			     struct ib_queue_pair *qp );
 extern int ib_open ( struct ib_device *ibdev );
 extern void ib_close ( struct ib_device *ibdev );
+extern int ib_link_rc ( struct ib_device *ibdev );
 extern int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 			     struct ib_gid *gid );
 extern void ib_mcast_detach ( struct ib_device *ibdev,
 			      struct ib_queue_pair *qp, struct ib_gid *gid );
+extern int ib_get_hca_info ( struct ib_device *ibdev,
+			     struct ib_gid_half *hca_guid );
+extern int ib_set_port_info ( struct ib_device *ibdev, union ib_mad *mad );
+extern int ib_set_pkey_table ( struct ib_device *ibdev, union ib_mad *mad );
 extern struct ib_device * alloc_ibdev ( size_t priv_size );
 extern int register_ibdev ( struct ib_device *ibdev );
 extern void unregister_ibdev ( struct ib_device *ibdev );
+extern struct ib_device * find_ibdev ( struct ib_gid *gid );
+extern struct ib_device * last_opened_ibdev ( void );
 extern void ib_link_state_changed ( struct ib_device *ibdev );
+extern void ib_poll_eq ( struct ib_device *ibdev );
 extern struct list_head ib_devices;
 
 /** Iterate over all network devices */
@@ -383,17 +492,6 @@ extern struct list_head ib_devices;
 	list_for_each_entry ( (ibdev), &ib_devices, list )
 
 /**
- * Poll completion queue
- *
- * @v ibdev		Infiniband device
- * @v cq		Completion queue
- */
-static inline __always_inline void
-ib_poll_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ) {
-	ibdev->op->poll_cq ( ibdev, cq );
-}
-
-/**
  * Check link state
  *
  * @v ibdev		Infiniband device
diff --git a/gpxe/src/include/gpxe/init.h b/gpxe/src/include/gpxe/init.h
index e0e9f9c..a72cba7 100644
--- a/gpxe/src/include/gpxe/init.h
+++ b/gpxe/src/include/gpxe/init.h
@@ -1,6 +1,8 @@
 #ifndef _GPXE_INIT_H
 #define _GPXE_INIT_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/tables.h>
 
 /**
@@ -13,9 +15,11 @@ struct init_fn {
 	void ( * initialise ) ( void );
 };
 
+/** Initialisation function table */
+#define INIT_FNS __table ( struct init_fn, "init_fns" )
+
 /** Declare an initialisation functon */
-#define __init_fn( init_order ) \
-	__table ( struct init_fn, init_fns, init_order )
+#define __init_fn( init_order ) __table_entry ( INIT_FNS, init_order )
 
 /** @defgroup initfn_order Initialisation function ordering
  * @{
@@ -49,9 +53,12 @@ struct startup_fn {
 	void ( * shutdown ) ( int flags );
 };
 
+/** Startup/shutdown function table */
+#define STARTUP_FNS __table ( struct startup_fn, "startup_fns" )
+
 /** Declare a startup/shutdown function */
 #define __startup_fn( startup_order ) \
-	__table ( struct startup_fn, startup_fns, startup_order )
+	__table_entry ( STARTUP_FNS, startup_order )
 
 /** @defgroup startfn_order Startup/shutdown function ordering
  *
diff --git a/gpxe/src/include/gpxe/interface.h b/gpxe/src/include/gpxe/interface.h
index 94c711a..114ebf3 100644
--- a/gpxe/src/include/gpxe/interface.h
+++ b/gpxe/src/include/gpxe/interface.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/refcnt.h>
 
 /** An object communication interface */
diff --git a/gpxe/src/include/gpxe/io.h b/gpxe/src/include/gpxe/io.h
index ebb8ba3..919823d 100644
--- a/gpxe/src/include/gpxe/io.h
+++ b/gpxe/src/include/gpxe/io.h
@@ -16,6 +16,8 @@
  * the address parameter.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/api.h>
 #include <config/ioapi.h>
diff --git a/gpxe/src/include/gpxe/iobuf.h b/gpxe/src/include/gpxe/iobuf.h
index 6d1a58a..8f05f9e 100644
--- a/gpxe/src/include/gpxe/iobuf.h
+++ b/gpxe/src/include/gpxe/iobuf.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <assert.h>
 #include <gpxe/list.h>
diff --git a/gpxe/src/include/gpxe/ip.h b/gpxe/src/include/gpxe/ip.h
index b1b0c48..4342a0c 100644
--- a/gpxe/src/include/gpxe/ip.h
+++ b/gpxe/src/include/gpxe/ip.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/in.h>
 #include <gpxe/list.h>
diff --git a/gpxe/src/include/gpxe/ip6.h b/gpxe/src/include/gpxe/ip6.h
index dc0ae31..edb2863 100644
--- a/gpxe/src/include/gpxe/ip6.h
+++ b/gpxe/src/include/gpxe/ip6.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/in.h>
 
diff --git a/gpxe/src/include/gpxe/ipoib.h b/gpxe/src/include/gpxe/ipoib.h
index 80adee5..1d02f79 100644
--- a/gpxe/src/include/gpxe/ipoib.h
+++ b/gpxe/src/include/gpxe/ipoib.h
@@ -6,10 +6,9 @@
  * IP over Infiniband
  */
 
-#include <gpxe/infiniband.h>
+FILE_LICENCE ( GPL2_OR_LATER );
 
-/** IPoIB packet length */
-#define IPOIB_PKT_LEN 2048
+#include <gpxe/infiniband.h>
 
 /** IPoIB MAC address length */
 #define IPOIB_ALEN 20
@@ -18,9 +17,10 @@
 struct ipoib_mac {
 	/** Queue pair number
 	 *
-	 * MSB must be zero; QPNs are only 24-bit.
+	 * MSB indicates support for IPoIB "connected mode".  Lower 24
+	 * bits are the QPN.
 	 */
-	uint32_t qpn;
+	uint32_t flags__qpn;
 	/** Port GID */
 	struct ib_gid gid;
 } __attribute__ (( packed ));
@@ -52,29 +52,10 @@ struct ipoib_hdr {
 	} __attribute__ (( packed )) u;
 } __attribute__ (( packed ));
 
-extern struct ll_protocol ipoib_protocol;
-
 extern const char * ipoib_ntoa ( const void *ll_addr );
-
-/**
- * Allocate IPoIB device
- *
- * @v priv_size		Size of driver private data
- * @ret netdev		Network device, or NULL
- */
-static inline struct net_device * alloc_ipoibdev ( size_t priv_size ) {
-	struct net_device *netdev;
-
-	netdev = alloc_netdev ( priv_size );
-	if ( netdev ) {
-		netdev->ll_protocol = &ipoib_protocol;
-		netdev->max_pkt_len = IPOIB_PKT_LEN;
-	}
-	return netdev;
-}
-
 extern void ipoib_link_state_changed ( struct ib_device *ibdev );
 extern int ipoib_probe ( struct ib_device *ibdev );
 extern void ipoib_remove ( struct ib_device *ibdev );
+extern struct net_device * alloc_ipoibdev ( size_t priv_size );
 
 #endif /* _GPXE_IPOIB_H */
diff --git a/gpxe/src/include/gpxe/isa.h b/gpxe/src/include/gpxe/isa.h
index bb25dbc..63027a5 100644
--- a/gpxe/src/include/gpxe/isa.h
+++ b/gpxe/src/include/gpxe/isa.h
@@ -1,6 +1,8 @@
 #ifndef	ISA_H
 #define ISA_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/isa_ids.h>
 #include <gpxe/device.h>
@@ -58,8 +60,11 @@ struct isa_driver {
 	void ( * remove ) ( struct isa_device *isa );
 };
 
+/** ISA driver table */
+#define ISA_DRIVERS __table ( struct isa_driver, "isa_drivers" )
+
 /** Declare an ISA driver */
-#define __isa_driver __table ( struct isa_driver, isa_drivers, 01 )
+#define __isa_driver __table_entry ( ISA_DRIVERS, 01 )
 
 /**
  * Set ISA driver-private data
diff --git a/gpxe/src/include/gpxe/isa_ids.h b/gpxe/src/include/gpxe/isa_ids.h
index bf3f101..1faf114 100644
--- a/gpxe/src/include/gpxe/isa_ids.h
+++ b/gpxe/src/include/gpxe/isa_ids.h
@@ -19,6 +19,8 @@
  * the underlying "meaning" is big-endian.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <byteswap.h>
 
 /*
diff --git a/gpxe/src/include/gpxe/isapnp.h b/gpxe/src/include/gpxe/isapnp.h
index 07797a9..b58a87e 100644
--- a/gpxe/src/include/gpxe/isapnp.h
+++ b/gpxe/src/include/gpxe/isapnp.h
@@ -33,6 +33,8 @@
 *
 ***************************************************************************/
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef ISAPNP_H
 #define ISAPNP_H
 
@@ -223,8 +225,11 @@ struct isapnp_driver {
 	void ( * remove ) ( struct isapnp_device *isapnp );
 };
 
+/** ISAPnP driver table */
+#define ISAPNP_DRIVERS __table ( struct isapnp_driver, "isapnp_drivers" )
+
 /** Declare an ISAPnP driver */
-#define __isapnp_driver __table ( struct isapnp_driver, isapnp_drivers, 01 )
+#define __isapnp_driver __table_entry ( ISAPNP_DRIVERS, 01 )
 
 extern uint16_t isapnp_read_port;
 
diff --git a/gpxe/src/include/gpxe/iscsi.h b/gpxe/src/include/gpxe/iscsi.h
index 0510974..00717d2 100644
--- a/gpxe/src/include/gpxe/iscsi.h
+++ b/gpxe/src/include/gpxe/iscsi.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/socket.h>
 #include <gpxe/scsi.h>
@@ -247,7 +249,7 @@ struct iscsi_bhs_scsi_command {
 	/** Segment lengths */
 	union iscsi_segment_lengths lengths;
 	/** SCSI Logical Unit Number */
-	uint64_t lun;
+	struct scsi_lun lun;
 	/** Initiator Task Tag */
 	uint32_t itt;
 	/** Expected data transfer length */
@@ -342,7 +344,7 @@ struct iscsi_bhs_data_in {
 	/** Segment lengths */
 	union iscsi_segment_lengths lengths;
 	/** Logical Unit Number */
-	uint64_t lun;
+	struct scsi_lun lun;
 	/** Initiator Task Tag */
 	uint32_t itt;
 	/** Target Transfer Tag */
@@ -390,7 +392,7 @@ struct iscsi_bhs_data_out {
 	/** Segment lengths */
 	union iscsi_segment_lengths lengths;
 	/** Logical Unit Number */
-	uint64_t lun;
+	struct scsi_lun lun;
 	/** Initiator Task Tag */
 	uint32_t itt;
 	/** Target Transfer Tag */
@@ -426,7 +428,7 @@ struct iscsi_bhs_r2t {
 	/** Segment lengths */
 	union iscsi_segment_lengths lengths;
 	/** Logical Unit Number */
-	uint64_t lun;
+	struct scsi_lun lun;
 	/** Initiator Task Tag */
 	uint32_t itt;
 	/** Target Transfer Tag */
@@ -505,7 +507,7 @@ struct iscsi_session {
 	/** Target IQN */
 	char *target_iqn;
 	/** Logical Unit Number (LUN) */
-	uint64_t lun;
+	struct scsi_lun lun;
 	/** Target socket address (recorded only for iBFT) */
 	struct sockaddr target_sockaddr;
 
@@ -612,11 +614,6 @@ struct iscsi_session {
 	 * Set to NULL when command is complete.
 	 */
 	struct scsi_command *command;
-	/** SCSI command return code
-	 *
-	 * Set to -EINPROGRESS while command is processing.
-	 */
-	int rc;
 	/** Instant return code
 	 *
 	 * Set to a non-zero value if all requests should return
diff --git a/gpxe/src/include/gpxe/job.h b/gpxe/src/include/gpxe/job.h
index 8e11aa3..f1bcada 100644
--- a/gpxe/src/include/gpxe/job.h
+++ b/gpxe/src/include/gpxe/job.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <gpxe/interface.h>
 
@@ -66,6 +68,8 @@ extern struct job_interface_operations null_job_ops;
 
 extern void job_done ( struct job_interface *job, int rc );
 extern void job_kill ( struct job_interface *job );
+extern void job_progress ( struct job_interface *job,
+			   struct job_progress *progress );
 
 extern void ignore_job_done ( struct job_interface *job, int rc );
 extern void ignore_job_kill ( struct job_interface *job );
diff --git a/gpxe/src/include/gpxe/keys.h b/gpxe/src/include/gpxe/keys.h
index 3da8a1f..25bc9bc 100644
--- a/gpxe/src/include/gpxe/keys.h
+++ b/gpxe/src/include/gpxe/keys.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /*
  * Symbolic names for some standard ASCII characters
  *
diff --git a/gpxe/src/include/gpxe/linebuf.h b/gpxe/src/include/gpxe/linebuf.h
index 676731a..cfa2147 100644
--- a/gpxe/src/include/gpxe/linebuf.h
+++ b/gpxe/src/include/gpxe/linebuf.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stddef.h>
 
diff --git a/gpxe/src/include/gpxe/linux_compat.h b/gpxe/src/include/gpxe/linux_compat.h
index 2c3cbbd..577512e 100644
--- a/gpxe/src/include/gpxe/linux_compat.h
+++ b/gpxe/src/include/gpxe/linux_compat.h
@@ -10,6 +10,8 @@
  * intended to be a substitute for proper porting.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <errno.h>
 #include <stdio.h>
diff --git a/gpxe/src/include/gpxe/list.h b/gpxe/src/include/gpxe/list.h
index 602382b..22ba201 100644
--- a/gpxe/src/include/gpxe/list.h
+++ b/gpxe/src/include/gpxe/list.h
@@ -9,6 +9,8 @@
  * list.h.
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 #include <stddef.h>
 #include <assert.h>
 
diff --git a/gpxe/src/include/gpxe/login_ui.h b/gpxe/src/include/gpxe/login_ui.h
index d92ba8f..4196f7b 100644
--- a/gpxe/src/include/gpxe/login_ui.h
+++ b/gpxe/src/include/gpxe/login_ui.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern int login_ui ( void );
 
 #endif /* _GPXE_LOGIN_UI_H */
diff --git a/gpxe/src/include/gpxe/malloc.h b/gpxe/src/include/gpxe/malloc.h
index cce5d1d..c02a866 100644
--- a/gpxe/src/include/gpxe/malloc.h
+++ b/gpxe/src/include/gpxe/malloc.h
@@ -9,6 +9,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /*
  * Prototypes for the standard functions (malloc() et al) are in
  * stdlib.h.  Include <gpxe/malloc.h> only if you need the
diff --git a/gpxe/src/include/gpxe/mca.h b/gpxe/src/include/gpxe/mca.h
index 21f9e74..da9d73e 100644
--- a/gpxe/src/include/gpxe/mca.h
+++ b/gpxe/src/include/gpxe/mca.h
@@ -5,6 +5,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef MCA_H
 #define MCA_H
 
@@ -77,8 +79,11 @@ struct mca_driver {
 	void ( * remove ) ( struct mca_device *mca );
 };
 
+/** MCA driver table */
+#define MCA_DRIVERS __table ( struct mca_driver, "mca_drivers" )
+
 /** Declare an MCA driver */
-#define __mca_driver __table ( struct mca_driver, mca_drivers, 01 )
+#define __mca_driver __table_entry ( MCA_DRIVERS, 01 )
 
 /**
  * Set MCA driver-private data
diff --git a/gpxe/src/include/gpxe/md5.h b/gpxe/src/include/gpxe/md5.h
index f8976a1..03d65c1 100644
--- a/gpxe/src/include/gpxe/md5.h
+++ b/gpxe/src/include/gpxe/md5.h
@@ -1,6 +1,8 @@
 #ifndef _GPXE_MD5_H
 #define _GPXE_MD5_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct digest_algorithm;
 
 #include <stdint.h>
diff --git a/gpxe/src/include/gpxe/memmap.h b/gpxe/src/include/gpxe/memmap.h
index 836a1b9..dc5bec3 100644
--- a/gpxe/src/include/gpxe/memmap.h
+++ b/gpxe/src/include/gpxe/memmap.h
@@ -10,6 +10,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** A usable memory region */
 struct memory_region {
 	/** Physical start address */
diff --git a/gpxe/src/include/gpxe/monojob.h b/gpxe/src/include/gpxe/monojob.h
index aaa38d0..35ff4fd 100644
--- a/gpxe/src/include/gpxe/monojob.h
+++ b/gpxe/src/include/gpxe/monojob.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct job_interface;
 
 extern struct job_interface monojob;
diff --git a/gpxe/src/include/gpxe/nap.h b/gpxe/src/include/gpxe/nap.h
index f9ae3cf..6c2e40c 100644
--- a/gpxe/src/include/gpxe/nap.h
+++ b/gpxe/src/include/gpxe/nap.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/api.h>
 #include <config/nap.h>
 
diff --git a/gpxe/src/include/gpxe/net80211.h b/gpxe/src/include/gpxe/net80211.h
new file mode 100644
index 0000000..027e091
--- /dev/null
+++ b/gpxe/src/include/gpxe/net80211.h
@@ -0,0 +1,1186 @@
+#ifndef _GPXE_NET80211_H
+#define _GPXE_NET80211_H
+
+#include <gpxe/process.h>
+#include <gpxe/ieee80211.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/rc80211.h>
+
+/** @file
+ *
+ * The gPXE 802.11 MAC layer.
+ */
+
+/*
+ * Major things NOT YET supported:
+ * - any type of security
+ * - 802.11n
+ *
+ * Major things that probably will NEVER be supported, barring a
+ * compelling use case and/or corporate sponsorship:
+ * - QoS
+ * - 802.1X authentication ("WPA Enterprise")
+ * - Contention-free periods
+ * - "ad-hoc" networks (IBSS), monitor mode, host AP mode
+ * - hidden networks on the 5GHz band due to regulatory issues
+ * - spectrum management on the 5GHz band (TPC and DFS), as required
+ *   in some non-US regulatory domains
+ * - Clause 14 PHYs (Frequency-Hopping Spread Spectrum on 2.4GHz)
+ *   and Clause 16 PHYs (infrared) - I'm not aware of any real-world
+ *   use of these.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/* All 802.11 devices are handled using a generic "802.11 device"
+   net_device, with a link in its `priv' field to a net80211_device
+   which we use to handle 802.11-specific details. */
+
+
+/** @defgroup net80211_band RF bands on which an 802.11 device can transmit */
+/** @{ */
+
+/** The 2.4 GHz ISM band, unlicensed in most countries */
+#define NET80211_BAND_2GHZ	0
+/** The band from 4.9 GHz to 5.7 GHz, which tends to be more restricted */
+#define NET80211_BAND_5GHZ	1
+/** Number of RF bands */
+#define NET80211_NR_BANDS	2
+
+/** Bitmask for the 2GHz band */
+#define NET80211_BAND_BIT_2GHZ	(1 << 0)
+/** Bitmask for the 5GHz band */
+#define NET80211_BAND_BIT_5GHZ	(1 << 1)
+
+/** @} */
+
+
+/** @defgroup net80211_mode 802.11 operation modes supported by hardware */
+/** @{ */
+
+/** 802.11a: 54 Mbps operation using OFDM signaling on the 5GHz band */
+#define NET80211_MODE_A		(1 << 0)
+
+/** 802.11b: 1-11 Mbps operation using DSSS/CCK signaling on the 2.4GHz band */
+#define NET80211_MODE_B		(1 << 1)
+
+/** 802.11g: 54 Mbps operation using ERP/OFDM signaling on the 2.4GHz band */
+#define NET80211_MODE_G		(1 << 2)
+
+/** 802.11n: High-rate operation using MIMO technology on 2.4GHz or 5GHz */
+#define NET80211_MODE_N		(1 << 3)
+
+/** @} */
+
+
+/** @defgroup net80211_cfg Constants for the net80211 config callback */
+/** @{ */
+
+/** Channel choice (@c dev->channel) or regulatory parameters have changed */
+#define NET80211_CFG_CHANNEL	(1 << 0)
+
+/** Requested transmission rate (@c dev->rate) has changed */
+#define NET80211_CFG_RATE	(1 << 1)
+
+/** Association has been established with a new BSS (@c dev->bssid) */
+#define NET80211_CFG_ASSOC	(1 << 2)
+
+/** Low-level link parameters (short preamble, protection, etc) have changed */
+#define NET80211_CFG_PHY_PARAMS	(1 << 3)
+
+/** @} */
+
+
+/** An 802.11 security handshaking protocol */
+enum net80211_security_proto {
+	/** No security handshaking
+	 *
+	 * This might be used with an open network or with WEP, as
+	 * WEP does not have a cryptographic handshaking phase.
+	 */
+	NET80211_SECPROT_NONE = 0,
+
+	/** Pre-shared key handshaking
+	 *
+	 * This implements the "WPA Personal" handshake. 802.1X
+	 * authentication is not performed -- the user supplies a
+	 * pre-shared key directly -- but there is a 4-way handshake
+	 * between client and AP to verify that both have the same key
+	 * without revealing the contents of that key.
+	 */
+	NET80211_SECPROT_PSK = 1,
+
+	/** Full EAP 802.1X handshaking
+	 *
+	 * This implements the "WPA Enterprise" handshake, connecting
+	 * to an 802.1X authentication server to provide credentials
+	 * and receive a pairwise master key (PMK), which is then used
+	 * in the same 4-way handshake as the PSK method.
+	 */
+	NET80211_SECPROT_EAP = 2,
+
+	/** Dummy value used when the handshaking type can't be detected */
+	NET80211_SECPROT_UNKNOWN = 3,
+};
+
+
+/** An 802.11 data encryption algorithm */
+enum net80211_crypto_alg {
+	/** No security, an "Open" network */
+	NET80211_CRYPT_NONE = 0,
+
+	/** Network protected with WEP (awful RC4-based system)
+	 *
+	 * WEP uses a naive application of RC4, with a monotonically
+	 * increasing initialization vector that is prepended to the
+	 * key to initialize the RC4 keystream. It is highly insecure
+	 * and can be completely cracked or subverted using automated,
+	 * robust, freely available tools (aircrack-ng) in minutes.
+	 *
+	 * 40-bit and 104-bit WEP are differentiated only by the size
+	 * of the key. They may be advertised as 64-bit and 128-bit,
+	 * counting the non-random IV as part of the key bits.
+	 */
+	NET80211_CRYPT_WEP = 1,
+
+	/** Network protected with TKIP (better RC4-based system)
+	 *
+	 * Usually known by its trade name of WPA (Wi-Fi Protected
+	 * Access), TKIP implements a message integrity code (MIC)
+	 * called Michael, a timestamp counter for replay prevention,
+	 * and a key mixing function that together remove almost all
+	 * the security problems with WEP. Countermeasures are
+	 * implemented to prevent high data-rate attacks.
+	 *
+	 * There exists one known attack on TKIP, that allows one to
+	 * send between 7 and 15 arbitrary short data packets on a
+	 * QoS-enabled network given about an hour of data
+	 * gathering. Since gPXE does not support QoS for 802.11
+	 * networks, this is not a threat to us. The only other method
+	 * is a brute-force passphrase attack.
+	 */
+	NET80211_CRYPT_TKIP = 2,
+
+	/** Network protected with CCMP (AES-based system)
+	 *
+	 * Often called WPA2 in commerce, or RSNA (Robust Security
+	 * Network Architecture) in the 802.11 standard, CCMP is
+	 * highly secure and does not have any known attack vectors.
+	 * Since it is based on a block cipher, the statistical
+	 * correlation and "chopchop" attacks used with great success
+	 * against WEP and minor success against TKIP fail.
+	 */
+	NET80211_CRYPT_CCMP = 3,
+
+	/** Dummy value used when the cryptosystem can't be detected */
+	NET80211_CRYPT_UNKNOWN = 4,
+};
+
+
+/** @defgroup net80211_state Bits for the 802.11 association state field */
+/** @{ */
+
+/** An error code indicating the failure mode, or 0 if successful */
+#define NET80211_STATUS_MASK    0x7F
+
+/** Whether the error code provided is a "reason" code, not a "status" code */
+#define NET80211_IS_REASON	0x80
+
+/** Whether we have found the network we will be associating with */
+#define NET80211_PROBED		(1 << 8)
+
+/** Whether we have successfully authenticated with the network
+ *
+ * This usually has nothing to do with actual security; it is a
+ * holdover from older 802.11 implementation ideas.
+ */
+#define NET80211_AUTHENTICATED  (1 << 9)
+
+/** Whether we have successfully associated with the network */
+#define NET80211_ASSOCIATED     (1 << 10)
+
+/** Whether we have completed security handshaking with the network
+ *
+ * Once this is set, we can send data packets. For that reason this
+ * bit is set even in cases where no security handshaking is
+ * required.
+ */
+#define NET80211_CRYPTO_SYNCED  (1 << 11)
+
+/** Whether the auto-association task is running */
+#define NET80211_WORKING        (1 << 12)
+
+/** Whether the auto-association task is waiting for a reply from the AP */
+#define NET80211_WAITING        (1 << 13)
+
+/** Whether the auto-association task should be suppressed
+ *
+ * This is set by the `iwlist' command so that it can open the device
+ * without starting another probe process that will interfere with its
+ * own.
+ */
+#define NET80211_NO_ASSOC	(1 << 14)
+
+/** Whether this association was performed using a broadcast SSID
+ *
+ * If the user opened this device without netX/ssid set, the device's
+ * SSID will be set to that of the network it chooses to associate
+ * with, but the netX/ssid setting will remain blank. If we don't
+ * remember that we started from no specified SSID, it will appear
+ * every time settings are updated (e.g. after DHCP) that we need to
+ * reassociate due to the difference between the set SSID and our own.
+ */
+#define NET80211_AUTO_SSID	(1 << 15)
+
+
+/** @} */
+
+
+/** @defgroup net80211_phy 802.11 physical layer flags */
+/** @{ */
+
+/** Whether to use RTS/CTS or CTS-to-self protection for transmissions
+ *
+ * Since the RTS or CTS is transmitted using 802.11b signaling, and
+ * includes a field indicating the amount of time that will be used by
+ * transmission of the following packet, this serves as an effective
+ * protection mechanism to avoid 802.11b clients interfering with
+ * 802.11g clients on mixed networks.
+ */
+#define NET80211_PHY_USE_PROTECTION      (1 << 1)
+
+/** Whether to use 802.11b short preamble operation
+ *
+ * Short-preamble operation can moderately increase throughput on
+ * 802.11b networks operating between 2Mbps and 11Mbps. It is
+ * irrelevant for 802.11g data rates, since they use a different
+ * modulation scheme.
+ */
+#define NET80211_PHY_USE_SHORT_PREAMBLE  (1 << 2)
+
+/** Whether to use 802.11g short slot operation
+ *
+ * This affects a low-level timing parameter of 802.11g transmissions.
+ */
+#define NET80211_PHY_USE_SHORT_SLOT      (1 << 3)
+
+/** @} */
+
+
+/** The maximum number of TX rates we allow to be configured simultaneously */
+#define NET80211_MAX_RATES	16
+
+/** The maximum number of channels we allow to be configured simultaneously */
+#define NET80211_MAX_CHANNELS	32
+
+/** Seconds we'll wait to get all fragments of a packet */
+#define NET80211_FRAG_TIMEOUT	2
+
+/** The number of fragments we can receive at once
+ *
+ * The 802.11 standard requires that this be at least 3.
+ */
+#define NET80211_NR_CONCURRENT_FRAGS 3
+
+/** Maximum TX power to allow (dBm), if we don't get a regulatory hint */
+#define NET80211_REG_TXPOWER	20
+
+
+struct net80211_device;
+
+/** Operations that must be implemented by an 802.11 driver */
+struct net80211_device_operations {
+	/** Open 802.11 device
+	 *
+	 * @v dev	802.11 device
+	 * @ret rc	Return status code
+	 *
+	 * This method should allocate RX I/O buffers and enable the
+	 * hardware to start transmitting and receiving packets on the
+	 * channels its net80211_register() call indicated it could
+	 * handle. It does not need to tune the antenna to receive
+	 * packets on any particular channel.
+	 */
+	int ( * open ) ( struct net80211_device *dev );
+
+	/** Close 802.11 network device
+	 *
+	 * @v dev	802.11 device
+	 *
+	 * This method should stop the flow of packets, and call
+	 * net80211_tx_complete() for any packets remaining in the
+	 * device's TX queue.
+	 */
+	void ( * close ) ( struct net80211_device *dev );
+
+	/** Transmit packet on 802.11 network device
+	 *
+	 * @v dev	802.11 device
+	 * @v iobuf	I/O buffer
+	 * @ret rc	Return status code
+	 *
+	 * This method should cause the hardware to initiate
+	 * transmission of the I/O buffer, using the channel and rate
+	 * most recently indicated by an appropriate call to the
+	 * @c config callback. The 802.11 layer guarantees that said
+	 * channel and rate will be the same as those currently
+	 * reflected in the fields of @a dev.
+	 *
+	 * If this method returns success, the I/O buffer remains
+	 * owned by the network layer's TX queue, and the driver must
+	 * eventually call net80211_tx_complete() to free the buffer
+	 * whether transmission succeeded or not. If this method
+	 * returns failure, it will be interpreted as "failure to
+	 * enqueue buffer" and the I/O buffer will be immediately
+	 * released.
+	 *
+	 * This method is guaranteed to be called only when the device
+	 * is open.
+	 */
+	int ( * transmit ) ( struct net80211_device *dev,
+			     struct io_buffer *iobuf );
+
+	/** Poll for completed and received packets
+	 *
+	 * @v dev	802.11 device
+	 *
+	 * This method should cause the hardware to check for
+	 * completed transmissions and received packets. Any received
+	 * packets should be delivered via net80211_rx(), and
+	 * completed transmissions should be indicated using
+	 * net80211_tx_complete().
+	 *
+	 * This method is guaranteed to be called only when the device
+	 * is open.
+	 */
+	void ( * poll ) ( struct net80211_device *dev );
+
+	/** Enable or disable interrupts
+	 *
+	 * @v dev	802.11 device
+	 * @v enable	If TRUE, interrupts should be enabled
+	 */
+	void ( * irq ) ( struct net80211_device *dev, int enable );
+
+	/** Update hardware state to match 802.11 layer state
+	 *
+	 * @v dev	802.11 device
+	 * @v changed	Set of flags indicating what may have changed
+	 * @ret rc	Return status code
+	 *
+	 * This method should cause the hardware state to be
+	 * reinitialized from the state indicated in fields of
+	 * net80211_device, in the areas indicated by bits set in
+	 * @a changed. If the hardware is unable to do so, this method
+	 * may return an appropriate error indication.
+	 *
+	 * This method is guaranteed to be called only when the device
+	 * is open.
+	 */
+	int ( * config ) ( struct net80211_device *dev, int changed );
+};
+
+/** An 802.11 RF channel. */
+struct net80211_channel
+{
+	/** The band with which this channel is associated */
+	u8 band;
+
+	/** A channel number interpreted according to the band
+	 *
+	 * The 2.4GHz band uses channel numbers from 1-13 at 5MHz
+	 * intervals such that channel 1 is 2407 MHz; channel 14,
+	 * legal for use only in Japan, is defined separately as 2484
+	 * MHz. Adjacent channels will overlap, since 802.11
+	 * transmissions use a 20 MHz (4-channel) bandwidth. Most
+	 * commonly, channels 1, 6, and 11 are used.
+	 *
+	 * The 5GHz band uses channel numbers derived directly from
+	 * the frequency; channel 0 is 5000 MHz, and channels are
+	 * always spaced 5 MHz apart. Channel numbers over 180 are
+	 * relative to 4GHz instead of 5GHz, but these are rarely
+	 * seen. Most channels are not legal for use.
+	 */
+	u8 channel_nr;
+
+	/** The center frequency for this channel
+	 *
+	 * Currently a bandwidth of 20 MHz is assumed.
+	 */
+	u16 center_freq;
+
+	/** Hardware channel value */
+	u16 hw_value;
+
+	/** Maximum allowable transmit power, in dBm
+	 *
+	 * This should be interpreted as EIRP, the power supplied to
+	 * an ideal isotropic antenna in order to achieve the same
+	 * average signal intensity as the real hardware at a
+	 * particular distance.
+	 *
+	 * Currently no provision is made for directional antennas.
+	 */
+	u8 maxpower;
+};
+
+/** Information on the capabilities of an 802.11 hardware device
+ *
+ * In its probe callback, an 802.11 driver must read hardware
+ * registers to determine the appropriate contents of this structure,
+ * fill it, and pass it to net80211_register() so that the 802.11
+ * layer knows how to treat the hardware and what to advertise as
+ * supported to access points.
+ */
+struct net80211_hw_info
+{
+	/** Default hardware MAC address.
+	 *
+	 * The user may change this by setting the @c netX/mac setting
+	 * before the driver's open function is called; in that case
+	 * the driver must set the hardware MAC address to the address
+	 * contained in the wrapping net_device's ll_addr field, or if
+	 * that is impossible, set that ll_addr field back to the
+	 * unchangeable hardware MAC address.
+	 */
+	u8 hwaddr[ETH_ALEN];
+
+	/** A bitwise OR of the 802.11x modes supported by this device */
+	int modes;
+
+	/** A bitwise OR of the bands on which this device can communicate */
+	int bands;
+
+	/** A set of flags indicating peculiarities of this device. */
+	enum {
+		/** Received frames include a frame check sequence. */
+		NET80211_HW_RX_HAS_FCS = (1 << 1),
+
+		/** Hardware doesn't support 2.4GHz short preambles
+		 *
+		 * This is only relevant for 802.11b operation above
+		 * 2Mbps. All 802.11g devices support short preambles.
+		 */
+		NET80211_HW_NO_SHORT_PREAMBLE = (1 << 2),
+
+		/** Hardware doesn't support 802.11g short slot operation */
+		NET80211_HW_NO_SHORT_SLOT = (1 << 3),
+	} flags;
+
+	/** Signal strength information that can be provided by the device
+	 *
+	 * Signal strength is passed to net80211_rx(), primarily to
+	 * allow determination of the closest access point for a
+	 * multi-AP network. The units are provided for completeness
+	 * of status displays.
+	 */
+	enum {
+		/** No signal strength information supported */
+		NET80211_SIGNAL_NONE = 0,
+		/** Signal strength in arbitrary units */
+		NET80211_SIGNAL_ARBITRARY,
+		/** Signal strength in decibels relative to arbitrary base */
+		NET80211_SIGNAL_DB,
+		/** Signal strength in decibels relative to 1mW */
+		NET80211_SIGNAL_DBM,
+	} signal_type;
+
+	/** Maximum signal in arbitrary cases
+	 *
+	 * If signal_type is NET80211_SIGNAL_ARBITRARY or
+	 * NET80211_SIGNAL_DB, the driver should report it on a scale
+	 * from 0 to signal_max.
+	 */
+	unsigned signal_max;
+
+	/** List of RF channels supported by the card */
+	struct net80211_channel channels[NET80211_MAX_CHANNELS];
+
+	/** Number of supported channels */
+	int nr_channels;
+
+	/** List of transmission rates supported by the card, indexed by band
+	 *
+	 * Rates should be in 100kbps increments (e.g. 11 Mbps would
+	 * be represented as the number 110).
+	 */
+	u16 rates[NET80211_NR_BANDS][NET80211_MAX_RATES];
+
+	/** Number of supported rates, indexed by band */
+	int nr_rates[NET80211_NR_BANDS];
+
+	/** Estimate of the time required to change channels, in microseconds
+	 *
+	 * If this is not known, a guess on the order of a few
+	 * milliseconds (value of 1000-5000) is reasonable.
+	 */
+	unsigned channel_change_time;
+};
+
+/** Structure tracking received fragments for a packet
+ *
+ * We set up a fragment cache entry when we receive a packet marked as
+ * fragment 0 with the "more fragments" bit set in its frame control
+ * header. We are required by the 802.11 standard to track 3
+ * fragmented packets arriving simultaneously; if we receive more we
+ * may drop some. Upon receipt of a new fragment-0 packet, if no entry
+ * is available or expired, we take over the most @e recent entry for
+ * the new packet, since we don't want to starve old entries from ever
+ * finishing at all. If we get a fragment after the zeroth with no
+ * cache entry for its packet, we drop it.
+ */
+struct net80211_frag_cache
+{
+	/** Whether this cache entry is in use */
+	u8 in_use;
+
+	/** Sequence number of this MSDU (packet) */
+	u16 seqnr;
+
+	/** Timestamp from point at which first fragment was collected */
+	u32 start_ticks;
+
+	/** Buffers for each fragment */
+	struct io_buffer *iob[16];
+};
+
+
+/** Interface to an 802.11 security handshaking protocol
+ *
+ * Security handshaking protocols handle parsing a user-specified key
+ * into a suitable input to the encryption algorithm, and for WPA and
+ * better systems, manage performing whatever authentication with the
+ * network is necessary.
+ *
+ * At all times when any method in this structure is called with a
+ * net80211_device argument @a dev, a dynamically allocated copy of
+ * the handshaker structure itself with space for the requested amount
+ * of private data may be accessed as @c dev->handshaker. The
+ * structure will not be modified, and will only be freed during
+ * reassociation and device closing after the @a stop method has been
+ * called.
+ */
+struct net80211_handshaker
+{
+	/** The security handshaking protocol implemented */
+	enum net80211_security_proto protocol;
+
+	/** Initialize security handshaking protocol
+	 *
+	 * @v dev	802.11 device
+	 * @ret rc	Return status code
+	 *
+	 * This method is expected to access @c netX/key or other
+	 * applicable settings to determine the parameters for
+	 * handshaking. If no handshaking is required, it should call
+	 * sec80211_install() with the cryptosystem and key that are
+	 * to be used, and @c start and @c step should be set to @c
+	 * NULL.
+	 *
+	 * This is always called just before association is performed,
+	 * but after its parameters have been set; in particular, you
+	 * may rely on the contents of the @a essid field in @a dev.
+	 */
+	int ( * init ) ( struct net80211_device *dev );
+
+	/** Start handshaking
+	 *
+	 * @v dev	802.11 device
+	 * @ret rc	Return status code
+	 *
+	 * This method is expected to set up internal state so that
+	 * packets sent immediately after association, before @a step
+	 * can be called, will be handled appropriately.
+	 *
+	 * This is always called just before association is attempted.
+	 */
+	int ( * start ) ( struct net80211_device *dev );
+
+	/** Process handshaking state
+	 *
+	 * @v dev	802.11 device
+	 * @ret rc	Return status code, or positive if done
+	 *
+	 * This method is expected to perform as much progress on the
+	 * protocol it implements as is possible without blocking. It
+	 * should return 0 if it wishes to be called again, a negative
+	 * return status code on error, or a positive value if
+	 * handshaking is complete. In the case of a positive return,
+	 * net80211_crypto_install() must have been called.
+	 *
+	 * If handshaking may require further action (e.g. an AP that
+	 * might decide to rekey), handlers must be installed by this
+	 * function that will act without further calls to @a step.
+	 */
+	int ( * step ) ( struct net80211_device *dev );
+
+	/** Change cryptographic key based on setting
+	 *
+	 * @v dev	802.11 device
+	 * @ret rc	Return status code
+	 *
+	 * This method is called whenever the @c netX/key setting
+	 * @e may have been changed. It is expected to determine
+	 * whether it did in fact change, and if so, to install the
+	 * new key using net80211_crypto_install(). If it is not
+	 * possible to do this immediately, this method should return
+	 * an error; in that case the 802.11 stack will reassociate,
+	 * following the usual init/start/step sequence.
+	 *
+	 * This method is only relevant when it is possible to
+	 * associate successfully with an incorrect key. When it is
+	 * not, a failed association will be retried until the user
+	 * changes the key setting, and a successful association will
+	 * not be dropped due to such a change. When association with
+	 * an incorrect key is impossible, this function should return
+	 * 0 after performing no action.
+	 */
+	int ( * change_key ) ( struct net80211_device *dev );
+
+	/** Stop security handshaking handlers
+	 *
+	 * @v dev	802.11 device
+	 *
+	 * This method is called just before freeing a security
+	 * handshaker; it could, for example, delete a process that @a
+	 * start had created to manage the security of the connection.
+	 * If not needed it may be set to NULL.
+	 */
+	void ( * stop ) ( struct net80211_device *dev );
+
+	/** Amount of private data requested
+	 *
+	 * Before @c init is called for the first time, this structure's
+	 * @c priv pointer will point to this many bytes of allocated
+	 * data, where the allocation will be performed separately for
+	 * each net80211_device.
+	 */
+	int priv_len;
+
+	/** Whether @a start has been called
+	 *
+	 * Reset to 0 after @a stop is called.
+	 */
+	int started;
+
+	/** Pointer to private data
+	 *
+	 * In initializing this structure statically for a linker
+	 * table, set this to NULL.
+	 */
+	void *priv;
+};
+
+#define NET80211_HANDSHAKERS __table ( struct net80211_handshaker, \
+				       "net80211_handshakers" )
+#define __net80211_handshaker __table_entry ( NET80211_HANDSHAKERS, 01 )
+
+
+/** Interface to an 802.11 cryptosystem
+ *
+ * Cryptosystems define a net80211_crypto structure statically, using
+ * a gPXE linker table to make it available to the 802.11 layer. When
+ * the cryptosystem needs to be used, the 802.11 code will allocate a
+ * copy of the static definition plus whatever space the algorithm has
+ * requested for private state, and point net80211_device::crypto or
+ * net80211_device::gcrypto at it.
+ */
+struct net80211_crypto
+{
+	/** The cryptographic algorithm implemented */
+	enum net80211_crypto_alg algorithm;
+
+	/** Initialize cryptosystem using a given key
+	 *
+	 * @v crypto	802.11 cryptosystem
+	 * @v key	Pointer to key bytes
+	 * @v keylen	Number of key bytes
+	 * @v rsc	Initial receive sequence counter, if applicable
+	 * @ret rc	Return status code
+	 *
+	 * This method is passed the communication key provided by the
+	 * security handshake handler, which will already be in the
+	 * low-level form required. It may not store a pointer to the
+	 * key after returning; it must copy it to its private storage.
+	 */
+	int ( * init ) ( struct net80211_crypto *crypto, const void *key,
+			 int keylen, const void *rsc );
+
+	/** Encrypt a frame using the cryptosystem
+	 *
+	 * @v crypto	802.11 cryptosystem
+	 * @v iob	I/O buffer
+	 * @ret eiob	Newly allocated I/O buffer with encrypted packet
+	 *
+	 * This method is called to encrypt a single frame. It is
+	 * guaranteed that initialize() will have completed
+	 * successfully before this method is called.
+	 *
+	 * The frame passed already has an 802.11 header prepended,
+	 * but the PROTECTED bit in the frame control field will not
+	 * be set; this method is responsible for setting it. The
+	 * returned I/O buffer should contain a complete copy of @a
+	 * iob, including the 802.11 header, but with the PROTECTED
+	 * bit set, the data encrypted, and whatever encryption
+	 * headers/trailers are necessary added.
+	 *
+	 * This method should never free the passed I/O buffer.
+	 *
+	 * Return NULL if the packet could not be encrypted, due to
+	 * memory limitations or otherwise.
+	 */
+	struct io_buffer * ( * encrypt ) ( struct net80211_crypto *crypto,
+					   struct io_buffer *iob );
+
+	/** Decrypt a frame using the cryptosystem
+	 *
+	 * @v crypto	802.11 cryptosystem
+	 * @v eiob	Encrypted I/O buffer
+	 * @ret iob	Newly allocated I/O buffer with decrypted packet
+	 *
+	 * This method is called to decrypt a single frame. It is
+	 * guaranteed that initialize() will have completed
+	 * successfully before this method is called.
+	 *
+	 * Decryption follows the reverse of the pattern used for
+	 * encryption: this method must copy the 802.11 header into
+	 * the returned packet, decrypt the data stream, remove any
+	 * encryption header or trailer, and clear the PROTECTED bit
+	 * in the frame control header.
+	 *
+	 * This method should never free the passed I/O buffer.
+	 *
+	 * Return NULL if memory was not available for decryption, if
+	 * a consistency or integrity check on the decrypted frame
+	 * failed, or if the decrypted frame should not be processed
+	 * by the network stack for any other reason.
+	 */
+	struct io_buffer * ( * decrypt ) ( struct net80211_crypto *crypto,
+					   struct io_buffer *iob );
+
+	/** Length of private data requested to be allocated */
+	int priv_len;
+
+	/** Private data for the algorithm to store key and state info */
+	void *priv;
+};
+
+#define NET80211_CRYPTOS __table ( struct net80211_crypto, "net80211_cryptos" )
+#define __net80211_crypto __table_entry ( NET80211_CRYPTOS, 01 )
+
+
+struct net80211_probe_ctx;
+struct net80211_assoc_ctx;
+
+
+/** Structure encapsulating the complete state of an 802.11 device
+ *
+ * An 802.11 device is always wrapped by a network device, and this
+ * network device is always pointed to by the @a netdev field. In
+ * general, operations should never be performed by 802.11 code using
+ * netdev functions directly. It is usually the case that the 802.11
+ * layer might need to do some processing or bookkeeping on top of
+ * what the netdevice code will do.
+ */
+struct net80211_device
+{
+	/** The net_device that wraps us. */
+	struct net_device *netdev;
+
+	/** List of 802.11 devices. */
+	struct list_head list;
+
+	/** 802.11 device operations */
+	struct net80211_device_operations *op;
+
+	/** Driver private data */
+	void *priv;
+
+	/** Information about the hardware, provided to net80211_register() */
+	struct net80211_hw_info *hw;
+
+	/* ---------- Channel and rate fields ---------- */
+
+	/** A list of all possible channels we might use */
+	struct net80211_channel channels[NET80211_MAX_CHANNELS];
+
+	/** The number of channels in the channels array */
+	u8 nr_channels;
+
+	/** The channel currently in use, as an index into the channels array */
+	u8 channel;
+
+	/** A list of all possible TX rates we might use
+	 *
+	 * Rates are in units of 100 kbps.
+	 */
+	u16 rates[NET80211_MAX_RATES];
+
+	/** The number of transmission rates in the rates array */
+	u8 nr_rates;
+
+	/** The rate currently in use, as an index into the rates array */
+	u8 rate;
+
+	/** The rate to use for RTS/CTS transmissions
+	 *
+	 * This is always the fastest basic rate that is not faster
+	 * than the data rate in use. Also an index into the rates array.
+	 */
+	u8 rtscts_rate;
+
+	/** Bitmask of basic rates
+	 *
+	 * If bit N is set in this value, with the LSB considered to
+	 * be bit 0, then rate N in the rates array is a "basic" rate.
+	 *
+	 * We don't decide which rates are "basic"; our AP does, and
+	 * we respect its wishes. We need to be able to identify basic
+	 * rates in order to calculate the duration of a CTS packet
+	 * used for 802.11 g/b interoperability.
+	 */
+	u32 basic_rates;
+
+	/* ---------- Association fields ---------- */
+
+	/** The asynchronous association process.
+	 *
+	 * When an 802.11 netdev is opened, or when the user changes
+	 * the SSID setting on an open 802.11 device, an
+	 * autoassociation task is started by net80211_autoassocate()
+	 * to associate with the new best network. The association is
+	 * asynchronous, but no packets can be transmitted until it is
+	 * complete. If it is successful, the wrapping net_device is
+	 * set as "link up". If it fails, @c assoc_rc will be set with
+	 * an error indication.
+	 */
+	struct process proc_assoc;
+
+	/** Network with which we are associating
+	 *
+	 * This will be NULL when we are not actively in the process
+	 * of associating with a network we have already successfully
+	 * probed for.
+	 */
+	struct net80211_wlan *associating;
+
+	/** Context for the association process
+	 *
+	 * This is a probe_ctx if the @c PROBED flag is not set in @c
+	 * state, and an assoc_ctx otherwise.
+	 */
+	union {
+		struct net80211_probe_ctx *probe;
+		struct net80211_assoc_ctx *assoc;
+	} ctx;
+
+	/** Security handshaker being used */
+	struct net80211_handshaker *handshaker;
+
+	/** State of our association to the network
+	 *
+	 * Since the association process happens asynchronously, it's
+	 * necessary to have some channel of communication so the
+	 * driver can say "I got an association reply and we're OK" or
+	 * similar. This variable provides that link. It is a bitmask
+	 * of any of NET80211_PROBED, NET80211_AUTHENTICATED,
+	 * NET80211_ASSOCIATED, NET80211_CRYPTO_SYNCED to indicate how
+	 * far along in associating we are; NET80211_WORKING if the
+	 * association task is running; and NET80211_WAITING if a
+	 * packet has been sent that we're waiting for a reply to. We
+	 * can only be crypto-synced if we're associated, we can
+	 * only be associated if we're authenticated, we can only be
+	 * authenticated if we've probed.
+	 *
+	 * If an association process fails (that is, we receive a
+	 * packet with an error indication), the error code is copied
+	 * into bits 6-0 of this variable and bit 7 is set to specify
+	 * what type of error code it is. An AP can provide either a
+	 * "status code" (0-51 are defined) explaining why it refused
+	 * an association immediately, or a "reason code" (0-45 are
+	 * defined) explaining why it canceled an association after it
+	 * had originally OK'ed it. Status and reason codes serve
+	 * similar functions, but they use separate error message
+	 * tables. A gPXE-formatted return status code (negative) is
+	 * placed in @c assoc_rc.
+	 *
+	 * If the failure to associate is indicated by a status code,
+	 * the NET80211_IS_REASON bit will be clear; if it is
+	 * indicated by a reason code, the bit will be set. If we were
+	 * successful, both zero status and zero reason mean success,
+	 * so there is no ambiguity.
+	 *
+	 * To prevent association when opening the device, user code
+	 * can set the NET80211_NO_ASSOC bit. The final bit in this
+	 * variable, NET80211_AUTO_SSID, is used to remember whether
+	 * we picked our SSID through automated probing as opposed to
+	 * user specification; the distinction becomes relevant in the
+	 * settings applicator.
+	 */
+	u16 state;
+
+	/** Return status code associated with @c state */
+	int assoc_rc;
+
+	/** RSN or WPA information element to include with association
+	 *
+	 * If set to @c NULL, none will be included. It is expected
+	 * that this will be set by the @a init function of a security
+	 * handshaker if it is needed.
+	 */
+	union ieee80211_ie *rsn_ie;
+
+	/* ---------- Parameters of currently associated network ---------- */
+
+	/** 802.11 cryptosystem for our current network
+	 *
+	 * For an open network, this will be set to NULL.
+	 */
+	struct net80211_crypto *crypto;
+
+	/** 802.11 cryptosystem for multicast and broadcast frames
+	 *
+	 * If this is NULL, the cryptosystem used for receiving
+	 * unicast frames will also be used for receiving multicast
+	 * and broadcast frames. Transmitted multicast and broadcast
+	 * frames are always sent unicast to the AP, who multicasts
+	 * them on our behalf; thus they always use the unicast
+	 * cryptosystem.
+	 */
+	struct net80211_crypto *gcrypto;
+
+	/** MAC address of the access point most recently associated */
+	u8 bssid[ETH_ALEN];
+
+	/** SSID of the access point we are or will be associated with
+	 *
+	 * Although the SSID field in 802.11 packets is generally not
+	 * NUL-terminated, here and in net80211_wlan we add a NUL for
+	 * convenience.
+	 */
+	char essid[IEEE80211_MAX_SSID_LEN+1];
+
+	/** Association ID given to us by the AP */
+	u16 aid;
+
+	/** TSFT value for last beacon received, microseconds */
+	u64 last_beacon_timestamp;
+
+	/** Time between AP sending beacons, microseconds */
+	u32 tx_beacon_interval;
+
+	/** Smoothed average time between beacons, microseconds */
+	u32 rx_beacon_interval;
+
+	/* ---------- Physical layer information ---------- */
+
+	/** Physical layer options
+	 *
+	 * These control the use of CTS protection, short preambles,
+	 * and short-slot operation.
+	 */
+	int phy_flags;
+
+	/** Signal strength of last received packet */
+	int last_signal;
+
+	/** Rate control state */
+	struct rc80211_ctx *rctl;
+
+	/* ---------- Packet handling state ---------- */
+
+	/** Fragment reassembly state */
+	struct net80211_frag_cache frags[NET80211_NR_CONCURRENT_FRAGS];
+
+	/** The sequence number of the last packet we sent */
+	u16 last_tx_seqnr;
+
+	/** Packet duplication elimination state
+	 *
+	 * We are only required to handle immediate duplicates for
+	 * each direct sender, and since we can only have one direct
+	 * sender (the AP), we need only keep the sequence control
+	 * field from the most recent packet we've received. Thus,
+	 * this field stores the last sequence control field we've
+	 * received for a packet from the AP.
+	 */
+	u16 last_rx_seq;
+
+	/** RX management packet queue
+	 *
+	 * Sometimes we want to keep probe, beacon, and action packets
+	 * that we receive, such as when we're scanning for networks.
+	 * Ordinarily we drop them because they are sent at a large
+	 * volume (ten beacons per second per AP, broadcast) and we
+	 * have no need of them except when we're scanning.
+	 *
+	 * When keep_mgmt is TRUE, received probe, beacon, and action
+	 * management packets will be stored in this queue.
+	 */
+	struct list_head mgmt_queue;
+
+	/** RX management packet info queue
+	 *
+	 * We need to keep track of the signal strength for management
+	 * packets we're keeping, because that provides the only way
+	 * to distinguish between multiple APs for the same network.
+	 * Since we can't extend io_buffer to store signal, this field
+	 * heads a linked list of "RX packet info" structures that
+	 * contain that signal strength field. Its entries always
+	 * parallel the entries in mgmt_queue, because the two queues
+	 * are always added to or removed from in parallel.
+	 */
+	struct list_head mgmt_info_queue;
+
+	/** Whether to store management packets
+	 *
+	 * Received beacon, probe, and action packets will be added to
+	 * mgmt_queue (and their signal strengths added to
+	 * mgmt_info_queue) only when this variable is TRUE. It should
+	 * be set by net80211_keep_mgmt() (which returns the old
+	 * value) only when calling code is prepared to poll the
+	 * management queue frequently, because packets will otherwise
+	 * pile up and exhaust memory.
+	 */
+	int keep_mgmt;
+};
+
+/** Structure representing a probed network.
+ *
+ * This is returned from the net80211_probe_finish functions and
+ * passed to the low-level association functions. At least essid,
+ * bssid, channel, beacon, and security must be filled in if you want
+ * to build this structure manually.
+ */
+struct net80211_wlan
+{
+	/** The human-readable ESSID (network name)
+	 *
+	 * Although the 802.11 SSID field is generally not
+	 * NUL-terminated, the gPXE code adds an extra NUL (and
+	 * expects one in this structure) for convenience.
+	 */
+	char essid[IEEE80211_MAX_SSID_LEN+1];
+
+	/** MAC address of the strongest-signal access point for this ESSID */
+	u8 bssid[ETH_ALEN];
+
+	/** Signal strength of beacon frame from that access point */
+	int signal;
+
+	/** The channel on which that access point communicates
+	 *
+	 * This is a raw channel number (net80211_channel::channel_nr),
+	 * so that it will not be affected by reconfiguration of the
+	 * device channels array.
+	 */
+	int channel;
+
+	/** The complete beacon or probe-response frame received */
+	struct io_buffer *beacon;
+
+	/** Security handshaking method used on the network */
+	enum net80211_security_proto handshaking;
+
+	/** Cryptographic algorithm used on the network */
+	enum net80211_crypto_alg crypto;
+
+	/** Link to allow chaining multiple structures into a list to
+	    be returned from net80211_probe_finish_all(). */
+	struct list_head list;
+};
+
+
+/** 802.11 encryption key setting */
+extern struct setting net80211_key_setting __setting;
+
+
+/**
+ * @defgroup net80211_probe 802.11 network location API
+ * @{
+ */
+int net80211_prepare_probe ( struct net80211_device *dev, int band,
+			     int active );
+struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev,
+						   const char *essid,
+						   int active );
+int net80211_probe_step ( struct net80211_probe_ctx *ctx );
+struct net80211_wlan *
+net80211_probe_finish_best ( struct net80211_probe_ctx *ctx );
+struct list_head *net80211_probe_finish_all ( struct net80211_probe_ctx *ctx );
+
+void net80211_free_wlan ( struct net80211_wlan *wlan );
+void net80211_free_wlanlist ( struct list_head *list );
+/** @} */
+
+
+/**
+ * @defgroup net80211_mgmt 802.11 network management API
+ * @{
+ */
+struct net80211_device * net80211_get ( struct net_device *netdev );
+void net80211_autoassociate ( struct net80211_device *dev );
+
+int net80211_change_channel ( struct net80211_device *dev, int channel );
+void net80211_set_rate_idx ( struct net80211_device *dev, int rate );
+
+int net80211_keep_mgmt ( struct net80211_device *dev, int enable );
+struct io_buffer * net80211_mgmt_dequeue ( struct net80211_device *dev,
+					   int *signal );
+int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc,
+		       u8 bssid[ETH_ALEN], struct io_buffer *iob );
+/** @} */
+
+
+/**
+ * @defgroup net80211_assoc 802.11 network association API
+ * @{
+ */
+int net80211_prepare_assoc ( struct net80211_device *dev,
+			     struct net80211_wlan *wlan );
+int net80211_send_auth ( struct net80211_device *dev,
+			 struct net80211_wlan *wlan, int method );
+int net80211_send_assoc ( struct net80211_device *dev,
+			  struct net80211_wlan *wlan );
+void net80211_deauthenticate ( struct net80211_device *dev, int rc );
+/** @} */
+
+
+/**
+ * @defgroup net80211_driver 802.11 driver interface API
+ * @{
+ */
+struct net80211_device *net80211_alloc ( size_t priv_size );
+int net80211_register ( struct net80211_device *dev,
+			struct net80211_device_operations *ops,
+			struct net80211_hw_info *hw );
+u16 net80211_duration ( struct net80211_device *dev, int bytes, u16 rate );
+void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
+		   int signal, u16 rate );
+void net80211_rx_err ( struct net80211_device *dev,
+		       struct io_buffer *iob, int rc );
+void net80211_tx_complete ( struct net80211_device *dev,
+			    struct io_buffer *iob, int retries, int rc );
+void net80211_unregister ( struct net80211_device *dev );
+void net80211_free ( struct net80211_device *dev );
+/** @} */
+
+/**
+ * Calculate duration field for a CTS control frame
+ *
+ * @v dev	802.11 device
+ * @v size	Size of the packet being cleared to send
+ *
+ * A CTS control frame's duration field captures the frame being
+ * protected and its 10-byte ACK.
+ */
+static inline u16 net80211_cts_duration ( struct net80211_device *dev,
+					  int size )
+{
+	return ( net80211_duration ( dev, 10,
+				     dev->rates[dev->rtscts_rate] ) +
+		 net80211_duration ( dev, size, dev->rates[dev->rate] ) );
+}
+
+#endif
diff --git a/gpxe/src/include/gpxe/netdevice.h b/gpxe/src/include/gpxe/netdevice.h
index f1585de..858d8e9 100644
--- a/gpxe/src/include/gpxe/netdevice.h
+++ b/gpxe/src/include/gpxe/netdevice.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/list.h>
 #include <gpxe/tables.h>
@@ -19,11 +21,26 @@ struct net_protocol;
 struct ll_protocol;
 struct device;
 
-/** Maximum length of a link-layer address */
+/** Maximum length of a hardware address
+ *
+ * The longest currently-supported link-layer address is for IPoIB.
+ */
+#define MAX_HW_ADDR_LEN 8
+
+/** Maximum length of a link-layer address
+ *
+ * The longest currently-supported link-layer address is for IPoIB.
+ */
 #define MAX_LL_ADDR_LEN 20
 
-/** Maximum length of a link-layer header */
-#define MAX_LL_HEADER_LEN 6
+/** Maximum length of a link-layer header
+ *
+ * The longest currently-supported link-layer header is for 802.11: a
+ * 24-byte frame header plus an 8-byte 802.3 LLC/SNAP header.  (The
+ * IPoIB link-layer pseudo-header doesn't actually include link-layer
+ * addresses; see ipoib.c for details).
+ */
+#define MAX_LL_HEADER_LEN 32
 
 /** Maximum length of a network-layer address */
 #define MAX_NET_ADDR_LEN 4
@@ -78,30 +95,41 @@ struct ll_protocol {
 	/**
 	 * Add link-layer header
 	 *
+	 * @v netdev		Network device
 	 * @v iobuf		I/O buffer
 	 * @v ll_dest		Link-layer destination address
 	 * @v ll_source		Source link-layer address
 	 * @v net_proto		Network-layer protocol, in network-byte order
 	 * @ret rc		Return status code
 	 */
-	int ( * push ) ( struct io_buffer *iobuf, const void *ll_dest,
-			 const void *ll_source, uint16_t net_proto );
+	int ( * push ) ( struct net_device *netdev, struct io_buffer *iobuf,
+			 const void *ll_dest, const void *ll_source,
+			 uint16_t net_proto );
 	/**
 	 * Remove link-layer header
 	 *
+	 * @v netdev		Network device
 	 * @v iobuf		I/O buffer
 	 * @ret ll_dest		Link-layer destination address
 	 * @ret ll_source	Source link-layer address
 	 * @ret net_proto	Network-layer protocol, in network-byte order
 	 * @ret rc		Return status code
 	 */
-	int ( * pull ) ( struct io_buffer *iobuf, const void **ll_dest,
-			 const void **ll_source, uint16_t *net_proto );
+	int ( * pull ) ( struct net_device *netdev, struct io_buffer *iobuf,
+			 const void **ll_dest, const void **ll_source,
+			 uint16_t *net_proto );
+	/**
+	 * Initialise link-layer address
+	 *
+	 * @v hw_addr		Hardware address
+	 * @v ll_addr		Link-layer address to fill in
+	 */
+	void ( * init_addr ) ( const void *hw_addr, void *ll_addr );
 	/**
 	 * Transcribe link-layer address
 	 *
-	 * @v ll_addr	Link-layer address
-	 * @ret string	Human-readable transcription of address
+	 * @v ll_addr		Link-layer address
+	 * @ret string		Human-readable transcription of address
 	 *
 	 * This method should convert the link-layer address into a
 	 * human-readable format.
@@ -109,28 +137,35 @@ struct ll_protocol {
 	 * The buffer used to hold the transcription is statically
 	 * allocated.
 	 */
-	const char * ( * ntoa ) ( const void * ll_addr );
+	const char * ( * ntoa ) ( const void *ll_addr );
 	/**
 	 * Hash multicast address
 	 *
-	 * @v af	Address family
-	 * @v net_addr	Network-layer address
-	 * @v ll_addr	Link-layer address to fill in
-	 * @ret rc	Return status code
+	 * @v af		Address family
+	 * @v net_addr		Network-layer address
+	 * @v ll_addr		Link-layer address to fill in
+	 * @ret rc		Return status code
 	 */
 	int ( * mc_hash ) ( unsigned int af, const void *net_addr,
 			    void *ll_addr );
+	/**
+	 * Generate Ethernet-compatible compressed link-layer address
+	 *
+	 * @v ll_addr		Link-layer address
+	 * @v eth_addr		Ethernet-compatible address to fill in
+	 */
+	int ( * eth_addr ) ( const void *ll_addr, void *eth_addr );
 	/** Link-layer protocol
 	 *
 	 * This is an ARPHRD_XXX constant, in network byte order.
 	 */
 	uint16_t ll_proto;
+	/** Hardware address length */
+	uint8_t hw_addr_len;
 	/** Link-layer address length */
 	uint8_t ll_addr_len;
 	/** Link-layer header length */
 	uint8_t ll_header_len;
-	/** Link-layer broadcast address */
-	const uint8_t *ll_broadcast;
 };
 
 /** Network device operations */
@@ -241,17 +276,35 @@ struct net_device {
 
 	/** Link-layer protocol */
 	struct ll_protocol *ll_protocol;
+	/** Hardware address
+	 *
+	 * This is an address which is an intrinsic property of the
+	 * hardware, e.g. an address held in EEPROM.
+	 *
+	 * Note that the hardware address may not be the same length
+	 * as the link-layer address.
+	 */
+	uint8_t hw_addr[MAX_HW_ADDR_LEN];
 	/** Link-layer address
 	 *
-	 * For Ethernet, this is the MAC address.
+	 * This is the current link-layer address assigned to the
+	 * device.  It can be changed at runtime.
 	 */
 	uint8_t ll_addr[MAX_LL_ADDR_LEN];
+	/** Link-layer broadcast address */
+	const uint8_t *ll_broadcast;
 
 	/** Current device state
 	 *
 	 * This is the bitwise-OR of zero or more NETDEV_XXX constants.
 	 */
 	unsigned int state;
+	/** Link status code
+	 *
+	 * Zero indicates that the link is up; any other value
+	 * indicates the error preventing link-up.
+	 */
+	int link_rc;
 	/** Maximum packet length
 	 *
 	 * This length includes any link-layer headers.
@@ -267,7 +320,7 @@ struct net_device {
 	struct net_device_stats rx_stats;
 
 	/** Configuration settings applicable to this device */
-	struct simple_settings settings;
+	struct generic_settings settings;
 
 	/** Driver private data */
 	void *priv;
@@ -276,17 +329,21 @@ struct net_device {
 /** Network device is open */
 #define NETDEV_OPEN 0x0001
 
-/** Network device has link */
-#define NETDEV_LINK_UP 0x0002
+/** Link-layer protocol table */
+#define LL_PROTOCOLS __table ( struct ll_protocol, "ll_protocols" )
 
 /** Declare a link-layer protocol */
-#define __ll_protocol  __table ( struct ll_protocol, ll_protocols, 01 )
+#define __ll_protocol  __table_entry ( LL_PROTOCOLS, 01 )
+
+/** Network-layer protocol table */
+#define NET_PROTOCOLS __table ( struct net_protocol, "net_protocols" )
 
 /** Declare a network-layer protocol */
-#define __net_protocol __table ( struct net_protocol, net_protocols, 01 )
+#define __net_protocol __table_entry ( NET_PROTOCOLS, 01 )
 
 extern struct list_head net_devices;
 extern struct net_device_operations null_netdev_operations;
+extern struct settings_operations netdev_settings_operations;
 
 /**
  * Initialise a network device
@@ -312,12 +369,12 @@ static inline void netdev_nullify ( struct net_device *netdev ) {
 }
 
 /**
- * Get printable network device hardware address
+ * Get printable network device link-layer address
  *
  * @v netdev		Network device
- * @ret name		Hardware address
+ * @ret name		Link-layer address
  */
-static inline const char * netdev_hwaddr ( struct net_device *netdev ) {
+static inline const char * netdev_addr ( struct net_device *netdev ) {
 	return netdev->ll_protocol->ntoa ( netdev->ll_addr );
 }
 
@@ -378,23 +435,38 @@ netdev_settings ( struct net_device *netdev ) {
 }
 
 /**
+ * Initialise a per-netdevice configuration settings block
+ *
+ * @v generics		Generic settings block
+ * @v refcnt		Containing object reference counter, or NULL
+ * @v name		Settings block name
+ */
+static inline __attribute__ (( always_inline )) void
+netdev_settings_init ( struct net_device *netdev ) {
+	generic_settings_init ( &netdev->settings,
+				&netdev->refcnt, netdev->name );
+	netdev->settings.settings.op = &netdev_settings_operations;
+}
+
+/**
  * Mark network device as having link up
  *
  * @v netdev		Network device
  */
 static inline __attribute__ (( always_inline )) void
 netdev_link_up ( struct net_device *netdev ) {
-	netdev->state |= NETDEV_LINK_UP;
+	netdev->link_rc = 0;
 }
 
 /**
- * Mark network device as having link down
+ * Mark network device as having link down due to a specific error
  *
  * @v netdev		Network device
+ * @v rc		Link status code
  */
 static inline __attribute__ (( always_inline )) void
-netdev_link_down ( struct net_device *netdev ) {
-	netdev->state &= ~NETDEV_LINK_UP;
+netdev_link_err ( struct net_device *netdev, int rc ) {
+	netdev->link_rc = rc;
 }
 
 /**
@@ -405,9 +477,10 @@ netdev_link_down ( struct net_device *netdev ) {
  */
 static inline __attribute__ (( always_inline )) int
 netdev_link_ok ( struct net_device *netdev ) {
-	return ( netdev->state & NETDEV_LINK_UP );
+	return ( netdev->link_rc == 0 );
 }
 
+extern void netdev_link_down ( struct net_device *netdev );
 extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf );
 extern void netdev_tx_complete_err ( struct net_device *netdev,
 				 struct io_buffer *iobuf, int rc );
@@ -432,8 +505,6 @@ extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
 extern int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
 		    uint16_t net_proto, const void *ll_source );
 
-extern struct settings_operations netdev_settings_operations;
-
 /**
  * Complete network transmission
  *
diff --git a/gpxe/src/include/gpxe/null_nap.h b/gpxe/src/include/gpxe/null_nap.h
index 6dd0cda..0f46eaa 100644
--- a/gpxe/src/include/gpxe/null_nap.h
+++ b/gpxe/src/include/gpxe/null_nap.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifdef NAP_NULL
 #define NAP_PREFIX_null
 #else
diff --git a/gpxe/src/include/gpxe/nvo.h b/gpxe/src/include/gpxe/nvo.h
index 28068f4..c965070 100644
--- a/gpxe/src/include/gpxe/nvo.h
+++ b/gpxe/src/include/gpxe/nvo.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/dhcpopts.h>
 #include <gpxe/settings.h>
diff --git a/gpxe/src/include/gpxe/nvs.h b/gpxe/src/include/gpxe/nvs.h
index b026dd4..5c90c65 100644
--- a/gpxe/src/include/gpxe/nvs.h
+++ b/gpxe/src/include/gpxe/nvs.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /** A non-volatile storage device */
diff --git a/gpxe/src/include/gpxe/open.h b/gpxe/src/include/gpxe/open.h
index 81d5fc2..ebf754d 100644
--- a/gpxe/src/include/gpxe/open.h
+++ b/gpxe/src/include/gpxe/open.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdarg.h>
 #include <gpxe/tables.h>
 #include <gpxe/socket.h>
@@ -58,8 +60,11 @@ struct uri_opener {
 	int ( * open ) ( struct xfer_interface *xfer, struct uri *uri );
 };
 
+/** URI opener table */
+#define URI_OPENERS __table ( struct uri_opener, "uri_openers" )
+
 /** Register a URI opener */
-#define __uri_opener __table ( struct uri_opener, uri_openers, 01 )
+#define __uri_opener __table_entry ( URI_OPENERS, 01 )
 
 /** A socket opener */
 struct socket_opener {
@@ -78,8 +83,11 @@ struct socket_opener {
 			 struct sockaddr *local );
 };
 
+/** Socket opener table */
+#define SOCKET_OPENERS __table ( struct socket_opener, "socket_openers" )
+
 /** Register a socket opener */
-#define __socket_opener __table ( struct socket_opener, socket_openers, 01 )
+#define __socket_opener __table_entry ( SOCKET_OPENERS, 01 )
 
 extern int xfer_open_uri ( struct xfer_interface *xfer, struct uri *uri );
 extern int xfer_open_uri_string ( struct xfer_interface *xfer,
@@ -91,5 +99,7 @@ extern int xfer_open_socket ( struct xfer_interface *xfer, int semantics,
 			      struct sockaddr *peer, struct sockaddr *local );
 extern int xfer_vopen ( struct xfer_interface *xfer, int type, va_list args );
 extern int xfer_open ( struct xfer_interface *xfer, int type, ... );
+extern int xfer_vreopen ( struct xfer_interface *xfer, int type,
+			  va_list args );
 
 #endif /* _GPXE_OPEN_H */
diff --git a/gpxe/src/include/gpxe/pci.h b/gpxe/src/include/gpxe/pci.h
index 1ccdb10..8bcf1e0 100644
--- a/gpxe/src/include/gpxe/pci.h
+++ b/gpxe/src/include/gpxe/pci.h
@@ -16,6 +16,8 @@
  * your option) any later version.
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 #include <stdint.h>
 #include <gpxe/device.h>
 #include <gpxe/tables.h>
@@ -41,6 +43,7 @@
 #define  PCI_COMMAND_WAIT 	0x80	/* Enable address/data stepping */
 #define  PCI_COMMAND_SERR	0x100	/* Enable SERR */
 #define  PCI_COMMAND_FAST_BACK	0x200	/* Enable back-to-back writes */
+#define  PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */
 
 
 #define PCI_VENDOR_ID           0x00    /* 16 bits */
@@ -233,6 +236,33 @@
 #define PCI_MSI_DATA_32		8	/* 16 bits of data for 32-bit devices */
 #define PCI_MSI_DATA_64		12	/* 16 bits of data for 64-bit devices */
 
+/* Advanced Error Reporting */
+
+#define PCI_ERR_UNCOR_STATUS	4	/* Uncorrectable Error Status */
+#define  PCI_ERR_UNC_TRAIN	0x00000001	/* Training */
+#define  PCI_ERR_UNC_DLP	0x00000010	/* Data Link Protocol */
+#define  PCI_ERR_UNC_POISON_TLP	0x00001000	/* Poisoned TLP */
+#define  PCI_ERR_UNC_FCP	0x00002000	/* Flow Control Protocol */
+#define  PCI_ERR_UNC_COMP_TIME	0x00004000	/* Completion Timeout */
+#define  PCI_ERR_UNC_COMP_ABORT	0x00008000	/* Completer Abort */
+#define  PCI_ERR_UNC_UNX_COMP	0x00010000	/* Unexpected Completion */
+#define  PCI_ERR_UNC_RX_OVER	0x00020000	/* Receiver Overflow */
+#define  PCI_ERR_UNC_MALF_TLP	0x00040000	/* Malformed TLP */
+#define  PCI_ERR_UNC_ECRC	0x00080000	/* ECRC Error Status */
+#define  PCI_ERR_UNC_UNSUP	0x00100000	/* Unsupported Request */
+#define PCI_ERR_UNCOR_MASK	8	/* Uncorrectable Error Mask */
+	/* Same bits as above */
+#define PCI_ERR_UNCOR_SEVER	12	/* Uncorrectable Error Severity */
+	/* Same bits as above */
+#define PCI_ERR_COR_STATUS	16	/* Correctable Error Status */
+#define  PCI_ERR_COR_RCVR	0x00000001	/* Receiver Error Status */
+#define  PCI_ERR_COR_BAD_TLP	0x00000040	/* Bad TLP Status */
+#define  PCI_ERR_COR_BAD_DLLP	0x00000080	/* Bad DLLP Status */
+#define  PCI_ERR_COR_REP_ROLL	0x00000100	/* REPLAY_NUM Rollover */
+#define  PCI_ERR_COR_REP_TIMER	0x00001000	/* Replay Timer Timeout */
+#define PCI_ERR_COR_MASK	20	/* Correctable Error Mask */
+	/* Same bits as above */
+
 /** A PCI device ID list entry */
 struct pci_device_id {
 	/** Name */
@@ -241,6 +271,8 @@ struct pci_device_id {
 	uint16_t vendor;
 	/** PCI device ID */
 	uint16_t device;
+	/** Arbitrary driver data */
+	unsigned long driver_data;
 };
 
 /** Match-anything ID */
@@ -308,12 +340,16 @@ struct pci_driver {
 	void ( * remove ) ( struct pci_device *pci );
 };
 
+/** PCI driver table */
+#define PCI_DRIVERS __table ( struct pci_driver, "pci_drivers" )
+
 /** Declare a PCI driver */
-#define __pci_driver __table ( struct pci_driver, pci_drivers, 01 )
+#define __pci_driver __table_entry ( PCI_DRIVERS, 01 )
 
 #define PCI_DEVFN( slot, func )		( ( (slot) << 3 ) | (func) )
 #define PCI_SLOT( devfn )		( ( (devfn) >> 3 ) & 0x1f )
 #define PCI_FUNC( devfn )		( (devfn) & 0x07 )
+#define PCI_BUS( busdevfn )		( (busdevfn) >> 8 )
 #define PCI_BUSDEVFN( bus, devfn )	( ( (bus) << 8 ) | (devfn) )
 
 #define PCI_BASE_CLASS( class )		( (class) >> 16 )
@@ -324,12 +360,18 @@ struct pci_driver {
  * PCI_ROM is used to build up entries in a struct pci_id array.  It
  * is also parsed by parserom.pl to generate Makefile rules and files
  * for rom-o-matic.
+ *
+ * PCI_ID can be used to generate entries without creating a
+ * corresponding ROM in the build process.
  */
-#define PCI_ROM( _vendor, _device, _name, _description ) {	\
-	.vendor = _vendor,					\
-	.device = _device,					\
-	.name = _name,						\
+#define PCI_ID( _vendor, _device, _name, _description, _data ) {	\
+	.vendor = _vendor,						\
+	.device = _device,						\
+	.name = _name,							\
+	.driver_data = _data						\
 }
+#define PCI_ROM( _vendor, _device, _name, _description, _data ) \
+	PCI_ID( _vendor, _device, _name, _description, _data )
 
 extern void adjust_pci_device ( struct pci_device *pci );
 extern unsigned long pci_bar_start ( struct pci_device *pci,
diff --git a/gpxe/src/include/gpxe/pci_ids.h b/gpxe/src/include/gpxe/pci_ids.h
index 075ff96..4207013 100644
--- a/gpxe/src/include/gpxe/pci_ids.h
+++ b/gpxe/src/include/gpxe/pci_ids.h
@@ -7,6 +7,8 @@
  *	Please keep sorted.
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /* Device classes and subclasses */
 
 #define PCI_CLASS_NOT_DEFINED		0x0000
@@ -315,6 +317,7 @@
 #define PCI_VENDOR_ID_TIMEDIA		0x1409
 #define PCI_VENDOR_ID_OXSEMI		0x1415
 #define PCI_VENDOR_ID_AIRONET		0x14b9
+#define PCI_VENDOR_ID_MYRICOM		0x14c1
 #define PCI_VENDOR_ID_TITAN		0x14D2
 #define PCI_VENDOR_ID_PANACOM		0x14d4
 #define PCI_VENDOR_ID_BROADCOM		0x14e4
diff --git a/gpxe/src/include/gpxe/pci_io.h b/gpxe/src/include/gpxe/pci_io.h
index 365166c..8b2729a 100644
--- a/gpxe/src/include/gpxe/pci_io.h
+++ b/gpxe/src/include/gpxe/pci_io.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/api.h>
 #include <config/ioapi.h>
diff --git a/gpxe/src/include/gpxe/pcibackup.h b/gpxe/src/include/gpxe/pcibackup.h
new file mode 100644
index 0000000..3d295c0
--- /dev/null
+++ b/gpxe/src/include/gpxe/pcibackup.h
@@ -0,0 +1,33 @@
+#ifndef _GPXE_PCIBACKUP_H
+#define _GPXE_PCIBACKUP_H
+
+/** @file
+ *
+ * PCI configuration space backup and restoration
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+
+/** A PCI configuration space backup */
+struct pci_config_backup {
+	uint32_t dwords[64];
+};
+
+/** PCI configuration space backup exclusion list end marker */
+#define PCI_CONFIG_BACKUP_EXCLUDE_END 0xff
+
+/** Define a PCI configuration space backup exclusion list */
+#define PCI_CONFIG_BACKUP_EXCLUDE(...) \
+	{ __VA_ARGS__, PCI_CONFIG_BACKUP_EXCLUDE_END }
+
+extern void pci_backup ( struct pci_device *pci,
+			 struct pci_config_backup *backup,
+			 const uint8_t *exclude );
+extern void pci_restore ( struct pci_device *pci,
+			  struct pci_config_backup *backup,
+			  const uint8_t *exclude );
+
+#endif /* _GPXE_PCIBACKUP_H */
diff --git a/gpxe/src/include/gpxe/posix_io.h b/gpxe/src/include/gpxe/posix_io.h
index 9984db0..3063dff 100644
--- a/gpxe/src/include/gpxe/posix_io.h
+++ b/gpxe/src/include/gpxe/posix_io.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/uaccess.h>
 
diff --git a/gpxe/src/include/gpxe/process.h b/gpxe/src/include/gpxe/process.h
index 8d9b109..944858d 100644
--- a/gpxe/src/include/gpxe/process.h
+++ b/gpxe/src/include/gpxe/process.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/list.h>
 #include <gpxe/refcnt.h>
 #include <gpxe/tables.h>
@@ -45,6 +47,7 @@ static inline __attribute__ (( always_inline )) void
 process_init_stopped ( struct process *process,
 		       void ( * step ) ( struct process *process ),
 		       struct refcnt *refcnt ) {
+	INIT_LIST_HEAD ( &process->list );
 	process->step = step;
 	process->refcnt = refcnt;
 }
@@ -63,13 +66,15 @@ process_init ( struct process *process,
 	process_add ( process );
 }
 
+/** Permanent process table */
+#define PERMANENT_PROCESSES __table ( struct process, "processes" )
+
 /**
  * Declare a permanent process
  *
  * Permanent processes will be automatically added to the process list
  * at initialisation time.
  */
-#define __permanent_process \
-	__table ( struct process, processes, 01 )
+#define __permanent_process __table_entry ( PERMANENT_PROCESSES, 01 )
 
 #endif /* _GPXE_PROCESS_H */
diff --git a/gpxe/src/include/gpxe/profile.h b/gpxe/src/include/gpxe/profile.h
index d46ca05..a5bdd3a 100644
--- a/gpxe/src/include/gpxe/profile.h
+++ b/gpxe/src/include/gpxe/profile.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /**
diff --git a/gpxe/src/include/gpxe/ramdisk.h b/gpxe/src/include/gpxe/ramdisk.h
index 4a77f05..31a1d99 100644
--- a/gpxe/src/include/gpxe/ramdisk.h
+++ b/gpxe/src/include/gpxe/ramdisk.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/uaccess.h>
 #include <gpxe/blockdev.h>
 
diff --git a/gpxe/src/include/gpxe/rarp.h b/gpxe/src/include/gpxe/rarp.h
index 81e03bd..7ade831 100644
--- a/gpxe/src/include/gpxe/rarp.h
+++ b/gpxe/src/include/gpxe/rarp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct net_protocol;
 
 extern struct net_protocol rarp_protocol;
diff --git a/gpxe/src/include/gpxe/rc80211.h b/gpxe/src/include/gpxe/rc80211.h
new file mode 100644
index 0000000..0856896
--- /dev/null
+++ b/gpxe/src/include/gpxe/rc80211.h
@@ -0,0 +1,19 @@
+#ifndef _GPXE_RC80211_H
+#define _GPXE_RC80211_H
+
+/** @file
+ *
+ * Rate-control algorithm prototype for 802.11.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct net80211_device;
+struct rc80211_ctx;
+
+struct rc80211_ctx * rc80211_init ( struct net80211_device *dev );
+void rc80211_update_tx ( struct net80211_device *dev, int retries, int rc );
+void rc80211_update_rx ( struct net80211_device *dev, int retry, u16 rate );
+void rc80211_free ( struct rc80211_ctx *ctx );
+
+#endif /* _GPXE_RC80211_H */
diff --git a/gpxe/src/include/gpxe/refcnt.h b/gpxe/src/include/gpxe/refcnt.h
index 68e0fd4..e56f1d3 100644
--- a/gpxe/src/include/gpxe/refcnt.h
+++ b/gpxe/src/include/gpxe/refcnt.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * A reference counter
  *
diff --git a/gpxe/src/include/gpxe/resolv.h b/gpxe/src/include/gpxe/resolv.h
index e73c820..33bb098 100644
--- a/gpxe/src/include/gpxe/resolv.h
+++ b/gpxe/src/include/gpxe/resolv.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/refcnt.h>
 #include <gpxe/interface.h>
 #include <gpxe/tables.h>
@@ -149,9 +151,11 @@ struct resolver {
 /** Normal resolver priority */
 #define RESOLV_NORMAL 02
 
+/** Resolvers table */
+#define RESOLVERS __table ( struct resolver, "resolvers" )
+
 /** Register as a name resolver */
-#define __resolver( resolv_order ) \
-	__table ( struct resolver, resolvers, resolv_order )
+#define __resolver( resolv_order ) __table_entry ( RESOLVERS, resolv_order )
 
 extern void resolv_done ( struct resolv_interface *resolv,
 			  struct sockaddr *sa, int rc );
diff --git a/gpxe/src/include/gpxe/retry.h b/gpxe/src/include/gpxe/retry.h
index 1e7fde4..ada0204 100644
--- a/gpxe/src/include/gpxe/retry.h
+++ b/gpxe/src/include/gpxe/retry.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/list.h>
 
 /** Default timeout value */
diff --git a/gpxe/src/include/gpxe/rotate.h b/gpxe/src/include/gpxe/rotate.h
index 42ec719..0371c57 100644
--- a/gpxe/src/include/gpxe/rotate.h
+++ b/gpxe/src/include/gpxe/rotate.h
@@ -6,6 +6,8 @@
  * Bit operations
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 static inline uint32_t rol32 ( uint32_t data, unsigned int rotation ) {
diff --git a/gpxe/src/include/gpxe/rsa.h b/gpxe/src/include/gpxe/rsa.h
index e30e1a5..5052ad4 100644
--- a/gpxe/src/include/gpxe/rsa.h
+++ b/gpxe/src/include/gpxe/rsa.h
@@ -1,6 +1,8 @@
 #ifndef _GPXE_RSA_H
 #define _GPXE_RSA_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct pubkey_algorithm;
 
 extern struct pubkey_algorithm rsa_algorithm;
diff --git a/gpxe/src/include/gpxe/sanboot.h b/gpxe/src/include/gpxe/sanboot.h
index ea26a35..fd06316 100644
--- a/gpxe/src/include/gpxe/sanboot.h
+++ b/gpxe/src/include/gpxe/sanboot.h
@@ -1,6 +1,8 @@
 #ifndef _GPXE_SANBOOT_H
 #define _GPXE_SANBOOT_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/tables.h>
 
 struct sanboot_protocol {
@@ -8,7 +10,11 @@ struct sanboot_protocol {
 	int ( * boot ) ( const char *root_path );
 };
 
-#define __sanboot_protocol \
-	__table ( struct sanboot_protocol, sanboot_protocols, 01 )
+#define SANBOOT_PROTOCOLS \
+	__table ( struct sanboot_protocol, "sanboot_protocols" )
+
+#define __sanboot_protocol __table_entry ( SANBOOT_PROTOCOLS, 01 )
+
+extern int keep_san ( void );
 
 #endif /* _GPXE_SANBOOT_H */
diff --git a/gpxe/src/include/gpxe/scsi.h b/gpxe/src/include/gpxe/scsi.h
index e820117..9741697 100644
--- a/gpxe/src/include/gpxe/scsi.h
+++ b/gpxe/src/include/gpxe/scsi.h
@@ -12,6 +12,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @defgroup scsiops SCSI operation codes
  * @{
@@ -234,18 +236,23 @@ struct scsi_command {
 	uint8_t status;
 	/** SCSI sense response code */
 	uint8_t sense_response;
+	/** Command status code */
+	int rc;
 };
 
+/** A SCSI LUN
+ *
+ * This is a four-level LUN as specified by SAM-2, in big-endian
+ * order.
+ */
+struct scsi_lun {
+	uint16_t u16[4];
+}  __attribute__ (( packed ));
+
 /** A SCSI device */
 struct scsi_device {
 	/** Block device interface */
 	struct block_device blockdev;
-	/** Logical unit number (LUN)
-	 *
-	 * This is a four-level LUN as specified by SAM-2, in
-	 * big-endian order.
-	 */
-	uint64_t lun;
 	/**
 	 * Issue SCSI command
 	 *
@@ -254,10 +261,11 @@ struct scsi_device {
 	 * @ret rc		Return status code
 	 *
 	 * Note that a successful return status code indicates only
-	 * that the SCSI command completed.  The caller must check the
-	 * status field in the command structure to see if, for
-	 * example, the device returned CHECK CONDITION or some other
-	 * non-success status code.
+	 * that the SCSI command was issued.  The caller must check
+	 * the status field in the command structure to see when the
+	 * command completes and whether, for example, the device
+	 * returned CHECK CONDITION or some other non-success status
+	 * code.
 	 */
 	int ( * command ) ( struct scsi_device *scsi,
 			    struct scsi_command *command );
@@ -265,6 +273,9 @@ struct scsi_device {
 	struct refcnt *backend;
 };
 
+extern int scsi_detached_command ( struct scsi_device *scsi,
+				   struct scsi_command *command );
 extern int init_scsidev ( struct scsi_device *scsi );
+extern int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun );
 
 #endif /* _GPXE_SCSI_H */
diff --git a/gpxe/src/include/gpxe/sec80211.h b/gpxe/src/include/gpxe/sec80211.h
new file mode 100644
index 0000000..502ebf7
--- /dev/null
+++ b/gpxe/src/include/gpxe/sec80211.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _GPXE_SEC80211_H
+#define _GPXE_SEC80211_H
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/net80211.h>
+#include <errno.h>
+
+/** @file
+ *
+ * Definitions for general secured-network routines.
+ *
+ * Any function in this file which may be referenced by code which is
+ * not exclusive to encryption-enabled builds (e.g. sec80211_detect(),
+ * which is called by net80211_probe_step() to fill the net80211_wlan
+ * structure's security fields) must be declared as a weak symbol,
+ * using an inline interface similar to that used for
+ * sec80211_detect() below. This prevents secure network support from
+ * bloating general builds by any more than a few tiny hooks to call
+ * crypto functions when crypto structures are non-NULL.
+ */
+
+int _sec80211_detect ( struct io_buffer *iob,
+		       enum net80211_security_proto *secprot,
+		       enum net80211_crypto_alg *crypt )
+	__attribute__ (( weak ));
+
+
+/**
+ * Inline safety wrapper for _sec80211_detect()
+ *
+ * @v iob	I/O buffer containing beacon frame
+ * @ret secprot	Security handshaking protocol used by network
+ * @ret crypt	Cryptosystem used by network
+ * @ret rc	Return status code
+ *
+ * This function transparently calls _sec80211_detect() if the file
+ * containing it was compiled in, or returns an error indication of
+ * @c -ENOTSUP if not.
+ */
+static inline int sec80211_detect ( struct io_buffer *iob,
+				    enum net80211_security_proto *secprot,
+				    enum net80211_crypto_alg *crypt ) {
+	if ( _sec80211_detect )
+		return _sec80211_detect ( iob, secprot, crypt );
+	return -ENOTSUP;
+}
+
+int sec80211_detect_ie ( int is_rsn, u8 *start, u8 *end,
+			 enum net80211_security_proto *secprot,
+			 enum net80211_crypto_alg *crypt );
+u8 * sec80211_find_rsn ( union ieee80211_ie *ie, void *ie_end,
+			 int *is_rsn, u8 **end );
+
+int sec80211_install ( struct net80211_crypto **which,
+		       enum net80211_crypto_alg crypt,
+		       const void *key, int len, const void *rsc );
+
+u32 sec80211_rsn_get_crypto_desc ( enum net80211_crypto_alg crypt, int rsnie );
+u32 sec80211_rsn_get_akm_desc ( enum net80211_security_proto secprot,
+				int rsnie );
+enum net80211_crypto_alg sec80211_rsn_get_net80211_crypt ( u32 desc );
+
+#endif /* _GPXE_SEC80211_H */
+
diff --git a/gpxe/src/include/gpxe/segment.h b/gpxe/src/include/gpxe/segment.h
index 5ab9169..5b59c54 100644
--- a/gpxe/src/include/gpxe/segment.h
+++ b/gpxe/src/include/gpxe/segment.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/uaccess.h>
 
 extern int prep_segment ( userptr_t segment, size_t filesz, size_t memsz );
diff --git a/gpxe/src/include/gpxe/serial.h b/gpxe/src/include/gpxe/serial.h
index 2825b93..a72ca7e 100644
--- a/gpxe/src/include/gpxe/serial.h
+++ b/gpxe/src/include/gpxe/serial.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern void serial_putc ( int ch );
 extern int serial_getc ( void );
 extern int serial_ischar ( void );
diff --git a/gpxe/src/include/gpxe/settings.h b/gpxe/src/include/gpxe/settings.h
index 9e62cde..efefe73 100644
--- a/gpxe/src/include/gpxe/settings.h
+++ b/gpxe/src/include/gpxe/settings.h
@@ -7,11 +7,12 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/tables.h>
 #include <gpxe/list.h>
 #include <gpxe/refcnt.h>
-#include <gpxe/dhcpopts.h>
 
 struct settings;
 struct in_addr;
@@ -36,8 +37,11 @@ struct setting {
 	unsigned int tag;
 };
 
+/** Configuration setting table */
+#define SETTINGS __table ( struct setting, "settings" )
+
 /** Declare a configuration setting */
-#define	__setting __table ( struct setting, settings, 01 )
+#define __setting __table_entry ( SETTINGS, 01 )
 
 /** Settings block operations */
 struct settings_operations {
@@ -64,6 +68,11 @@ struct settings_operations {
 	 */
 	int ( * fetch ) ( struct settings *settings, struct setting *setting,
 			  void *data, size_t len );
+	/** Clear settings block
+	 *
+	 * @v settings		Settings block
+	 */
+	void ( * clear ) ( struct settings *settings );
 };
 
 /** A settings block */
@@ -123,9 +132,11 @@ struct setting_type {
 			   char *buf, size_t len );
 };
 
+/** Configuration setting type table */
+#define SETTING_TYPES __table ( struct setting_type, "setting_types" )
+
 /** Declare a configuration setting type */
-#define	__setting_type \
-	__table ( struct setting_type, setting_types, 01 )
+#define __setting_type __table_entry ( SETTING_TYPES, 01 )
 
 /**
  * A settings applicator
@@ -139,28 +150,32 @@ struct settings_applicator {
 	int ( * apply ) ( void );
 };
 
+/** Settings applicator table */
+#define SETTINGS_APPLICATORS \
+	__table ( struct settings_applicator, "settings_applicators" )
+
 /** Declare a settings applicator */
-#define __settings_applicator \
-	__table ( struct settings_applicator, settings_applicators, 01 )
+#define __settings_applicator __table_entry ( SETTINGS_APPLICATORS, 01 )
 
 /**
- * A simple settings block
+ * A generic settings block
  *
  */
-struct simple_settings {
+struct generic_settings {
 	/** Settings block */
 	struct settings settings;
-	/** DHCP options */
-	struct dhcp_options dhcpopts;
+	/** List of generic settings */
+	struct list_head list;
 };
 
-extern struct settings_operations simple_settings_operations;
-extern int simple_settings_store ( struct settings *settings,
-				   struct setting *setting,
-				   const void *data, size_t len );
-extern int simple_settings_fetch ( struct settings *settings,
-				   struct setting *setting,
-				   void *data, size_t len );
+extern struct settings_operations generic_settings_operations;
+extern int generic_settings_store ( struct settings *settings,
+				    struct setting *setting,
+				    const void *data, size_t len );
+extern int generic_settings_fetch ( struct settings *settings,
+				    struct setting *setting,
+				    void *data, size_t len );
+extern void generic_settings_clear ( struct settings *settings );
 
 extern int register_settings ( struct settings *settings,
 			       struct settings *parent );
@@ -191,10 +206,9 @@ extern unsigned long fetch_uintz_setting ( struct settings *settings,
 					   struct setting *setting );
 extern int fetch_uuid_setting ( struct settings *settings,
 				struct setting *setting, union uuid *uuid );
+extern void clear_settings ( struct settings *settings );
 extern int setting_cmp ( struct setting *a, struct setting *b );
 
-extern struct settings * find_child_settings ( struct settings *parent,
-					       const char *name );
 extern struct settings * find_settings ( const char *name );
 
 extern int storef_setting ( struct settings *settings,
@@ -228,6 +242,7 @@ extern struct setting priority_setting __setting;
 extern struct setting uuid_setting __setting;
 extern struct setting next_server_setting __setting;
 extern struct setting mac_setting __setting;
+extern struct setting busid_setting __setting;
 extern struct setting user_class_setting __setting;
 
 /**
@@ -255,15 +270,16 @@ static inline void settings_init ( struct settings *settings,
 /**
  * Initialise a settings block
  *
- * @v simple		Simple settings block
+ * @v generics		Generic settings block
  * @v refcnt		Containing object reference counter, or NULL
  * @v name		Settings block name
  */
-static inline void simple_settings_init ( struct simple_settings *simple,
-					  struct refcnt *refcnt,
-					  const char *name ) {
-	settings_init ( &simple->settings, &simple_settings_operations,
+static inline void generic_settings_init ( struct generic_settings *generics,
+					   struct refcnt *refcnt,
+					   const char *name ) {
+	settings_init ( &generics->settings, &generic_settings_operations,
 			refcnt, name, 0 );
+	INIT_LIST_HEAD ( &generics->list );
 }
 
 /**
diff --git a/gpxe/src/include/gpxe/settings_ui.h b/gpxe/src/include/gpxe/settings_ui.h
index 48548fd..a82d733 100644
--- a/gpxe/src/include/gpxe/settings_ui.h
+++ b/gpxe/src/include/gpxe/settings_ui.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct settings;
 
 extern int settings_ui ( struct settings *settings ) __nonnull;
diff --git a/gpxe/src/include/gpxe/sha1.h b/gpxe/src/include/gpxe/sha1.h
index 66370d4..c203d99 100644
--- a/gpxe/src/include/gpxe/sha1.h
+++ b/gpxe/src/include/gpxe/sha1.h
@@ -1,6 +1,8 @@
 #ifndef _GPXE_SHA1_H
 #define _GPXE_SHA1_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include "crypto/axtls/crypto.h"
 
 struct digest_algorithm;
@@ -10,4 +12,13 @@ struct digest_algorithm;
 
 extern struct digest_algorithm sha1_algorithm;
 
+/* SHA1-wrapping functions defined in sha1extra.c: */
+
+void prf_sha1 ( const void *key, size_t key_len, const char *label,
+		const void *data, size_t data_len, void *prf, size_t prf_len );
+
+void pbkdf2_sha1 ( const void *passphrase, size_t pass_len,
+		   const void *salt, size_t salt_len,
+		   int iterations, void *key, size_t key_len );
+
 #endif /* _GPXE_SHA1_H */
diff --git a/gpxe/src/include/gpxe/shell.h b/gpxe/src/include/gpxe/shell.h
index c353fc4..a65a344 100644
--- a/gpxe/src/include/gpxe/shell.h
+++ b/gpxe/src/include/gpxe/shell.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern void shell ( void );
 
 #endif /* _GPXE_SHELL_H */
diff --git a/gpxe/src/include/gpxe/shell_banner.h b/gpxe/src/include/gpxe/shell_banner.h
index f8e92a4..28482be 100644
--- a/gpxe/src/include/gpxe/shell_banner.h
+++ b/gpxe/src/include/gpxe/shell_banner.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern int shell_banner ( void );
 
 #endif /* _GPXE_SHELL_BANNER_H */
diff --git a/gpxe/src/include/gpxe/smbios.h b/gpxe/src/include/gpxe/smbios.h
index 2b0fcbd..4df25c3 100644
--- a/gpxe/src/include/gpxe/smbios.h
+++ b/gpxe/src/include/gpxe/smbios.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/api.h>
 #include <config/general.h>
@@ -113,6 +115,25 @@ struct smbios_system_information {
 /** SMBIOS system information structure type */
 #define SMBIOS_TYPE_SYSTEM_INFORMATION 1
 
+/** SMBIOS enclosure information structure */
+struct smbios_enclosure_information {
+	/** SMBIOS structure header */
+	struct smbios_header header;
+	/** Manufacturer string */
+	uint8_t manufacturer;
+	/** Type string */
+	uint8_t type;
+	/** Version string */
+	uint8_t version;
+	/** Serial number string */
+	uint8_t serial;
+	/** Asset tag */
+	uint8_t asset_tag;
+} __attribute__ (( packed ));
+
+/** SMBIOS enclosure information structure type */
+#define SMBIOS_TYPE_ENCLOSURE_INFORMATION 3
+
 /**
  * SMBIOS entry point descriptor
  *
diff --git a/gpxe/src/include/gpxe/socket.h b/gpxe/src/include/gpxe/socket.h
index b683bed..9ea0db9 100644
--- a/gpxe/src/include/gpxe/socket.h
+++ b/gpxe/src/include/gpxe/socket.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /**
diff --git a/gpxe/src/include/gpxe/spi.h b/gpxe/src/include/gpxe/spi.h
index 8f90e3d..8e4a676 100644
--- a/gpxe/src/include/gpxe/spi.h
+++ b/gpxe/src/include/gpxe/spi.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/nvs.h>
 
 /**
@@ -102,6 +104,14 @@ struct spi_device {
 	unsigned int munge_address : 1;
 };
 
+/**
+ * SPI magic autodetection address length
+ *
+ * Set @c spi_device::address_len to @c SPI_AUTODETECT_ADDRESS_LEN if
+ * the address length should be autodetected.
+ */
+#define SPI_AUTODETECT_ADDRESS_LEN 0
+
 static inline __attribute__ (( always_inline )) struct spi_device *
 nvs_to_spi ( struct nvs_device *nvs ) {
 	return container_of ( nvs, struct spi_device, nvs );
diff --git a/gpxe/src/include/gpxe/spi_bit.h b/gpxe/src/include/gpxe/spi_bit.h
index ced85ce..8bd2519 100644
--- a/gpxe/src/include/gpxe/spi_bit.h
+++ b/gpxe/src/include/gpxe/spi_bit.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/spi.h>
 #include <gpxe/bitbash.h>
 
diff --git a/gpxe/src/include/gpxe/srp.h b/gpxe/src/include/gpxe/srp.h
new file mode 100644
index 0000000..85f39b9
--- /dev/null
+++ b/gpxe/src/include/gpxe/srp.h
@@ -0,0 +1,868 @@
+#ifndef _GPXE_SRP_H
+#define _GPXE_SRP_H
+
+/** @file
+ *
+ * SCSI RDMA Protocol
+ *
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdint.h>
+#include <byteswap.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/xfer.h>
+#include <gpxe/scsi.h>
+
+/*****************************************************************************
+ *
+ * Common fields
+ *
+ *****************************************************************************
+ */
+
+/** An SRP information unit tag */
+struct srp_tag {
+	uint32_t dwords[2];
+} __attribute__ (( packed ));
+
+/** An SRP port ID */
+struct srp_port_id {
+	uint8_t bytes[16];
+} __attribute__ (( packed ));
+
+/** An SRP port ID pair */
+struct srp_port_ids {
+	/** Initiator port ID */
+	struct srp_port_id initiator;
+	/** Target port ID */
+	struct srp_port_id target;
+} __attribute__ (( packed ));
+
+/** SRP information unit common fields */
+struct srp_common {
+	/** Information unit type */
+	uint8_t type;
+	/** Reserved */
+	uint8_t reserved0[7];
+	/** Tag */
+	struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/*****************************************************************************
+ *
+ * Login request
+ *
+ *****************************************************************************
+ */
+
+/** An SRP login request information unit */
+struct srp_login_req {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_LOGIN_REQ
+	 */
+	uint8_t type;
+	/** Reserved */
+	uint8_t reserved0[7];
+	/** Tag */
+	struct srp_tag tag;
+	/** Requested maximum initiator to target IU length */
+	uint32_t max_i_t_iu_len;
+	/** Reserved */
+	uint8_t reserved1[4];
+	/** Required buffer formats
+	 *
+	 * This is the bitwise OR of one or more @c
+	 * SRP_LOGIN_REQ_FMT_XXX constants.
+	 */
+	uint16_t required_buffer_formats;
+	/** Flags
+	 *
+	 * This is the bitwise OR of zero or more @c
+	 * SRP_LOGIN_REQ_FLAG_XXX and @c SRP_LOGIN_REQ_MCA_XXX
+	 * constants.
+	 */
+	uint8_t flags;
+	/** Reserved */
+	uint8_t reserved2[5];
+	/** Initiator and target port identifiers */
+	struct srp_port_ids port_ids;
+} __attribute__ (( packed ));
+
+/** Type of an SRP login request */
+#define SRP_LOGIN_REQ 0x00
+
+/** Require indirect data buffer descriptor format */
+#define SRP_LOGIN_REQ_FMT_IDBD 0x04
+
+/** Require direct data buffer descriptor format */
+#define SRP_LOGIN_REQ_FMT_DDBD 0x02
+
+/** Use solicited notification for asynchronous events */
+#define SRP_LOGIN_REQ_FLAG_AESOLNT 0x40
+
+/** Use solicited notification for credit request */
+#define SRP_LOGIN_REQ_FLAG_CRSOLNT 0x20
+
+/** Use solicited notification for logouts */
+#define SRP_LOGIN_REQ_FLAG_LOSOLNT 0x10
+
+/** Multi-channel action mask */
+#define SRP_LOGIN_REQ_MCA_MASK 0x03
+
+/** Single RDMA channel operation */
+#define SRP_LOGIN_REQ_MCA_SINGLE_CHANNEL 0x00
+
+/** Multiple independent RDMA channel operation */
+#define SRP_LOGIN_REQ_MCA_MULTIPLE_CHANNELS 0x01
+
+/*****************************************************************************
+ *
+ * Login response
+ *
+ *****************************************************************************
+ */
+
+/** An SRP login response */
+struct srp_login_rsp {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_LOGIN_RSP
+	 */
+	uint8_t type;
+	/** Reserved */
+	uint8_t reserved0[3];
+	/** Request limit delta */
+	uint32_t request_limit_delta;
+	/** Tag */
+	struct srp_tag tag;
+	/** Maximum initiator to target IU length */
+	uint32_t max_i_t_iu_len;
+	/** Maximum target to initiator IU length */
+	uint32_t max_t_i_iu_len;
+	/** Supported buffer formats
+	 *
+	 * This is the bitwise OR of one or more @c
+	 * SRP_LOGIN_RSP_FMT_XXX constants.
+	 */
+	uint16_t supported_buffer_formats;
+	/** Flags
+	 *
+	 * This is the bitwise OR of zero or more @c
+	 * SRP_LOGIN_RSP_FLAG_XXX and @c SRP_LOGIN_RSP_MCR_XXX
+	 * constants.
+	 */
+	uint8_t flags;
+	/** Reserved */
+	uint8_t reserved1[25];
+} __attribute__ (( packed ));
+
+/** Type of an SRP login response */
+#define SRP_LOGIN_RSP 0xc0
+
+/** Indirect data buffer descriptor format supported */
+#define SRP_LOGIN_RSP_FMT_IDBD 0x04
+
+/** Direct data buffer descriptor format supported */
+#define SRP_LOGIN_RSP_FMT_DDBD 0x02
+
+/** Solicited notification is supported */
+#define SRP_LOGIN_RSP_FLAG_SOLNTSUP 0x10
+
+/** Multi-channel result mask */
+#define SRP_LOGIN_RSP_MCR_MASK 0x03
+
+/** No existing RDMA channels were associated with the same I_T nexus */
+#define SRP_LOGIN_RSP_MCR_NO_EXISTING_CHANNELS 0x00
+
+/** One or more existing RDMA channels were terminated */
+#define SRP_LOGIN_RSP_MCR_EXISTING_CHANNELS_TERMINATED 0x01
+
+/** One or more existing RDMA channels continue to operate independently */
+#define SRP_LOGIN_RSP_MCR_EXISTING_CHANNELS_CONTINUE 0x02
+
+/*****************************************************************************
+ *
+ * Login rejection
+ *
+ *****************************************************************************
+ */
+
+/** An SRP login rejection */
+struct srp_login_rej {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_LOGIN_REJ
+	 */
+	uint8_t type;
+	/** Reserved */
+	uint8_t reserved0[3];
+	/** Reason
+	 *
+	 * This is a @c SRP_LOGIN_REJ_REASON_XXX constant.
+	 */
+	uint32_t reason;
+	/** Tag */
+	struct srp_tag tag;
+	/** Reserved */
+	uint8_t reserved1[8];
+	/** Supported buffer formats
+	 *
+	 * This is the bitwise OR of one or more @c
+	 * SRP_LOGIN_REJ_FMT_XXX constants.
+	 */
+	uint16_t supported_buffer_formats;
+	/** Reserved */
+	uint8_t reserved2[6];
+} __attribute__ (( packed ));
+
+/** Type of an SRP login rejection */
+#define SRP_LOGIN_REJ 0xc2
+
+/** Unable to establish RDMA channel, no reason specified */
+#define SRP_LOGIN_REJ_REASON_UNKNOWN 0x00010000UL
+
+/** Insufficient RDMA channel resources */
+#define SRP_LOGIN_REJ_REASON_INSUFFICIENT_RESOURCES 0x00010001UL
+
+/** Requested maximum initiator to target IU length value too large */
+#define SRP_LOGIN_REJ_REASON_BAD_MAX_I_T_IU_LEN 0x00010002UL
+
+/** Unable to associate RDMA channel with specified I_T nexus */
+#define SRP_LOGIN_REJ_REASON_CANNOT_ASSOCIATE 0x00010003UL
+
+/** One or more requested data buffer descriptor formats are not supported */
+#define SRP_LOGIN_REJ_REASON_UNSUPPORTED_BUFFER_FORMAT 0x00010004UL
+
+/** SRP target port does not support multiple RDMA channels per I_T nexus */
+#define SRP_LOGIN_REJ_REASON_NO_MULTIPLE_CHANNELS 0x00010005UL
+
+/** RDMA channel limit reached for this initiator */
+#define SRP_LOGIN_REJ_REASON_NO_MORE_CHANNELS 0x00010006UL
+
+/** Indirect data buffer descriptor format supported */
+#define SRP_LOGIN_REJ_FMT_IDBD 0x04
+
+/** Direct data buffer descriptor format supported */
+#define SRP_LOGIN_REJ_FMT_DDBD 0x02
+
+/*****************************************************************************
+ *
+ * Initiator logout
+ *
+ *****************************************************************************
+ */
+
+/** An SRP initiator logout request */
+struct srp_i_logout {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_I_LOGOUT
+	 */
+	uint8_t type;
+	/** Reserved */
+	uint8_t reserved0[7];
+	/** Tag */
+	struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP initiator logout request */
+#define SRP_I_LOGOUT 0x03
+
+/*****************************************************************************
+ *
+ * Target logout
+ *
+ *****************************************************************************
+ */
+
+/** An SRP target logout request */
+struct srp_t_logout {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_T_LOGOUT
+	 */
+	uint8_t type;
+	/** Flags
+	 *
+	 * This is the bitwise OR of zero or more @c
+	 * SRP_T_LOGOUT_FLAG_XXX constants.
+	 */
+	uint8_t flags;
+	/** Reserved */
+	uint8_t reserved0[2];
+	/** Reason
+	 *
+	 * This is a @c SRP_T_LOGOUT_REASON_XXX constant.
+	 */
+	uint32_t reason;
+	/** Tag */
+	struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP target logout request */
+#define SRP_T_LOGOUT 0x80
+
+/** The initiator specified solicited notification of logouts */
+#define SRP_T_LOGOUT_FLAG_SOLNT 0x01
+
+/** No reason specified */
+#define SRP_T_LOGOUT_REASON_UNKNOWN 0x00000000UL
+
+/** Inactive RDMA channel (reclaiming resources) */
+#define SRP_T_LOGOUT_REASON_INACTIVE 0x00000001UL
+
+/** Invalid information unit type code received by SRP target port */
+#define SRP_T_LOGOUT_REASON_INVALID_TYPE 0x00000002UL
+
+/** SRP initiator port sent response with no corresponding request */
+#define SRP_T_LOGOUT_REASON_SPURIOUS_RESPONSE 0x00000003UL
+
+/** RDMA channel disconnected due to multi-channel action code in new login */
+#define SRP_T_LOGOUT_REASON_MCA 0x00000004UL
+
+/** Unsuppported format code value specified in data-out buffer descriptor */
+#define SRP_T_LOGOUT_UNSUPPORTED_DATA_OUT_FORMAT 0x00000005UL
+
+/** Unsuppported format code value specified in data-in buffer descriptor */
+#define SRP_T_LOGOUT_UNSUPPORTED_DATA_IN_FORMAT 0x00000006UL
+
+/** Invalid length for IU type */
+#define SRP_T_LOGOUT_INVALID_IU_LEN 0x00000008UL
+
+/*****************************************************************************
+ *
+ * Task management
+ *
+ *****************************************************************************
+ */
+
+/** An SRP task management request */
+struct srp_tsk_mgmt {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_TSK_MGMT
+	 */
+	uint8_t type;
+	/** Flags
+	 *
+	 * This is the bitwise OR of zero or more
+	 * @c SRP_TSK_MGMT_FLAG_XXX constants.
+	 */
+	uint8_t flags;
+	/** Reserved */
+	uint8_t reserved0[6];
+	/** Tag */
+	struct srp_tag tag;
+	/** Reserved */
+	uint8_t reserved1[4];
+	/** Logical unit number */
+	struct scsi_lun lun;
+	/** Reserved */
+	uint8_t reserved2[2];
+	/** Task management function
+	 *
+	 * This is a @c SRP_TASK_MGMT_FUNC_XXX constant
+	 */
+	uint8_t function;
+	/** Reserved */
+	uint8_t reserved3[1];
+	/** Tag of task to be managed */
+	struct srp_tag managed_tag;
+	/** Reserved */
+	uint8_t reserved4[8];
+} __attribute__ (( packed ));
+
+/** Type of an SRP task management request */
+#define SRP_TSK_MGMT 0x01
+
+/** Use solicited notification for unsuccessful completions */
+#define SRP_TSK_MGMT_FLAG_UCSOLNT 0x04
+
+/** Use solicited notification for successful completions */
+#define SRP_TSK_MGMT_FLAG_SCSOLNT 0x02
+
+/** The task manager shall perform an ABORT TASK function */
+#define SRP_TSK_MGMT_FUNC_ABORT_TASK 0x01
+
+/** The task manager shall perform an ABORT TASK SET function */
+#define SRP_TSK_MGMT_FUNC_ABORT_TASK_SET 0x02
+
+/** The task manager shall perform a CLEAR TASK SET function */
+#define SRP_TSK_MGMT_FUNC_CLEAR_TASK_SET 0x04
+
+/** The task manager shall perform a LOGICAL UNIT RESET function */
+#define SRP_TSK_MGMT_FUNC_LOGICAL_UNIT_RESET 0x08
+
+/** The task manager shall perform a CLEAR ACA function */
+#define SRP_TSK_MGMT_FUNC_CLEAR_ACA 0x40
+
+/*****************************************************************************
+ *
+ * SCSI command
+ *
+ *****************************************************************************
+ */
+
+/** An SRP SCSI command */
+struct srp_cmd {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_CMD
+	 */
+	uint8_t type;
+	/** Flags
+	 *
+	 * This is the bitwise OR of zero or more @c SRP_CMD_FLAG_XXX
+	 * constants.
+	 */
+	uint8_t flags;
+	/** Reserved */
+	uint8_t reserved0[3];
+	/** Data buffer descriptor formats
+	 *
+	 * This is the bitwise OR of one @c SRP_CMD_DO_FMT_XXX and one @c
+	 * SRP_CMD_DI_FMT_XXX constant.
+	 */
+	uint8_t data_buffer_formats;
+	/** Data-out buffer descriptor count */
+	uint8_t data_out_buffer_count;
+	/** Data-in buffer descriptor count */
+	uint8_t data_in_buffer_count;
+	/** Tag */
+	struct srp_tag tag;
+	/** Reserved */
+	uint8_t reserved1[4];
+	/** Logical unit number */
+	struct scsi_lun lun;
+	/** Reserved */
+	uint8_t reserved2[1];
+	/** Task attribute
+	 *
+	 * This is a @c SRP_CMD_TASK_ATTR_XXX constant.
+	 */
+	uint8_t task_attr;
+	/** Reserved */
+	uint8_t reserved3[1];
+	/** Additional CDB length */
+	uint8_t additional_cdb_len;
+	/** Command data block */
+	union scsi_cdb cdb;
+} __attribute__ (( packed ));
+
+/** Type of an SRP SCSI command */
+#define SRP_CMD 0x02
+
+/** Use solicited notification for unsuccessful completions */
+#define SRP_CMD_FLAG_UCSOLNT 0x04
+
+/** Use solicited notification for successful completions */
+#define SRP_CMD_FLAG_SCSOLNT 0x02
+
+/** Data-out buffer format mask */
+#define SRP_CMD_DO_FMT_MASK 0xf0
+
+/** Direct data-out buffer format */
+#define SRP_CMD_DO_FMT_DIRECT 0x10
+
+/** Indirect data-out buffer format */
+#define SRP_CMD_DO_FMT_INDIRECT 0x20
+
+/** Data-in buffer format mask */
+#define SRP_CMD_DI_FMT_MASK 0x0f
+
+/** Direct data-in buffer format */
+#define SRP_CMD_DI_FMT_DIRECT 0x01
+
+/** Indirect data-in buffer format */
+#define SRP_CMD_DI_FMT_INDIRECT 0x02
+
+/** Use the rules for a simple task attribute */
+#define SRP_CMD_TASK_ATTR_SIMPLE 0x00
+
+/** Use the rules for a head of queue task attribute */
+#define SRP_CMD_TASK_ATTR_QUEUE_HEAD 0x01
+
+/** Use the rules for an ordered task attribute */
+#define SRP_CMD_TASK_ATTR_ORDERED 0x02
+
+/** Use the rules for an automatic contingent allegiance task attribute */
+#define SRP_CMD_TASK_ATTR_AUTOMATIC_CONTINGENT_ALLEGIANCE 0x08
+
+/** An SRP memory descriptor */
+struct srp_memory_descriptor {
+	/** Virtual address */
+	uint64_t address;
+	/** Memory handle */
+	uint32_t handle;
+	/** Data length */
+	uint32_t len;
+} __attribute__ (( packed ));
+
+/*****************************************************************************
+ *
+ * SCSI response
+ *
+ *****************************************************************************
+ */
+
+/** An SRP SCSI response */
+struct srp_rsp {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_RSP
+	 */
+	uint8_t type;
+	/** Flags
+	 *
+	 * This is the bitwise OR of zero or more @c SRP_RSP_FLAG_XXX
+	 * constants.
+	 */
+	uint8_t flags;
+	/** Reserved */
+	uint8_t reserved0[2];
+	/** Request limit delta */
+	uint32_t request_limit_delta;
+	/** Tag */
+	struct srp_tag tag;
+	/** Reserved */
+	uint8_t reserved1[2];
+	/** Valid fields
+	 *
+	 * This is the bitwise OR of zero or more @c SRP_RSP_VALID_XXX
+	 * constants.
+	 */
+	uint8_t valid;
+	/** Status
+	 *
+	 * This is the SCSI status code.
+	 */
+	uint8_t status;
+	/** Data-out residual count */
+	uint32_t data_out_residual_count;
+	/** Data-in residual count */
+	uint32_t data_in_residual_count;
+	/** Sense data list length */
+	uint32_t sense_data_len;
+	/** Response data list length */
+	uint32_t response_data_len;
+} __attribute__ (( packed ));
+
+/** Type of an SRP SCSI response */
+#define SRP_RSP 0xc1
+
+/** The initiator specified solicited notification of this response */
+#define SRP_RSP_FLAG_SOLNT 0x01
+
+/** Data-in residual count field is valid and represents an underflow */
+#define SRP_RSP_VALID_DIUNDER 0x20
+
+/** Data-in residual count field is valid and represents an overflow */
+#define SRP_RSP_VALID_DIOVER 0x10
+
+/** Data-out residual count field is valid and represents an underflow */
+#define SRP_RSP_VALID_DOUNDER 0x08
+
+/** Data-out residual count field is valid and represents an overflow */
+#define SRP_RSP_VALID_DOOVER 0x04
+
+/** Sense data list length field is valid */
+#define SRP_RSP_VALID_SNSVALID 0x02
+
+/** Response data list length field is valid */
+#define SRP_RSP_VALID_RSPVALID 0x01
+
+/**
+ * Get response data portion of SCSI response
+ *
+ * @v rsp			SCSI response
+ * @ret response_data		Response data, or NULL if not present
+ */
+static inline void * srp_rsp_response_data ( struct srp_rsp *rsp ) {
+	return ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ?
+		 ( ( ( void * ) rsp ) + sizeof ( *rsp ) ) : NULL );
+}
+
+/**
+ * Get length of response data portion of SCSI response
+ *
+ * @v rsp			SCSI response
+ * @ret response_data_len	Response data length
+ */
+static inline size_t srp_rsp_response_data_len ( struct srp_rsp *rsp ) {
+	return ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ?
+		 ntohl ( rsp->response_data_len ) : 0 );
+}
+
+/**
+ * Get sense data portion of SCSI response
+ *
+ * @v rsp			SCSI response
+ * @ret sense_data		Sense data, or NULL if not present
+ */
+static inline void * srp_rsp_sense_data ( struct srp_rsp *rsp ) {
+	return ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ?
+		 ( ( ( void * ) rsp ) + sizeof ( *rsp ) +
+		   srp_rsp_response_data_len ( rsp ) ) : NULL );
+}
+
+/**
+ * Get length of sense data portion of SCSI response
+ *
+ * @v rsp			SCSI response
+ * @ret sense_data_len		Sense data length
+ */
+static inline size_t srp_rsp_sense_data_len ( struct srp_rsp *rsp ) {
+	return ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ?
+		 ntohl ( rsp->sense_data_len ) : 0 );
+}
+
+/*****************************************************************************
+ *
+ * Credit request
+ *
+ *****************************************************************************
+ */
+
+/** An SRP credit request */
+struct srp_cred_req {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_CRED_REQ
+	 */
+	uint8_t type;
+	/** Flags
+	 *
+	 * This is the bitwise OR of zero or more
+	 * @c SRP_CRED_REQ_FLAG_XXX constants.
+	 */
+	uint8_t flags;
+	/** Reserved */
+	uint8_t reserved0[2];
+	/** Request limit delta */
+	uint32_t request_limit_delta;
+	/** Tag */
+	struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP credit request */
+#define SRP_CRED_REQ 0x81
+
+/** The initiator specified solicited notification of credit requests */
+#define SRP_CRED_REQ_FLAG_SOLNT 0x01
+
+/*****************************************************************************
+ *
+ * Credit response
+ *
+ *****************************************************************************
+ */
+
+/** An SRP credit response */
+struct srp_cred_rsp {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_CRED_RSP
+	 */
+	uint8_t type;
+	/** Reserved */
+	uint8_t reserved0[7];
+	/** Tag */
+	struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP credit response */
+#define SRP_CRED_RSP 0x41
+
+/*****************************************************************************
+ *
+ * Asynchronous event request
+ *
+ *****************************************************************************
+ */
+
+/** An SRP asynchronous event request */
+struct srp_aer_req {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_AER_REQ
+	 */
+	uint8_t type;
+	/** Flags
+	 *
+	 * This is the bitwise OR of zero or more @c
+	 * SRP_AER_REQ_FLAG_XXX constants.
+	 */
+	uint8_t flags;
+	/** Reserved */
+	uint8_t reserved0[2];
+	/** Request limit delta */
+	uint32_t request_limit_delta;
+	/** Tag */
+	struct srp_tag tag;
+	/** Reserved */
+	uint8_t reserved1[4];
+	/** Logical unit number */
+	struct scsi_lun lun;
+	/** Sense data list length */
+	uint32_t sense_data_len;
+	/** Reserved */
+	uint8_t reserved2[4];
+} __attribute__ (( packed ));
+
+/** Type of an SRP asynchronous event request */
+#define SRP_AER_REQ 0x82
+
+/** The initiator specified solicited notification of asynchronous events */
+#define SRP_AER_REQ_FLAG_SOLNT 0x01
+
+/**
+ * Get sense data portion of asynchronous event request
+ *
+ * @v aer_req			SRP asynchronous event request
+ * @ret sense_data		Sense data
+ */
+static inline __always_inline void *
+srp_aer_req_sense_data ( struct srp_aer_req *aer_req ) {
+	return ( ( ( void * ) aer_req ) + sizeof ( *aer_req ) );
+}
+
+/**
+ * Get length of sense data portion of asynchronous event request
+ *
+ * @v aer_req			SRP asynchronous event request
+ * @ret sense_data_len		Sense data length
+ */
+static inline __always_inline size_t
+srp_aer_req_sense_data_len ( struct srp_aer_req *aer_req ) {
+	return ( ntohl ( aer_req->sense_data_len ) );
+}
+
+/*****************************************************************************
+ *
+ * Asynchronous event response
+ *
+ *****************************************************************************
+ */
+
+/** An SRP asynchronous event response */
+struct srp_aer_rsp {
+	/** Information unit type
+	 *
+	 * This must be @c SRP_AER_RSP
+	 */
+	uint8_t type;
+	/** Reserved */
+	uint8_t reserved0[7];
+	/** Tag */
+	struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP asynchronous event response */
+#define SRP_AER_RSP 0x42
+
+/*****************************************************************************
+ *
+ * Information units
+ *
+ *****************************************************************************
+ */
+
+/** Maximum length of any initiator-to-target IU that we will send
+ *
+ * The longest IU is a SRP_CMD with no additional CDB and two direct
+ * data buffer descriptors, which comes to 80 bytes.
+ */
+#define SRP_MAX_I_T_IU_LEN 80
+
+/*****************************************************************************
+ *
+ * SRP device
+ *
+ *****************************************************************************
+ */
+
+struct srp_device;
+
+/** An SRP transport type */
+struct srp_transport_type {
+	/** Length of transport private data */
+	size_t priv_len;
+	/** Parse root path
+	 *
+	 * @v srp		SRP device
+	 * @v root_path		Root path
+	 * @ret 		Return status code
+	 */
+	int ( * parse_root_path ) ( struct srp_device *srp,
+				    const char *root_path );
+	/** Connect SRP session
+	 *
+	 * @v srp		SRP device
+	 * @ret rc		Return status code
+	 *
+	 * This method should open the underlying socket.
+	 */
+	int ( * connect ) ( struct srp_device *srp );
+};
+
+/** An SRP device */
+struct srp_device {
+	/** Reference count */
+	struct refcnt refcnt;
+
+	/** Initiator and target port IDs */
+	struct srp_port_ids port_ids;
+	/** Logical unit number */
+	struct scsi_lun lun;
+	/** Memory handle */
+	uint32_t memory_handle;
+
+	/** Current state
+	 *
+	 * This is the bitwise-OR of zero or more @c SRP_STATE_XXX
+	 * flags.
+	 */
+	unsigned int state;
+	/** Retry counter */
+	unsigned int retry_count;
+	/** Current SCSI command */
+	struct scsi_command *command;
+
+	/** Underlying data transfer interface */
+	struct xfer_interface socket;
+
+	/** Transport type */
+	struct srp_transport_type *transport;
+	/** Transport private data */
+	char transport_priv[0];
+};
+
+/**
+ * Get SRP transport private data
+ *
+ * @v srp		SRP device
+ * @ret priv		SRP transport private data
+ */
+static inline __always_inline void *
+srp_transport_priv ( struct srp_device *srp ) {
+	return ( ( void * ) srp->transport_priv );
+}
+
+/** SRP state flags */
+enum srp_state {
+	/** Underlying socket is open */
+	SRP_STATE_SOCKET_OPEN = 0x0001,
+	/** Session is logged in */
+	SRP_STATE_LOGGED_IN = 0x0002,
+};
+
+/** Maximum number of SRP retry attempts */
+#define SRP_MAX_RETRIES 3
+
+extern int srp_attach ( struct scsi_device *scsi, const char *root_path );
+extern void srp_detach ( struct scsi_device *scsi );
+
+#endif /* _GPXE_SRP_H */
diff --git a/gpxe/src/include/gpxe/tables.h b/gpxe/src/include/gpxe/tables.h
index b2c56ab..7dfced8 100644
--- a/gpxe/src/include/gpxe/tables.h
+++ b/gpxe/src/include/gpxe/tables.h
@@ -1,6 +1,8 @@
 #ifndef _GPXE_TABLES_H
 #define _GPXE_TABLES_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** @page ifdef_harmful #ifdef considered harmful
  *
  * Overuse of @c #ifdef has long been a problem in Etherboot.
@@ -98,7 +100,7 @@
  * The linker script takes care of assembling the tables for us.  All
  * our table sections have names of the format @c .tbl.NAME.NN where
  * @c NAME designates the data structure stored in the table (e.g. @c
- * init_fn) and @c NN is a two-digit decimal number used to impose an
+ * init_fns) and @c NN is a two-digit decimal number used to impose an
  * ordering upon the tables if required.  @c NN=00 is reserved for the
  * symbol indicating "table start", and @c NN=99 is reserved for the
  * symbol indicating "table end".
@@ -115,7 +117,9 @@
  *	void ( *frob ) ( void ); 	// The frobnicating function itself
  *   };
  *
- *   #define __frobnicator __table ( frobnicators, 01 )
+ *   #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
+ *
+ *   #define __frobnicator __table_entry ( FROBNICATORS, 01 )
  *
  * @endcode
  *
@@ -145,14 +149,11 @@
  *
  *   #include "frob.h"
  *
- *   static struct frob frob_start[0] __table_start ( frobnicators );
- *   static struct frob frob_end[0] __table_end ( frobnicators );
- *
  *   // Call all linked-in frobnicators
  *   void frob_all ( void ) {
  *	struct frob *frob;
  *
- *      for ( frob = frob_start ; frob < frob_end ; frob++ ) {
+ *	for_each_table ( frob, FROBNICATORS ) {
  *         printf ( "Calling frobnicator \"%s\"\n", frob->name );
  *	   frob->frob ();
  *	}
@@ -168,62 +169,266 @@
 #define __attribute__( x )
 #endif
 
-#define __table_str( x ) #x
-#define __table_section( table, idx ) \
-	__section__ ( ".tbl." __table_str ( table ) "." __table_str ( idx ) )
+/**
+ * Declare a linker table
+ *
+ * @v type		Data type
+ * @v name		Table name
+ * @ret table		Linker table
+ */
+#define __table( type, name ) ( type, name )
 
-#define __table_section_start( table ) __table_section ( table, 00 )
-#define __table_section_end( table ) __table_section ( table, 99 )
+/**
+ * Get linker table data type
+ *
+ * @v table		Linker table
+ * @ret type		Data type
+ */
+#define __table_type( table ) __table_extract_type table
+#define __table_extract_type( type, name ) type
 
-#define __natural_alignment( type ) __aligned__ ( __alignof__ ( type ) )
+/**
+ * Get linker table name
+ *
+ * @v table		Linker table
+ * @ret name		Table name
+ */
+#define __table_name( table ) __table_extract_name table
+#define __table_extract_name( type, name ) name
 
 /**
- * Linker table entry.
+ * Get linker table section name
  *
- * Declares a data structure to be part of a linker table.  Use as
- * e.g.
+ * @v table		Linker table
+ * @v idx		Sub-table index
+ * @ret section		Section name
+ */
+#define __table_section( table, idx ) \
+	".tbl." __table_name ( table ) "." __table_str ( idx )
+#define __table_str( x ) #x
+
+/**
+ * Get linker table alignment
+ *
+ * @v table		Linker table
+ * @ret align		Alignment
+ */
+#define __table_alignment( table ) __alignof__ ( __table_type ( table ) )
+
+/**
+ * Declare a linker table entry
+ *
+ * @v table		Linker table
+ * @v idx		Sub-table index
+ *
+ * Example usage:
  *
  * @code
  *
- *   struct my_foo __table ( foo, 01 ) = {
+ *   #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
+ *
+ *   #define __frobnicator __table_entry ( FROBNICATORS, 01 )
+ *
+ *   struct frobnicator my_frob __frobnicator = {
  *      ...
  *   };
  *
  * @endcode
+ */
+#define __table_entry( table, idx )					\
+	__attribute__ (( __section__ ( __table_section ( table, idx ) ),\
+			 __aligned__ ( __table_alignment ( table ) ) ))
+
+/**
+ * Get start of linker table entries
+ *
+ * @v table		Linker table
+ * @v idx		Sub-table index
+ * @ret entries		Start of entries
+ */
+#define __table_entries( table, idx ) ( {				\
+	static __table_type ( table ) __table_entries[0]		\
+		__table_entry ( table, idx ); 				\
+	__table_entries; } )
+
+/**
+ * Get start of linker table
+ *
+ * @v table		Linker table
+ * @ret start		Start of linker table
+ *
+ * Example usage:
+ *
+ * @code
+ *
+ *   #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
+ *
+ *   struct frobnicator *frobs = table_start ( FROBNICATORS );
+ *
+ * @endcode
+ */
+#define table_start( table ) __table_entries ( table, 00 )
+
+/**
+ * Get end of linker table
+ *
+ * @v table		Linker table
+ * @ret end		End of linker table
+ *
+ * Example usage:
+ *
+ * @code
+ *
+ *   #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
+ *
+ *   struct frobnicator *frobs_end = table_end ( FROBNICATORS );
+ *
+ * @endcode
+ */
+#define table_end( table ) __table_entries ( table, 99 )
+
+/**
+ * Get number of entries in linker table
+ *
+ * @v table		Linker table
+ * @ret num_entries	Number of entries in linker table
+ *
+ * Example usage:
+ *
+ * @code
+ *
+ *   #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
+ *
+ *   unsigned int num_frobs = table_num_entries ( FROBNICATORS );
+ *
+ * @endcode
  *
  */
-#define __table( type, table, idx )				\
-	__attribute__ (( __table_section ( table, idx ),	\
-			 __natural_alignment ( type ) ))
+#define table_num_entries( table )					\
+	( ( unsigned int ) ( table_end ( table ) -			\
+			     table_start ( table ) ) )
 
 /**
- * Linker table start marker.
+ * Iterate through all entries within a linker table
+ *
+ * @v pointer		Entry pointer
+ * @v table		Linker table
  *
- * Declares a data structure (usually an empty data structure) to be
- * the start of a linker table.  Use as e.g.
+ * Example usage:
  *
  * @code
  *
- *   static struct foo_start[0] __table_start ( foo );
+ *   #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
+ *
+ *   struct frobnicator *frob;
+ *
+ *   for_each_table_entry ( frob, FROBNICATORS ) {
+ *     ...
+ *   }
  *
  * @endcode
  *
  */
-#define __table_start( type, table ) __table ( type, table, 00 )
+#define for_each_table_entry( pointer, table )				\
+	for ( pointer = table_start ( table ) ;				\
+	      pointer < table_end ( table ) ;				\
+	      pointer++ )
 
 /**
- * Linker table end marker.
+ * Iterate through all entries within a linker table in reverse order
  *
- * Declares a data structure (usually an empty data structure) to be
- * the end of a linker table.  Use as e.g.
+ * @v pointer		Entry pointer
+ * @v table		Linker table
+ *
+ * Example usage:
  *
  * @code
  *
- *   static struct foo_end[0] __table_end ( foo );
+ *   #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
+ *
+ *   struct frobnicator *frob;
+ *
+ *   for_each_table_entry_reverse ( frob, FROBNICATORS ) {
+ *     ...
+ *   }
  *
  * @endcode
  *
  */
-#define __table_end( type, table ) __table ( type, table, 99 )
+#define for_each_table_entry_reverse( pointer, table )			\
+	for ( pointer = ( table_end ( table ) - 1 ) ;			\
+	      pointer >= table_start ( table ) ;			\
+	      pointer-- )
+
+/******************************************************************************
+ *
+ * Intel's C compiler chokes on several of the constructs used in this
+ * file.  The workarounds are ugly, so we use them only for an icc
+ * build.
+ *
+ */
+#define ICC_ALIGN_HACK_FACTOR 128
+#ifdef __ICC
+
+/*
+ * icc miscompiles zero-length arrays by inserting padding to a length
+ * of two array elements.  We therefore have to generate the
+ * __table_entries() symbols by hand in asm.
+ *
+ */
+#undef __table_entries
+#define __table_entries( table, idx ) ( {				\
+	extern __table_type ( table )					\
+		__table_temp_sym ( idx, __LINE__ ) []			\
+		__table_entry ( table, idx ) 				\
+		asm ( __table_entries_sym ( table, idx ) );		\
+	__asm__ ( ".ifndef %c0\n\t"					\
+		  ".section " __table_section ( table, idx ) "\n\t"	\
+		  ".align %c1\n\t"					\
+	          "\n%c0:\n\t"						\
+		  ".previous\n\t" 					\
+		  ".endif\n\t"						\
+		  : : "i" ( __table_temp_sym ( idx, __LINE__ ) ),	\
+		      "i" ( __table_alignment ( table ) ) );		\
+	__table_temp_sym ( idx, __LINE__ ); } )
+#define __table_entries_sym( table, idx )				\
+	"__tbl_" __table_name ( table ) "_" #idx
+#define __table_temp_sym( a, b )					\
+	___table_temp_sym( __table_, a, _, b )
+#define ___table_temp_sym( a, b, c, d ) a ## b ## c ## d
+
+/*
+ * icc ignores __attribute__ (( aligned (x) )) when it is used to
+ * decrease the compiler's default choice of alignment (which may be
+ * higher than the alignment actually required by the structure).  We
+ * work around this by forcing the alignment to a large multiple of
+ * the required value (so that we are never attempting to decrease the
+ * default alignment) and then postprocessing the object file to
+ * reduce the alignment back down to the "real" value.
+ *
+ */
+#undef __table_alignment
+#define __table_alignment( table ) \
+	( ICC_ALIGN_HACK_FACTOR * __alignof__ ( __table_type ( table ) ) )
+
+/*
+ * Because of the alignment hack, we must ensure that the compiler
+ * never tries to place multiple objects within the same section,
+ * otherwise the assembler will insert padding to the (incorrect)
+ * alignment boundary.  Do this by appending the line number to table
+ * section names.
+ *
+ * Note that we don't need to worry about padding between array
+ * elements, since the alignment is declared on the variable (i.e. the
+ * whole array) rather than on the type (i.e. on all individual array
+ * elements).
+ */
+#undef __table_section
+#define __table_section( table, idx ) \
+	".tbl." __table_name ( table ) "." __table_str ( idx ) \
+	"." __table_xstr ( __LINE__ )
+#define __table_xstr( x ) __table_str ( x )
+
+#endif /* __ICC */
 
 #endif /* _GPXE_TABLES_H */
diff --git a/gpxe/src/include/gpxe/tcp.h b/gpxe/src/include/gpxe/tcp.h
index 264ec29..7ae7eab 100644
--- a/gpxe/src/include/gpxe/tcp.h
+++ b/gpxe/src/include/gpxe/tcp.h
@@ -9,6 +9,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/tcpip.h>
 
 /**
@@ -227,6 +229,16 @@ struct tcp_options {
 			TCP_STATE_SENT ( TCP_FIN ) ) )			    \
 	  == TCP_STATE_ACKED ( TCP_SYN ) )
 
+/** Have ever been fully established
+ *
+ * We have been fully established if we have both received a SYN and
+ * had our own SYN acked.
+ */
+#define TCP_HAS_BEEN_ESTABLISHED(state)					    \
+	( ( (state) & ( TCP_STATE_ACKED ( TCP_SYN ) |			    \
+			TCP_STATE_RCVD ( TCP_SYN ) ) )			    \
+	  == ( TCP_STATE_ACKED ( TCP_SYN ) | TCP_STATE_RCVD ( TCP_SYN ) ) )
+
 /** Have closed gracefully
  *
  * We have closed gracefully if we have both received a FIN and had
diff --git a/gpxe/src/include/gpxe/tcpip.h b/gpxe/src/include/gpxe/tcpip.h
index da89530..f71d7d6 100644
--- a/gpxe/src/include/gpxe/tcpip.h
+++ b/gpxe/src/include/gpxe/tcpip.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/socket.h>
 #include <gpxe/in.h>
@@ -98,13 +100,18 @@ struct tcpip_net_protocol {
 		       uint16_t *trans_csum );
 };
 
+/** TCP/IP transport-layer protocol table */
+#define TCPIP_PROTOCOLS __table ( struct tcpip_protocol, "tcpip_protocols" )
+
 /** Declare a TCP/IP transport-layer protocol */
-#define	__tcpip_protocol \
-	__table ( struct tcpip_protocol, tcpip_protocols, 01 )
+#define __tcpip_protocol __table_entry ( TCPIP_PROTOCOLS, 01 )
+
+/** TCP/IP network-layer protocol table */
+#define TCPIP_NET_PROTOCOLS \
+	__table ( struct tcpip_net_protocol, "tcpip_net_protocols" )
 
 /** Declare a TCP/IP network-layer protocol */
-#define	__tcpip_net_protocol \
-	__table ( struct tcpip_net_protocol, tcpip_net_protocols, 01 )
+#define __tcpip_net_protocol __table_entry ( TCPIP_NET_PROTOCOLS, 01 )
 
 extern int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
 		      struct sockaddr_tcpip *st_src,
diff --git a/gpxe/src/include/gpxe/tftp.h b/gpxe/src/include/gpxe/tftp.h
index 0177a95..c57bb25 100644
--- a/gpxe/src/include/gpxe/tftp.h
+++ b/gpxe/src/include/gpxe/tftp.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 #define TFTP_PORT	       69 /**< Default TFTP server port */
diff --git a/gpxe/src/include/gpxe/threewire.h b/gpxe/src/include/gpxe/threewire.h
index 865fc25..e23284a 100644
--- a/gpxe/src/include/gpxe/threewire.h
+++ b/gpxe/src/include/gpxe/threewire.h
@@ -10,6 +10,8 @@
  * support.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/spi.h>
 #include <limits.h>
 
@@ -43,6 +45,7 @@ extern int threewire_read ( struct nvs_device *nvs, unsigned int address,
 			    void *data, size_t len );
 extern int threewire_write ( struct nvs_device *nvs, unsigned int address,
 			     const void *data, size_t len );
+extern int threewire_detect_address_len ( struct spi_device *device );
 
 /**
  * @defgroup tdevs Three-wire device types
@@ -84,6 +87,19 @@ init_at93c56 ( struct spi_device *device, unsigned int organisation ) {
 	init_at93cx6 ( device, organisation );
 }
 
+/**
+ * Initialise Atmel AT93C66 serial EEPROM
+ *
+ * @v device		SPI device
+ * @v organisation	Word organisation (8 or 16)
+ */
+static inline __attribute__ (( always_inline )) void
+init_at93c66 ( struct spi_device *device, unsigned int organisation ) {
+	device->nvs.size = ( 4096 / organisation );
+	device->address_len = ( ( organisation == 8 ) ? 9 : 8 );
+	init_at93cx6 ( device, organisation );
+}
+
 /** @} */
 
 #endif /* _GPXE_THREEWIRE_H */
diff --git a/gpxe/src/include/gpxe/timer.h b/gpxe/src/include/gpxe/timer.h
index 862d87b..86722dc 100644
--- a/gpxe/src/include/gpxe/timer.h
+++ b/gpxe/src/include/gpxe/timer.h
@@ -9,6 +9,8 @@
  * for a monotonically increasing tick counter.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/api.h>
 #include <config/timer.h>
 
diff --git a/gpxe/src/include/gpxe/tls.h b/gpxe/src/include/gpxe/tls.h
index ddec7be..e2da046 100644
--- a/gpxe/src/include/gpxe/tls.h
+++ b/gpxe/src/include/gpxe/tls.h
@@ -7,6 +7,8 @@
  * Transport Layer Security Protocol
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <gpxe/refcnt.h>
 #include <gpxe/filter.h>
diff --git a/gpxe/src/include/gpxe/uaccess.h b/gpxe/src/include/gpxe/uaccess.h
index 33aaed1..5a8f292 100644
--- a/gpxe/src/include/gpxe/uaccess.h
+++ b/gpxe/src/include/gpxe/uaccess.h
@@ -19,6 +19,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <gpxe/api.h>
diff --git a/gpxe/src/include/gpxe/udp.h b/gpxe/src/include/gpxe/udp.h
index e515f65..670c5e5 100644
--- a/gpxe/src/include/gpxe/udp.h
+++ b/gpxe/src/include/gpxe/udp.h
@@ -9,6 +9,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <gpxe/iobuf.h>
 #include <gpxe/tcpip.h>
diff --git a/gpxe/src/include/gpxe/umalloc.h b/gpxe/src/include/gpxe/umalloc.h
index e6fc7bf..b0e5564 100644
--- a/gpxe/src/include/gpxe/umalloc.h
+++ b/gpxe/src/include/gpxe/umalloc.h
@@ -8,6 +8,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/api.h>
 #include <config/umalloc.h>
 #include <gpxe/uaccess.h>
diff --git a/gpxe/src/include/gpxe/uri.h b/gpxe/src/include/gpxe/uri.h
index 3803868..405d0ce 100644
--- a/gpxe/src/include/gpxe/uri.h
+++ b/gpxe/src/include/gpxe/uri.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdlib.h>
 #include <gpxe/refcnt.h>
@@ -18,6 +20,10 @@
  *
  * Note that all fields within a URI are optional and may be NULL.
  *
+ * The pointers to the various fields are packed together so they can
+ * be accessed in array fashion in some places in uri.c where doing so
+ * saves significant code size.
+ *
  * Some examples are probably helpful:
  *
  *   http://www.etherboot.org/wiki :
@@ -59,8 +65,40 @@ struct uri {
 	const char *query;
 	/** Fragment */
 	const char *fragment;
+} __attribute__ (( packed ));
+
+/** A field in a URI
+ *
+ * The order of the indices in this enumeration must match the order
+ * of the fields in the URI structure.
+ */
+enum {
+	URI_SCHEME = 0,		URI_SCHEME_BIT = ( 1 << URI_SCHEME ),
+	URI_OPAQUE = 1,		URI_OPAQUE_BIT = ( 1 << URI_OPAQUE ),
+	URI_USER = 2,		URI_USER_BIT = ( 1 << URI_USER ),
+	URI_PASSWORD = 3,	URI_PASSWORD_BIT = ( 1 << URI_PASSWORD ),
+	URI_HOST = 4,		URI_HOST_BIT = ( 1 << URI_HOST ),
+	URI_PORT = 5,		URI_PORT_BIT = ( 1 << URI_PORT ),
+	URI_PATH = 6,		URI_PATH_BIT = ( 1 << URI_PATH ),
+	URI_QUERY = 7,		URI_QUERY_BIT = ( 1 << URI_QUERY ),
+	URI_FRAGMENT = 8,	URI_FRAGMENT_BIT = ( 1 << URI_FRAGMENT ),
+
+	URI_FIRST_FIELD = URI_SCHEME,
+	URI_LAST_FIELD = URI_FRAGMENT,
 };
 
+/** Extract field from URI */
+#define uri_get_field( uri, field )	(&uri->scheme)[field]
+
+/** All URI fields */
+#define URI_ALL		( URI_SCHEME_BIT | URI_OPAQUE_BIT | URI_USER_BIT | \
+			  URI_PASSWORD_BIT | URI_HOST_BIT | URI_PORT_BIT | \
+			  URI_PATH_BIT | URI_QUERY_BIT | URI_FRAGMENT_BIT )
+
+/** URI fields that should be decoded on storage */
+#define URI_ENCODED	( URI_USER_BIT | URI_PASSWORD_BIT | URI_HOST_BIT | \
+			  URI_PATH_BIT | URI_QUERY_BIT | URI_FRAGMENT_BIT )
+
 /**
  * URI is an absolute URI
  *
@@ -129,14 +167,16 @@ extern struct uri *cwuri;
 
 extern struct uri * parse_uri ( const char *uri_string );
 extern unsigned int uri_port ( struct uri *uri, unsigned int default_port );
-extern int unparse_uri ( char *buf, size_t size, struct uri *uri );
+extern int unparse_uri ( char *buf, size_t size, struct uri *uri,
+			 unsigned int fields );
 extern struct uri * uri_dup ( struct uri *uri );
 extern char * resolve_path ( const char *base_path,
 			     const char *relative_path );
 extern struct uri * resolve_uri ( struct uri *base_uri,
 				  struct uri *relative_uri );
 extern void churi ( struct uri *uri );
-extern size_t uri_encode ( const char *raw_string, char *buf, size_t len );
-extern size_t uri_decode ( const char *encoded_string, char *buf, size_t len );
+extern size_t uri_encode ( const char *raw_string, char *buf, ssize_t len,
+			   int field );
+extern size_t uri_decode ( const char *encoded_string, char *buf, ssize_t len );
 
 #endif /* _GPXE_URI_H */
diff --git a/gpxe/src/include/gpxe/uuid.h b/gpxe/src/include/gpxe/uuid.h
index 18d1f14..019cd05 100644
--- a/gpxe/src/include/gpxe/uuid.h
+++ b/gpxe/src/include/gpxe/uuid.h
@@ -6,6 +6,8 @@
  * Universally unique IDs
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 /** A universally unique ID */
diff --git a/gpxe/src/include/gpxe/vsprintf.h b/gpxe/src/include/gpxe/vsprintf.h
index aa8f8a3..ee860a5 100644
--- a/gpxe/src/include/gpxe/vsprintf.h
+++ b/gpxe/src/include/gpxe/vsprintf.h
@@ -31,6 +31,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdarg.h>
 #include <stdio.h>
diff --git a/gpxe/src/include/gpxe/wpa.h b/gpxe/src/include/gpxe/wpa.h
new file mode 100644
index 0000000..a7f19d7
--- /dev/null
+++ b/gpxe/src/include/gpxe/wpa.h
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _GPXE_WPA_H
+#define _GPXE_WPA_H
+
+#include <gpxe/ieee80211.h>
+#include <gpxe/list.h>
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** @file
+ *
+ * Common definitions for all types of WPA-protected networks.
+ */
+
+
+/** EAPOL-Key type field for modern 802.11i/RSN WPA packets */
+#define EAPOL_KEY_TYPE_RSN	2
+
+/** Old EAPOL-Key type field used by WPA1 hardware before 802.11i ratified */
+#define EAPOL_KEY_TYPE_WPA	254
+
+
+/**
+ * @defgroup eapol_key_info EAPOL-Key Info field bits
+ * @{
+ */
+
+/** Key descriptor version, indicating WPA or WPA2 */
+#define EAPOL_KEY_INFO_VERSION	0x0007
+
+/** Key type bit, indicating pairwise or group */
+#define EAPOL_KEY_INFO_TYPE	0x0008
+
+/** Key install bit; set on message 3 except when legacy hacks are used */
+#define EAPOL_KEY_INFO_INSTALL	0x0040
+
+/** Key ACK bit; set when a response is required, on all messages except #4 */
+#define EAPOL_KEY_INFO_KEY_ACK	0x0080
+
+/** Key MIC bit; set when the MIC field is valid, on messages 3 and 4 */
+#define EAPOL_KEY_INFO_KEY_MIC	0x0100
+
+/** Secure bit; set when both sides have both keys, on messages 3 and 4 */
+#define EAPOL_KEY_INFO_SECURE	0x0200
+
+/** Error bit; set on a MIC failure for TKIP */
+#define EAPOL_KEY_INFO_ERROR	0x0400
+
+/** Request bit; set when authentication is initiated by the Peer (unusual) */
+#define EAPOL_KEY_INFO_REQUEST	0x0800
+
+/** Key Encrypted bit; set when the Key Data field is encrypted */
+#define EAPOL_KEY_INFO_KEY_ENC	0x1000
+
+/** SMC Message bit; set when this frame is part of an IBSS SMK handshake */
+#define EAPOL_KEY_INFO_SMC_MESS	0x2000
+
+
+/** Key descriptor version field value for WPA (TKIP) */
+#define EAPOL_KEY_VERSION_WPA	1
+
+/** Key descriptor version field value for WPA2 (CCMP) */
+#define EAPOL_KEY_VERSION_WPA2	2
+
+/** Key type field value for a PTK (pairwise) key handshake */
+#define EAPOL_KEY_TYPE_PTK	0x0008
+
+/** Key type field value for a GTK (group) key handshake */
+#define EAPOL_KEY_TYPE_GTK	0x0000
+
+/** @} */
+
+
+
+/** An EAPOL-Key packet.
+ *
+ * These are used for the WPA 4-Way Handshake, whether or not prior
+ * authentication has been performed using EAP.
+ *
+ * On LANs, an eapol_key_pkt is always encapsulated in the data field
+ * of an eapol_frame, with the frame's type code set to EAPOL_TYPE_KEY.
+ *
+ * Unlike 802.11 frame headers, the fields in this structure are
+ * stored in big-endian!
+ */
+struct eapol_key_pkt
+{
+	/** One of the EAPOL_KEY_TYPE_* defines. */
+	u8 type;
+
+	/** Bitfield of key characteristics, network byte order */
+	u16 info;
+
+	/** Length of encryption key to be used, network byte order
+	 *
+	 * This is 16 for CCMP, 32 for TKIP, and 5 or 13 for WEP.
+	 */
+	u16 keysize;
+
+	/** Monotonically increasing value for EAPOL-Key conversations
+	 *
+	 * In another classic demonstration of overengineering, this
+	 * 8-byte value will rarely be anything above 1. It's stored
+	 * in network byte order.
+	 */
+	u64 replay;
+
+	/** Nonce value
+	 *
+	 * This is the authenticator's ANonce in frame 1, the peer's
+	 * SNonce in frame 2, and 0 in frames 3 and 4.
+	 */
+	u8 nonce[32];
+
+	/** Initialization vector
+	 *
+	 * This contains the IV used with the Key Encryption Key, or 0
+	 * if the key is unencrypted or encrypted using an algorithm
+	 * that does not require an IV.
+	 */
+	u8 iv[16];
+
+	/** Receive sequence counter for GTK
+	 *
+	 * This is used to synchronize the client's replay counter for
+	 * ordinary data packets. The first six bytes contain PN0
+	 * through PN5 for CCMP mode, or TSC0 through TSC5 for TKIP
+	 * mode. The last two bytes are zero.
+	 */
+	u8 rsc[8];
+
+	/** Reserved bytes */
+	u8 _reserved[8];
+
+	/** Message integrity code over the entire EAPOL frame
+	 *
+	 * This is calculated using HMAC-MD5 when the key descriptor
+	 * version field in @a info is 1, and HMAC-SHA1 ignoring the
+	 * last 4 bytes of the hash when the version field in @a info
+	 * is 2.
+	 */
+	u8 mic[16];
+
+	/** Length of the @a data field in bytes, network byte order */
+	u16 datalen;
+
+	/** Key data
+	 *
+	 * This is formatted as a series of 802.11 information
+	 * elements, with cryptographic data encapsulated using a
+	 * "vendor-specific IE" code and an IEEE-specified OUI.
+	 */
+	u8 data[0];
+} __attribute__ (( packed ));
+
+
+/** WPA handshaking state */
+enum wpa_state {
+	/** Waiting for PMK to be set */
+	WPA_WAITING = 0,
+
+	/** Ready for 4-Way Handshake */
+	WPA_READY,
+
+	/** Performing 4-Way Handshake */
+	WPA_WORKING,
+
+	/** 4-Way Handshake succeeded */
+	WPA_SUCCESS,
+
+	/** 4-Way Handshake failed */
+	WPA_FAILURE,
+};
+
+/** Bitfield indicating a selection of WPA transient keys */
+enum wpa_keymask {
+	/** Pairwise transient key */
+	WPA_PTK = 1,
+
+	/** Group transient key */
+	WPA_GTK = 2,
+};
+
+
+/** Length of a nonce */
+#define WPA_NONCE_LEN		32
+
+/** Length of a TKIP main key */
+#define WPA_TKIP_KEY_LEN	16
+
+/** Length of a TKIP MIC key */
+#define WPA_TKIP_MIC_KEY_LEN	8
+
+/** Length of a CCMP key */
+#define WPA_CCMP_KEY_LEN	16
+
+/** Length of an EAPOL Key Confirmation Key */
+#define WPA_KCK_LEN		16
+
+/** Length of an EAPOL Key Encryption Key */
+#define WPA_KEK_LEN		16
+
+/** Usual length of a Pairwise Master Key */
+#define WPA_PMK_LEN		32
+
+/** Length of a PMKID */
+#define WPA_PMKID_LEN		16
+
+
+/** Structure of the Temporal Key for TKIP encryption */
+struct tkip_tk
+{
+	/** Main key: input to TKIP Phase 1 and Phase 2 key mixing functions */
+	u8 key[WPA_TKIP_KEY_LEN];
+
+	/** Michael MIC keys */
+	struct {
+		/** MIC key for packets from the AP */
+		u8 rx[WPA_TKIP_MIC_KEY_LEN];
+
+		/** MIC key for packets to the AP */
+		u8 tx[WPA_TKIP_MIC_KEY_LEN];
+	} __attribute__ (( packed )) mic;
+} __attribute__ (( packed ));
+
+/** Structure of a generic Temporal Key */
+union wpa_tk
+{
+	/** CCMP key */
+	u8 ccmp[WPA_CCMP_KEY_LEN];
+
+	/** TKIP keys */
+	struct tkip_tk tkip;
+};
+
+/** Structure of the Pairwise Transient Key */
+struct wpa_ptk
+{
+	/** EAPOL-Key Key Confirmation Key (KCK) */
+	u8 kck[WPA_KCK_LEN];
+
+	/** EAPOL-Key Key Encryption Key (KEK) */
+	u8 kek[WPA_KEK_LEN];
+
+	/** Temporal key */
+	union wpa_tk tk;
+} __attribute__ (( packed ));
+
+/** Structure of the Group Transient Key */
+struct wpa_gtk
+{
+	/** Temporal key */
+	union wpa_tk tk;
+} __attribute__ (( packed ));
+
+
+/** Common context for WPA security handshaking
+ *
+ * Any implementor of a particular handshaking type (e.g. PSK or EAP)
+ * must include this structure at the very beginning of their private
+ * data context structure, to allow the EAPOL-Key handling code to
+ * work. When the preliminary authentication is done, it is necessary
+ * to call wpa_start(), passing the PMK (derived from PSK or EAP MSK)
+ * as an argument. The handshaker can use its @a step function to
+ * monitor @a state in this wpa_ctx structure for success or
+ * failure. On success, the keys will be available in @a ptk and @a
+ * gtk according to the state of the @a valid bitmask.
+ *
+ * After an initial success, the parent handshaker does not need to
+ * concern itself with rekeying; the WPA common code takes care of
+ * that.
+ */
+struct wpa_common_ctx
+{
+	/** 802.11 device we are authenticating for */
+	struct net80211_device *dev;
+
+	/** The Pairwise Master Key to use in handshaking
+	 *
+	 * This is set either by running the PBKDF2 algorithm on a
+	 * passphrase with the SSID as salt to generate a pre-shared
+	 * key, or by copying the first 32 bytes of the EAP Master
+	 * Session Key in 802.1X-served authentication.
+	 */
+	u8 pmk[WPA_PMK_LEN];
+
+	/** Length of the Pairwise Master Key
+	 *
+	 * This is always 32 except with one EAP method which only
+	 * gives 16 bytes.
+	 */
+	int pmk_len;
+
+	/** State of EAPOL-Key handshaking */
+	enum wpa_state state;
+
+	/** Replay counter for this association
+	 *
+	 * This stores the replay counter value for the most recent
+	 * packet we've accepted. It is initially initialised to ~0 to
+	 * show we'll accept anything.
+	 */
+	u64 replay;
+
+	/** Mask of valid keys after authentication success
+	 *
+	 * If the PTK is not valid, the GTK should be used for both
+	 * unicast and multicast decryption; if the GTK is not valid,
+	 * multicast packets cannot be decrypted.
+	 */
+	enum wpa_keymask valid;
+
+	/** The cipher to use for unicast RX and all TX */
+	enum net80211_crypto_alg crypt;
+
+	/** The cipher to use for broadcast and multicast RX */
+	enum net80211_crypto_alg gcrypt;
+
+	/** The Pairwise Transient Key derived from the handshake */
+	struct wpa_ptk ptk;
+
+	/** The Group Transient Key derived from the handshake */
+	struct wpa_gtk gtk;
+
+	/** Authenticator-provided nonce */
+	u8 Anonce[WPA_NONCE_LEN];
+
+	/** Supplicant-generated nonce (that's us) */
+	u8 Snonce[WPA_NONCE_LEN];
+
+	/** Whether we should refrain from generating another SNonce */
+	int have_Snonce;
+
+	/** Data in WPA or RSN IE from AP's beacon frame */
+	void *ap_rsn_ie;
+
+	/** Length of @a ap_rsn_ie */
+	int ap_rsn_ie_len;
+
+	/** Whether @a ap_rsn_ie is an RSN IE (as opposed to old WPA) */
+	int ap_rsn_is_rsn;
+
+	/** List entry */
+	struct list_head list;
+};
+
+
+/** WPA handshake key integrity and encryption handler
+ *
+ * Note that due to the structure of the 4-Way Handshake we never
+ * actually need to encrypt key data, only decrypt it.
+ */
+struct wpa_kie {
+	/** Value of version bits in EAPOL-Key info field for which to use
+	 *
+	 * This should be one of the @c EAPOL_KEY_VERSION_* constants.
+	 */
+	int version;
+
+	/** Calculate MIC over message
+	 *
+	 * @v kck	Key Confirmation Key, 16 bytes
+	 * @v msg	Message to calculate MIC over
+	 * @v len	Number of bytes to calculate MIC over
+	 * @ret mic	Calculated MIC, 16 bytes long
+	 *
+	 * The @a mic return may point within @a msg, so it must not
+	 * be filled until the calculation has been performed.
+	 */
+	void ( * mic ) ( const void *kck, const void *msg, size_t len,
+			 void *mic );
+
+	/** Decrypt key data
+	 *
+	 * @v kek	Key Encryption Key, 16 bytes
+	 * @v iv	Initialisation vector for encryption, 16 bytes
+	 * @v msg	Message to decrypt (Key Data field)
+	 * @v len	Length of message
+	 * @ret msg	Decrypted message in place of original
+	 * @ret len	Updated to reflect encrypted length
+	 * @ret rc	Return status code
+	 *
+	 * The decrypted message is written over the encrypted one.
+	 */
+	int ( * decrypt ) ( const void *kek, const void *iv, void *msg,
+			    u16 *len );
+};
+
+#define WPA_KIES	__table ( struct wpa_kie, "wpa_kies" )
+#define __wpa_kie	__table_entry ( WPA_KIES, 01 )
+
+
+
+/**
+ * @defgroup wpa_kde Key descriptor element types
+ * @{
+ */
+
+/** Payload structure of the GTK-encapsulating KDE
+ *
+ * This does not include the IE type, length, or OUI bytes, which are
+ * generic to all KDEs.
+ */
+struct wpa_kde_gtk_encap
+{
+	/** Key ID and TX bit */
+	u8 id;
+
+	/** Reserved byte */
+	u8 _rsvd;
+
+	/** Encapsulated group transient key */
+	struct wpa_gtk gtk;
+} __attribute__ (( packed ));
+
+/** Mask for Key ID in wpa_kde_gtk::id field */
+#define WPA_GTK_KID	0x03
+
+/** Mask for Tx bit in wpa_kde_gtk::id field */
+#define WPA_GTK_TXBIT	0x04
+
+
+/** KDE type for an encapsulated Group Transient Key (requires encryption) */
+#define WPA_KDE_GTK	_MKOUI ( 0x00, 0x0F, 0xAC, 0x01 )
+
+/** KDE type for a MAC address */
+#define WPA_KDE_MAC	_MKOUI ( 0x00, 0x0F, 0xAC, 0x03 )
+
+/** KDE type for a PMKID */
+#define WPA_KDE_PMKID	_MKOUI ( 0x00, 0x0F, 0xAC, 0x04 )
+
+/** KDE type for a nonce */
+#define WPA_KDE_NONCE	_MKOUI ( 0x00, 0x0F, 0xAC, 0x06 )
+
+/** KDE type for a lifetime value */
+#define WPA_KDE_LIFETIME _MKOUI ( 0x00, 0x0F, 0xAC, 0x07 )
+
+
+/** Any key descriptor element type
+ *
+ * KDEs follow the 802.11 information element format of a type byte
+ * (in this case "vendor-specific", with the requisite OUI+subtype
+ * after length) and a length byte whose value does not include the
+ * length of the type and length bytes.
+ */
+struct wpa_kde
+{
+	/** Information element type: always 0xDD (IEEE80211_IE_VENDOR) */
+	u8 ie_type;
+
+	/** Length, not including ie_type and length fields */
+	u8 len;
+
+	/** OUI + type byte */
+	u32 oui_type;
+
+	/** Payload data */
+	union {
+		/** For GTK-type KDEs, encapsulated GTK */
+		struct wpa_kde_gtk_encap gtk_encap;
+
+		/** For MAC-type KDEs, the MAC address */
+		u8 mac[ETH_ALEN];
+
+		/** For PMKID-type KDEs, the PMKID */
+		u8 pmkid[WPA_PMKID_LEN];
+
+		/** For Nonce-type KDEs, the nonce */
+		u8 nonce[WPA_NONCE_LEN];
+
+		/** For Lifetime-type KDEs, the lifetime in seconds
+		 *
+		 * This is in network byte order!
+		 */
+		u32 lifetime;
+	};
+} __attribute__ (( packed ));
+
+/** @} */
+
+int wpa_make_rsn_ie ( struct net80211_device *dev, union ieee80211_ie **ie );
+int wpa_start ( struct net80211_device *dev, struct wpa_common_ctx *ctx,
+		const void *pmk, size_t pmk_len );
+void wpa_stop ( struct net80211_device *dev );
+
+#endif /* _GPXE_WPA_H */
diff --git a/gpxe/src/include/gpxe/x509.h b/gpxe/src/include/gpxe/x509.h
index 071e1de..1b9d9aa 100644
--- a/gpxe/src/include/gpxe/x509.h
+++ b/gpxe/src/include/gpxe/x509.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 
 struct asn1_cursor;
diff --git a/gpxe/src/include/gpxe/xfer.h b/gpxe/src/include/gpxe/xfer.h
index e592fa3..edd3703 100644
--- a/gpxe/src/include/gpxe/xfer.h
+++ b/gpxe/src/include/gpxe/xfer.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdarg.h>
 #include <gpxe/interface.h>
diff --git a/gpxe/src/include/hci/ifmgmt_cmd.h b/gpxe/src/include/hci/ifmgmt_cmd.h
new file mode 100644
index 0000000..e9c810a
--- /dev/null
+++ b/gpxe/src/include/hci/ifmgmt_cmd.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _IFMGMT_CMD_H
+#define _IFMGMT_CMD_H
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct net_device;
+
+extern int ifcommon_exec (  int argc, char **argv,
+			    int ( * payload ) ( struct net_device * ),
+			    const char *verb );
+
+#endif /* _IFMGMT_CMD_H */
diff --git a/gpxe/src/include/igmp.h b/gpxe/src/include/igmp.h
deleted file mode 100644
index 8b3292f..0000000
--- a/gpxe/src/include/igmp.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef	IGMP_H
-#define	IGMP_H
-
-#include "stdint.h"
-#include <gpxe/in.h>
-
-#define IGMP_QUERY	0x11
-#define IGMPv1_REPORT	0x12
-#define IGMPv2_REPORT	0x16
-#define IGMP_LEAVE	0x17
-#define GROUP_ALL_HOSTS 0xe0000001 /* 224.0.0.1 Host byte order */
-
-#define MULTICAST_MASK    0xf0000000
-#define MULTICAST_NETWORK 0xe0000000
-
-enum {
-	IGMP_SERVER,
-	MAX_IGMP
-};
-
-struct igmp {
-	uint8_t		type;
-	uint8_t		response_time;
-	uint16_t	chksum;
-	struct in_addr	group;
-} PACKED;
-
-struct igmp_ip_t { /* Format of an igmp ip packet */
-	struct iphdr ip;
-	uint8_t router_alert[4]; /* Router alert option */
-	struct igmp igmp;
-} PACKED;
-
-struct igmptable_t {
-	struct in_addr group;
-	unsigned long time;
-} PACKED;
-
-extern void join_group ( int slot, unsigned long group );
-extern void leave_group ( int slot );
-
-#endif	/* IGMP_H */
diff --git a/gpxe/src/include/lib.h b/gpxe/src/include/lib.h
deleted file mode 100644
index 400ea46..0000000
--- a/gpxe/src/include/lib.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef LIB_H
-#define LIB_H
-
-#include <stdint.h>
-
-int getline(char *buf, int max);
-
-extern struct pci_device *dev_list;
-extern int n_devs;
-
-extern void pci_init(void);
-extern struct pci_device *pci_find_device(int vendor, int device, int devclass,
-int prog_if, int index);
-
-void *calloc(size_t nmemb, size_t size);
-void *realloc(void *ptr, size_t size);
-
-char *strdup(const char *s);
-
-int isspace(int c);
-
-unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base);
-unsigned long long strtoull_with_suffix(const char *cp,char **endp,unsigned int base);
-
-unsigned int get_le32(const unsigned char *);
-unsigned int get_le16(const unsigned char *);
-void hexdump(const void *p, unsigned int len);
-
-long long simple_strtoll(const char *cp,char **endp,unsigned int base);
-
-#define LOADER_NOT_SUPPORT 0xbadf11e
-
-struct sys_info;
-int elf_load(struct sys_info *, const char *filename, const char *cmdline);
-
-#if LINUX_LOADER
-int linux_load(struct sys_info *, const char *filename, const char *cmdline);
-#else
-#define linux_load(x,y,z) LOADER_NOT_SUPPORT /* nop */
-#endif
-
-#endif /* LIB_H */
diff --git a/gpxe/src/include/libgen.h b/gpxe/src/include/libgen.h
index 56a2f76..7e94881 100644
--- a/gpxe/src/include/libgen.h
+++ b/gpxe/src/include/libgen.h
@@ -1,6 +1,8 @@
 #ifndef _LIBGEN_H
 #define _LIBGEN_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern char * basename ( char *path );
 extern char * dirname ( char *path );
 
diff --git a/gpxe/src/include/little_bswap.h b/gpxe/src/include/little_bswap.h
index e4f8375..a5dc9c8 100644
--- a/gpxe/src/include/little_bswap.h
+++ b/gpxe/src/include/little_bswap.h
@@ -1,6 +1,8 @@
 #ifndef ETHERBOOT_LITTLE_BSWAP_H
 #define ETHERBOOT_LITTLE_BSWAP_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #define ntohl(x)	__bswap_32(x)
 #define htonl(x) 	__bswap_32(x)
 #define ntohs(x) 	__bswap_16(x)
diff --git a/gpxe/src/include/mii.h b/gpxe/src/include/mii.h
index 34c1ca9..3b735d9 100644
--- a/gpxe/src/include/mii.h
+++ b/gpxe/src/include/mii.h
@@ -10,6 +10,11 @@
  * 03/26/2004
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
+#ifndef _MII_H_
+#define _MII_H_
+
 /* Generic MII registers. */
 
 #define MII_BMCR            0x00	/* Basic mode control register */
@@ -19,6 +24,9 @@
 #define MII_ADVERTISE       0x04	/* Advertisement control reg   */
 #define MII_LPA             0x05	/* Link partner ability reg    */
 #define MII_EXPANSION       0x06        /* Expansion register          */
+#define MII_CTRL1000        0x09        /* 1000BASE-T control          */
+#define MII_STAT1000        0x0a        /* 1000BASE-T status           */
+#define MII_ESTATUS         0x0f        /* Extended Status */
 #define MII_DCOUNTER        0x12        /* Disconnect counter          */
 #define MII_FCSCOUNTER      0x13        /* False carrier counter       */
 #define MII_NWAYTEST        0x14        /* N-way auto-neg test reg     */
@@ -32,7 +40,8 @@
 #define MII_NCONFIG         0x1c        /* Network interface config    */
 
 /* Basic mode control register. */
-#define BMCR_RESV               0x007f  /* Unused...                   */
+#define BMCR_RESV               0x003f  /* Unused...                   */
+#define BMCR_SPEED1000          0x0040  /* MSB of Speed (1000)         */
 #define BMCR_CTST               0x0080  /* Collision test              */
 #define BMCR_FULLDPLX           0x0100	/* Full duplex                 */
 #define BMCR_ANRESTART          0x0200	/* Auto negotiation restart    */
@@ -50,7 +59,10 @@
 #define BMSR_ANEGCAPABLE        0x0008  /* Able to do auto-negotiation */
 #define BMSR_RFAULT             0x0010  /* Remote fault detected       */
 #define BMSR_ANEGCOMPLETE       0x0020  /* Auto-negotiation complete   */
-#define BMSR_RESV               0x07c0  /* Unused...                   */
+#define BMSR_RESV               0x00c0  /* Unused...                   */
+#define BMSR_ESTATEN            0x0100  /* Extended Status in R15 */
+#define BMSR_100HALF2           0x0200  /* Can do 100BASE-T2 HDX */
+#define BMSR_100FULL2           0x0400  /* Can do 100BASE-T2 FDX */
 #define BMSR_10HALF             0x0800  /* Can do 10mbps, half-duplex  */
 #define BMSR_10FULL             0x1000  /* Can do 10mbps, full-duplex  */
 #define BMSR_100HALF            0x2000  /* Can do 100mbps, half-duplex */
@@ -61,11 +73,17 @@
 #define ADVERTISE_SLCT          0x001f  /* Selector bits               */
 #define ADVERTISE_CSMA          0x0001  /* Only selector supported     */
 #define ADVERTISE_10HALF        0x0020	/* Try for 10mbps half-duplex  */
+#define ADVERTISE_1000XFULL     0x0020  /* Try for 1000BASE-X full-duplex */
 #define ADVERTISE_10FULL        0x0040	/* Try for 10mbps full-duplex  */
+#define ADVERTISE_1000XHALF     0x0040  /* Try for 1000BASE-X half-duplex */
 #define ADVERTISE_100HALF       0x0080	/* Try for 100mbps half-duplex */
+#define ADVERTISE_1000XPAUSE    0x0080  /* Try for 1000BASE-X pause    */
 #define ADVERTISE_100FULL       0x0100	/* Try for 100mbps full-duplex */
+#define ADVERTISE_1000XPSE_ASYM 0x0100  /* Try for 1000BASE-X asym pause */
 #define ADVERTISE_100BASE4      0x0200  /* Try for 100mbps 4k packets  */
-#define ADVERTISE_RESV          0x1c00  /* Unused...                   */
+#define ADVERTISE_PAUSE_CAP     0x0400  /* Try for pause               */
+#define ADVERTISE_PAUSE_ASYM    0x0800  /* Try for asymetric pause     */
+#define ADVERTISE_RESV          0x1000  /* Unused...                   */
 #define ADVERTISE_RFAULT        0x2000  /* Say we can detect faults    */
 #define ADVERTISE_LPACK         0x4000  /* Ack link partners response  */
 #define ADVERTISE_NPAGE         0x8000  /* Next page bit               */
@@ -78,11 +96,17 @@
 /* Link partner ability register. */
 #define LPA_SLCT                0x001f  /* Same as advertise selector  */
 #define LPA_10HALF              0x0020  /* Can do 10mbps half-duplex   */
+#define LPA_1000XFULL           0x0020  /* Can do 1000BASE-X full-duplex */
 #define LPA_10FULL              0x0040  /* Can do 10mbps full-duplex   */
+#define LPA_1000XHALF           0x0040  /* Can do 1000BASE-X half-duplex */
 #define LPA_100HALF             0x0080  /* Can do 100mbps half-duplex  */
+#define LPA_1000XPAUSE          0x0080  /* Can do 1000BASE-X pause     */
 #define LPA_100FULL             0x0100  /* Can do 100mbps full-duplex  */
+#define LPA_1000XPAUSE_ASYM     0x0100  /* Can do 1000BASE-X pause asym*/
 #define LPA_100BASE4            0x0200  /* Can do 100mbps 4k packets   */
-#define LPA_RESV                0x1c00  /* Unused...                   */
+#define LPA_PAUSE_CAP           0x0400  /* Can pause                   */
+#define LPA_PAUSE_ASYM          0x0800  /* Can pause asymetrically     */
+#define LPA_RESV                0x1000  /* Unused...                   */
 #define LPA_RFAULT              0x2000  /* Link partner faulted        */
 #define LPA_LPACK               0x4000  /* Link partner acked us       */
 #define LPA_NPAGE               0x8000  /* Next page bit               */
@@ -98,8 +122,98 @@
 #define EXPANSION_MFAULTS       0x0010  /* Multiple faults detected    */
 #define EXPANSION_RESV          0xffe0  /* Unused...                   */
 
+#define ESTATUS_1000_TFULL      0x2000  /* Can do 1000BT Full */
+#define ESTATUS_1000_THALF      0x1000  /* Can do 1000BT Half */
+
 /* N-way test register. */
 #define NWAYTEST_RESV1          0x00ff  /* Unused...                   */
 #define NWAYTEST_LOOPBACK       0x0100  /* Enable loopback for N-way   */
 #define NWAYTEST_RESV2          0xfe00  /* Unused...                   */
 
+/* 1000BASE-T Control register */
+#define ADVERTISE_1000FULL      0x0200  /* Advertise 1000BASE-T full duplex */
+#define ADVERTISE_1000HALF      0x0100  /* Advertise 1000BASE-T half duplex */
+
+/* 1000BASE-T Status register */
+#define LPA_1000LOCALRXOK       0x2000  /* Link partner local receiver status */
+#define LPA_1000REMRXOK         0x1000  /* Link partner remote receiver status */
+#define LPA_1000FULL            0x0800  /* Link partner 1000BASE-T full duplex */
+#define LPA_1000HALF            0x0400  /* Link partner 1000BASE-T half duplex */
+
+#include <gpxe/netdevice.h>
+
+struct mii_if_info {
+        int phy_id;
+        int advertising;
+        int phy_id_mask;
+        int reg_num_mask;
+
+        unsigned int full_duplex : 1;   /* is full duplex? */
+        unsigned int force_media : 1;   /* is autoneg. disabled? */
+        unsigned int supports_gmii : 1; /* are GMII registers supported? */
+
+        struct net_device *dev;
+        int (*mdio_read) (struct net_device *dev, int phy_id, int location);
+        void (*mdio_write) (struct net_device *dev, int phy_id, int location, int val);
+};
+
+
+extern int mii_link_ok (struct mii_if_info *mii);
+extern void mii_check_link (struct mii_if_info *mii);
+extern unsigned int mii_check_media (struct mii_if_info *mii,
+                                       unsigned int ok_to_print,
+                                       unsigned int init_media);
+
+
+/**
+ * mii_nway_result
+ * @negotiated: value of MII ANAR and'd with ANLPAR
+ *
+ * Given a set of MII abilities, check each bit and returns the
+ * currently supported media, in the priority order defined by
+ * IEEE 802.3u.  We use LPA_xxx constants but note this is not the
+ * value of LPA solely, as described above.
+ *
+ * The one exception to IEEE 802.3u is that 100baseT4 is placed
+ * between 100T-full and 100T-half.  If your phy does not support
+ * 100T4 this is fine.  If your phy places 100T4 elsewhere in the
+ * priority order, you will need to roll your own function.
+ */
+static inline unsigned int mii_nway_result (unsigned int negotiated)
+{
+        unsigned int ret;
+
+        if (negotiated & LPA_100FULL)
+                ret = LPA_100FULL;
+        else if (negotiated & LPA_100BASE4)
+                ret = LPA_100BASE4;
+        else if (negotiated & LPA_100HALF)
+                ret = LPA_100HALF;
+        else if (negotiated & LPA_10FULL)
+                ret = LPA_10FULL;
+        else
+                ret = LPA_10HALF;
+
+        return ret;
+}
+
+/**
+ * mii_duplex
+ * @duplex_lock: Non-zero if duplex is locked at full
+ * @negotiated: value of MII ANAR and'd with ANLPAR
+ *
+ * A small helper function for a common case.  Returns one
+ * if the media is operating or locked at full duplex, and
+ * returns zero otherwise.
+ */
+static inline unsigned int mii_duplex (unsigned int duplex_lock,
+                                       unsigned int negotiated)
+{
+        if (duplex_lock)
+                return 1;
+        if (mii_nway_result(negotiated) & LPA_DUPLEX)
+                return 1;
+        return 0;
+}
+
+#endif
diff --git a/gpxe/src/include/nfs.h b/gpxe/src/include/nfs.h
deleted file mode 100644
index 0877bb6..0000000
--- a/gpxe/src/include/nfs.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef	_NFS_H
-#define	_NFS_H
-
-#define SUNRPC_PORT	111
-
-#define PROG_PORTMAP	100000
-#define PROG_NFS	100003
-#define PROG_MOUNT	100005
-
-#define MSG_CALL	0
-#define MSG_REPLY	1
-
-#define PORTMAP_GETPORT	3
-
-#define MOUNT_ADDENTRY	1
-#define MOUNT_UMOUNTALL	4
-
-#define NFS_LOOKUP	4
-#define	NFS_READLINK	5
-#define NFS_READ	6
-
-#define NFS_FHSIZE	32
-
-#define NFSERR_PERM	1
-#define NFSERR_NOENT	2
-#define NFSERR_ACCES	13
-#define	NFSERR_ISDIR	21
-#define	NFSERR_INVAL	22
-
-/* Block size used for NFS read accesses.  A RPC reply packet (including  all
- * headers) must fit within a single Ethernet frame to avoid fragmentation.
- * Chosen to be a power of two, as most NFS servers are optimized for this.  */
-#define NFS_READ_SIZE	1024
-
-#define NFS_MAXLINKDEPTH 16
-
-struct rpc_t {
-	struct iphdr ip;
-	struct udphdr udp;
-	union {
-		uint8_t  data[300];		/* longest RPC call must fit!!!! */
-		struct {
-			uint32_t id;
-			uint32_t type;
-			uint32_t rpcvers;
-			uint32_t prog;
-			uint32_t vers;
-			uint32_t proc;
-			uint32_t data[1];
-		} call;
-		struct {
-			uint32_t id;
-			uint32_t type;
-			uint32_t rstatus;
-			uint32_t verifier;
-			uint32_t v2;
-			uint32_t astatus;
-			uint32_t data[1];
-		} reply;
-	} u;
-} PACKED;
-
-#endif	/* _NFS_H */
diff --git a/gpxe/src/include/nic.h b/gpxe/src/include/nic.h
index 186b2ea..c808972 100644
--- a/gpxe/src/include/nic.h
+++ b/gpxe/src/include/nic.h
@@ -5,6 +5,8 @@
  * your option) any later version.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #ifndef	NIC_H
 #define NIC_H
 
@@ -18,7 +20,6 @@
 #include <gpxe/eisa.h>
 #include <gpxe/mca.h>
 #include <gpxe/io.h>
-#include "dhcp.h"
 
 typedef enum {
 	DISABLE = 0,
@@ -45,7 +46,6 @@ struct nic {
 	unsigned char		irqno;
 	unsigned int		mbps;
 	duplex_t		duplex;
-	struct dhcp_dev_id	dhcp_dev_id;
 	void			*priv_data;	/* driver private data */
 };
 
diff --git a/gpxe/src/include/nmb.h b/gpxe/src/include/nmb.h
deleted file mode 100644
index 3e551ff..0000000
--- a/gpxe/src/include/nmb.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef NMB_H
-#define NMB_H
-
-#include <gpxe/dns.h>
-
-/*
- * NetBIOS name query packets are basically the same as DNS packets,
- * though the resource record format is different.
- *
- */
-
-#define DNS_TYPE_NB		0x20
-#define DNS_FLAG_BROADCAST	( 0x01 << 4 )
-#define NBNS_UDP_PORT		137
-
-struct dns_rr_info_nb {
-	struct dns_rr_info info;
-	uint16_t	nb_flags;
-	struct in_addr	nb_address;
-} __attribute__ (( packed ));
-
-#endif /* NMB_H */
diff --git a/gpxe/src/include/readline/readline.h b/gpxe/src/include/readline/readline.h
index 1a03b48..700b7aa 100644
--- a/gpxe/src/include/readline/readline.h
+++ b/gpxe/src/include/readline/readline.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern char * __malloc readline ( const char *prompt );
 
 #endif /* _READLINE_H */
diff --git a/gpxe/src/include/stdarg.h b/gpxe/src/include/stdarg.h
index a4eb711..78b261a 100644
--- a/gpxe/src/include/stdarg.h
+++ b/gpxe/src/include/stdarg.h
@@ -1,6 +1,8 @@
 #ifndef _STDARG_H
 #define _STDARG_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 typedef __builtin_va_list va_list;
 #define va_start( ap, last ) __builtin_va_start ( ap, last )
 #define va_arg( ap, type ) __builtin_va_arg ( ap, type )
diff --git a/gpxe/src/include/stddef.h b/gpxe/src/include/stddef.h
index 11ea934..2a02a89 100644
--- a/gpxe/src/include/stddef.h
+++ b/gpxe/src/include/stddef.h
@@ -1,6 +1,8 @@
 #ifndef STDDEF_H
 #define STDDEF_H
 
+FILE_LICENCE ( GPL2_ONLY );
+
 /* for size_t */
 #include <stdint.h>
 
diff --git a/gpxe/src/include/stdint.h b/gpxe/src/include/stdint.h
index 4b0e44f..8cc9b84 100644
--- a/gpxe/src/include/stdint.h
+++ b/gpxe/src/include/stdint.h
@@ -1,6 +1,18 @@
 #ifndef _STDINT_H
 #define _STDINT_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/*
+ * This is a standard predefined macro on all gcc's I've seen. It's
+ * important that we define size_t in the same way as the compiler,
+ * because that's what it's expecting when it checks %zd/%zx printf
+ * format specifiers.
+ */
+#ifndef __SIZE_TYPE__
+#define __SIZE_TYPE__ unsigned long /* safe choice on most systems */
+#endif
+
 #include <bits/stdint.h>
 
 typedef int8_t s8;
diff --git a/gpxe/src/include/stdio.h b/gpxe/src/include/stdio.h
index 82077e2..84181f0 100644
--- a/gpxe/src/include/stdio.h
+++ b/gpxe/src/include/stdio.h
@@ -1,6 +1,8 @@
 #ifndef _STDIO_H
 #define _STDIO_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdarg.h>
 
diff --git a/gpxe/src/include/stdlib.h b/gpxe/src/include/stdlib.h
index 838a22a..254e39b 100644
--- a/gpxe/src/include/stdlib.h
+++ b/gpxe/src/include/stdlib.h
@@ -1,6 +1,8 @@
 #ifndef STDLIB_H
 #define STDLIB_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <assert.h>
 
diff --git a/gpxe/src/include/string.h b/gpxe/src/include/string.h
index a2894a3..2fd6acf 100644
--- a/gpxe/src/include/string.h
+++ b/gpxe/src/include/string.h
@@ -11,6 +11,8 @@
  * published by the Free Software Foundation.
  */
 
+FILE_LICENCE ( GPL2_ONLY );
+
 #ifndef ETHERBOOT_STRING_H
 #define ETHERBOOT_STRING_H
 
diff --git a/gpxe/src/include/strings.h b/gpxe/src/include/strings.h
index 968a7c1..c7063d6 100644
--- a/gpxe/src/include/strings.h
+++ b/gpxe/src/include/strings.h
@@ -1,6 +1,8 @@
 #ifndef _STRINGS_H
 #define _STRINGS_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <limits.h>
 #include <string.h>
 
diff --git a/gpxe/src/include/unistd.h b/gpxe/src/include/unistd.h
index dc1f67f..ef94694 100644
--- a/gpxe/src/include/unistd.h
+++ b/gpxe/src/include/unistd.h
@@ -1,6 +1,8 @@
 #ifndef _UNISTD_H
 #define _UNISTD_H
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <stdarg.h>
 
diff --git a/gpxe/src/include/usr/autoboot.h b/gpxe/src/include/usr/autoboot.h
index 1e9647c..a918020 100644
--- a/gpxe/src/include/usr/autoboot.h
+++ b/gpxe/src/include/usr/autoboot.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/in.h>
 struct net_device;
 
@@ -16,6 +18,8 @@ extern void autoboot ( void );
 extern int boot_next_server_and_filename ( struct in_addr next_server,
 					   const char *filename );
 extern int boot_root_path ( const char *root_path );
-extern int pxe_menu_boot ( struct net_device *netdev );
+
+extern int pxe_menu_boot ( struct net_device *netdev )
+	__attribute__ (( weak ));
 
 #endif /* _USR_AUTOBOOT_H */
diff --git a/gpxe/src/include/usr/dhcpmgmt.h b/gpxe/src/include/usr/dhcpmgmt.h
index 0f27560..2394dac 100644
--- a/gpxe/src/include/usr/dhcpmgmt.h
+++ b/gpxe/src/include/usr/dhcpmgmt.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct net_device;
 
 extern int dhcp ( struct net_device *netdev );
diff --git a/gpxe/src/include/usr/ifmgmt.h b/gpxe/src/include/usr/ifmgmt.h
index 7b49d34..f762c7b 100644
--- a/gpxe/src/include/usr/ifmgmt.h
+++ b/gpxe/src/include/usr/ifmgmt.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct net_device;
 
 extern int ifopen ( struct net_device *netdev );
diff --git a/gpxe/src/include/usr/imgmgmt.h b/gpxe/src/include/usr/imgmgmt.h
index 438af00..0c8c8cf 100644
--- a/gpxe/src/include/usr/imgmgmt.h
+++ b/gpxe/src/include/usr/imgmgmt.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 struct image;
 
 extern int imgfetch ( struct image *image, const char *uri_string,
diff --git a/gpxe/src/include/usr/iwmgmt.h b/gpxe/src/include/usr/iwmgmt.h
new file mode 100644
index 0000000..c1bdc37
--- /dev/null
+++ b/gpxe/src/include/usr/iwmgmt.h
@@ -0,0 +1,17 @@
+#ifndef _USR_IWMGMT_H
+#define _USR_IWMGMT_H
+
+/** @file
+ *
+ * Wireless network interface management
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct net80211_device;
+
+extern void iwstat ( struct net80211_device *dev );
+extern int iwlist ( struct net80211_device *dev );
+
+#endif /* _USR_IWMGMT_H */
diff --git a/gpxe/src/include/usr/route.h b/gpxe/src/include/usr/route.h
index fc85589..855fa7b 100644
--- a/gpxe/src/include/usr/route.h
+++ b/gpxe/src/include/usr/route.h
@@ -7,6 +7,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 extern void route ( void );
 
 #endif /* _USR_ROUTE_H */
diff --git a/gpxe/src/interface/efi/efi_console.c b/gpxe/src/interface/efi/efi_console.c
index b78de61..04af28a 100644
--- a/gpxe/src/interface/efi/efi_console.c
+++ b/gpxe/src/interface/efi/efi_console.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <assert.h>
 #include <gpxe/efi/efi.h>
diff --git a/gpxe/src/interface/efi/efi_init.c b/gpxe/src/interface/efi/efi_init.c
index 6e54cf7..ad55037 100644
--- a/gpxe/src/interface/efi/efi_init.c
+++ b/gpxe/src/interface/efi/efi_init.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <gpxe/efi/efi.h>
 #include <gpxe/uuid.h>
@@ -26,18 +28,6 @@ EFI_HANDLE efi_image_handle;
 /** System table passed to entry point */
 EFI_SYSTEM_TABLE *efi_systab;
 
-/** Declared used EFI protocols */
-static struct efi_protocol efi_protocols[0] \
-	__table_start ( struct efi_protocol, efi_protocols );
-static struct efi_protocol efi_protocols_end[0] \
-	__table_end ( struct efi_protocol, efi_protocols );
-
-/** Declared used EFI configuration tables */
-static struct efi_config_table efi_config_tables[0] \
-	__table_start ( struct efi_config_table, efi_config_tables );
-static struct efi_config_table efi_config_tables_end[0] \
-	__table_end ( struct efi_config_table, efi_config_tables );
-
 /**
  * Look up EFI configuration table
  *
@@ -92,7 +82,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
 
 	/* Look up used protocols */
 	bs = systab->BootServices;
-	for ( prot = efi_protocols ; prot < efi_protocols_end ; prot++ ) {
+	for_each_table_entry ( prot, EFI_PROTOCOLS ) {
 		if ( ( efirc = bs->LocateProtocol ( &prot->u.guid, NULL,
 						    prot->protocol ) ) == 0 ) {
 			DBGC ( systab, "EFI protocol %s is at %p\n",
@@ -106,7 +96,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
 	}
 
 	/* Look up used configuration tables */
-	for ( tab = efi_config_tables ; tab < efi_config_tables_end ; tab++ ) {
+	for_each_table_entry ( tab, EFI_CONFIG_TABLES ) {
 		if ( ( *(tab->table) = efi_find_table ( &tab->u.guid ) ) ) {
 			DBGC ( systab, "EFI configuration table %s is at %p\n",
 			       uuid_ntoa ( &tab->u.uuid ), *(tab->table) );
diff --git a/gpxe/src/interface/efi/efi_io.c b/gpxe/src/interface/efi/efi_io.c
index e11f9bf..0ba16f8 100644
--- a/gpxe/src/interface/efi/efi_io.c
+++ b/gpxe/src/interface/efi/efi_io.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <assert.h>
 #include <gpxe/io.h>
 #include <gpxe/efi/efi.h>
diff --git a/gpxe/src/interface/efi/efi_pci.c b/gpxe/src/interface/efi/efi_pci.c
index f87b540..ec43391 100644
--- a/gpxe/src/interface/efi/efi_pci.c
+++ b/gpxe/src/interface/efi/efi_pci.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <errno.h>
 #include <gpxe/pci.h>
 #include <gpxe/efi/efi.h>
diff --git a/gpxe/src/interface/efi/efi_smbios.c b/gpxe/src/interface/efi/efi_smbios.c
index 5888f2f..8caf624 100644
--- a/gpxe/src/interface/efi/efi_smbios.c
+++ b/gpxe/src/interface/efi/efi_smbios.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <errno.h>
 #include <gpxe/smbios.h>
 #include <gpxe/efi/efi.h>
diff --git a/gpxe/src/interface/efi/efi_snp.c b/gpxe/src/interface/efi/efi_snp.c
index 771b917..b5241e5 100644
--- a/gpxe/src/interface/efi/efi_snp.c
+++ b/gpxe/src/interface/efi/efi_snp.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -117,20 +119,20 @@ static EFI_GUID efi_pci_io_protocol_guid
 static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) {
 	struct net_device *netdev = snpdev->netdev;
 	EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode;
-	unsigned int ll_addr_len = netdev->ll_protocol->ll_addr_len;
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
+	unsigned int ll_addr_len = ll_protocol->ll_addr_len;
 
 	mode->HwAddressSize = ll_addr_len;
-	mode->MediaHeaderSize = netdev->ll_protocol->ll_header_len;
+	mode->MediaHeaderSize = ll_protocol->ll_header_len;
 	mode->MaxPacketSize = netdev->max_pkt_len;
 	mode->ReceiveFilterMask = ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
 				    EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
 				    EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST );
 	assert ( ll_addr_len <= sizeof ( mode->CurrentAddress ) );
 	memcpy ( &mode->CurrentAddress, netdev->ll_addr, ll_addr_len );
-	memcpy ( &mode->BroadcastAddress, netdev->ll_protocol->ll_broadcast,
-		 ll_addr_len );
-	memcpy ( &mode->PermanentAddress, netdev->ll_addr, ll_addr_len );
-	mode->IfType = ntohs ( netdev->ll_protocol->ll_proto );
+	memcpy ( &mode->BroadcastAddress, netdev->ll_broadcast, ll_addr_len );
+	ll_protocol->init_addr ( netdev->hw_addr, &mode->PermanentAddress );
+	mode->IfType = ntohs ( ll_protocol->ll_proto );
 	mode->MacAddressChangeable = TRUE;
 	mode->MediaPresentSupported = TRUE;
 	mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE );
@@ -592,7 +594,8 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
 	/* Create link-layer header, if specified */
 	if ( ll_header_len ) {
 		iob_pull ( iobuf, ll_header_len );
-		if ( ( rc = ll_protocol->push ( iobuf, ll_dest, ll_src,
+		if ( ( rc = ll_protocol->push ( snpdev->netdev,
+						iobuf, ll_dest, ll_src,
 						htons ( *net_proto ) )) != 0 ){
 			DBGC ( snpdev, "SNPDEV %p TX could not construct "
 			       "header: %s\n", snpdev, strerror ( rc ) );
@@ -670,8 +673,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
 	*len = iob_len ( iobuf );
 
 	/* Attempt to decode link-layer header */
-	if ( ( rc = ll_protocol->pull ( iobuf, &iob_ll_dest, &iob_ll_src,
-					&iob_net_proto ) ) != 0 ) {
+	if ( ( rc = ll_protocol->pull ( snpdev->netdev, iobuf, &iob_ll_dest,
+					&iob_ll_src, &iob_net_proto ) ) != 0 ){
 		DBGC ( snpdev, "SNPDEV %p could not parse header: %s\n",
 		       snpdev, strerror ( rc ) );
 		efirc = RC_TO_EFIRC ( rc );
diff --git a/gpxe/src/interface/efi/efi_strerror.c b/gpxe/src/interface/efi/efi_strerror.c
index adfeaed..2bf4581 100644
--- a/gpxe/src/interface/efi/efi_strerror.c
+++ b/gpxe/src/interface/efi/efi_strerror.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <gpxe/efi/efi.h>
 
diff --git a/gpxe/src/interface/efi/efi_timer.c b/gpxe/src/interface/efi/efi_timer.c
index d1ba43a..0dcb760 100644
--- a/gpxe/src/interface/efi/efi_timer.c
+++ b/gpxe/src/interface/efi/efi_timer.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <limits.h>
 #include <assert.h>
 #include <unistd.h>
diff --git a/gpxe/src/interface/efi/efi_uaccess.c b/gpxe/src/interface/efi/efi_uaccess.c
index 1c54c03..63e9521 100644
--- a/gpxe/src/interface/efi/efi_uaccess.c
+++ b/gpxe/src/interface/efi/efi_uaccess.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <gpxe/uaccess.h>
 #include <gpxe/efi/efi.h>
 
diff --git a/gpxe/src/interface/efi/efi_umalloc.c b/gpxe/src/interface/efi/efi_umalloc.c
index 4de3789..7113c79 100644
--- a/gpxe/src/interface/efi/efi_umalloc.c
+++ b/gpxe/src/interface/efi/efi_umalloc.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <assert.h>
 #include <gpxe/umalloc.h>
 #include <gpxe/efi/efi.h>
diff --git a/gpxe/src/interface/smbios/smbios.c b/gpxe/src/interface/smbios/smbios.c
index 8207c1f..cc7df59 100644
--- a/gpxe/src/interface/smbios/smbios.c
+++ b/gpxe/src/interface/smbios/smbios.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
diff --git a/gpxe/src/interface/smbios/smbios_settings.c b/gpxe/src/interface/smbios/smbios_settings.c
index 61c2d91..1c96564 100644
--- a/gpxe/src/interface/smbios/smbios_settings.c
+++ b/gpxe/src/interface/smbios/smbios_settings.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
@@ -62,22 +64,6 @@
 	  ( offsetof ( _structure, _field ) << 8 ) )
 
 /**
- * Store value of SMBIOS setting
- *
- * @v settings		Settings block
- * @v setting		Setting to store
- * @v data		Setting data, or NULL to clear setting
- * @v len		Length of setting data
- * @ret rc		Return status code
- */
-static int smbios_store ( struct settings *settings __unused,
-			  struct setting *setting __unused,
-			  const void *data __unused, size_t len __unused ) {
-	/* Cannot write data into SMBIOS */
-	return -ENOTSUP;
-}
-
-/**
  * Fetch value of SMBIOS setting
  *
  * @v settings		Settings block, or NULL to search all blocks
@@ -133,7 +119,6 @@ static int smbios_fetch ( struct settings *settings __unused,
 
 /** SMBIOS settings operations */
 static struct settings_operations smbios_settings_operations = {
-	.store = smbios_store,
 	.fetch = smbios_fetch,
 };
 
@@ -198,4 +183,12 @@ struct setting smbios_named_settings[] __setting = {
 					   serial ),
 		.type = &setting_type_string,
 	},
+	{
+		.name = "asset",
+		.description = "Asset tag",
+		.tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_ENCLOSURE_INFORMATION,
+					   struct smbios_enclosure_information,
+					   asset_tag ),
+		.type = &setting_type_string,
+	},
 };
diff --git a/gpxe/src/libgcc/icc.c b/gpxe/src/libgcc/icc.c
new file mode 100644
index 0000000..2f7f605
--- /dev/null
+++ b/gpxe/src/libgcc/icc.c
@@ -0,0 +1,8 @@
+/*
+ * Intel's compiler creates an implicit call to this function at the
+ * start of main().
+ *
+ */
+void __libgcc __intel_new_proc_init ( void ) {
+	/* Do nothing */
+}
diff --git a/gpxe/src/net/80211/net80211.c b/gpxe/src/net/80211/net80211.c
new file mode 100644
index 0000000..1c54597
--- /dev/null
+++ b/gpxe/src/net/80211/net80211.c
@@ -0,0 +1,2829 @@
+/*
+ * The gPXE 802.11 MAC layer.
+ *
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <byteswap.h>
+#include <stdlib.h>
+#include <gpxe/settings.h>
+#include <gpxe/if_arp.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/ieee80211.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/net80211.h>
+#include <gpxe/sec80211.h>
+#include <gpxe/timer.h>
+#include <gpxe/nap.h>
+#include <unistd.h>
+#include <errno.h>
+
+/** @file
+ *
+ * 802.11 device management
+ */
+
+/* Disambiguate the EINVAL's a bit */
+#define EINVAL_PKT_TOO_SHORT	( EINVAL | EUNIQ_01 )
+#define EINVAL_PKT_VERSION	( EINVAL | EUNIQ_02 )
+#define EINVAL_PKT_NOT_DATA	( EINVAL | EUNIQ_03 )
+#define EINVAL_PKT_NOT_FROMDS	( EINVAL | EUNIQ_04 )
+#define EINVAL_PKT_LLC_HEADER	( EINVAL | EUNIQ_05 )
+#define EINVAL_CRYPTO_REQUEST	( EINVAL | EUNIQ_06 )
+#define EINVAL_ACTIVE_SCAN	( EINVAL | EUNIQ_07 )
+
+/*
+ * 802.11 error codes: The AP can give us a status code explaining why
+ * authentication failed, or a reason code explaining why we were
+ * deauthenticated/disassociated. These codes range from 0-63 (the
+ * field is 16 bits wide, but only up to 45 or so are defined yet; we
+ * allow up to 63 for extensibility). This is encoded into an error
+ * code as such:
+ *
+ *                                      status & 0x1f goes here --vv--
+ *   Status code 0-31:  ECONNREFUSED | EUNIQ_(status & 0x1f) (0e1a6038)
+ *   Status code 32-63: EHOSTUNREACH | EUNIQ_(status & 0x1f) (171a6011)
+ *   Reason code 0-31:  ECONNRESET | EUNIQ_(reason & 0x1f)   (0f1a6039)
+ *   Reason code 32-63: ENETRESET | EUNIQ_(reason & 0x1f)    (271a6001)
+ *
+ * The POSIX error codes more or less convey the appropriate message
+ * (status codes occur when we can't associate at all, reason codes
+ * when we lose association unexpectedly) and let us extract the
+ * complete 802.11 error code from the rc value.
+ */
+
+/** Make return status code from 802.11 status code */
+#define E80211_STATUS( stat )  ( ((stat & 0x20)? EHOSTUNREACH : ECONNREFUSED) \
+					| ((stat & 0x1f) << 8) )
+
+/** Make return status code from 802.11 reason code */
+#define E80211_REASON( reas )  ( ((reas & 0x20)? ENETRESET : ECONNRESET) \
+					| ((reas & 0x1f) << 8) )
+
+
+/** List of 802.11 devices */
+static struct list_head net80211_devices = LIST_HEAD_INIT ( net80211_devices );
+
+/** Set of device operations that does nothing */
+static struct net80211_device_operations net80211_null_ops;
+
+/** Information associated with a received management packet
+ *
+ * This is used to keep beacon signal strengths in a parallel queue to
+ * the beacons themselves.
+ */
+struct net80211_rx_info {
+	int signal;
+	struct list_head list;
+};
+
+/** Context for a probe operation */
+struct net80211_probe_ctx {
+	/** 802.11 device to probe on */
+	struct net80211_device *dev;
+
+	/** Value of keep_mgmt before probe was started */
+	int old_keep_mgmt;
+
+	/** If scanning actively, pointer to probe packet to send */
+	struct io_buffer *probe;
+
+	/** If non-"", the ESSID to limit ourselves to */
+	const char *essid;
+
+	/** Time probe was started */
+	u32 ticks_start;
+
+	/** Time last useful beacon was received */
+	u32 ticks_beacon;
+
+	/** Time channel was last changed */
+	u32 ticks_channel;
+
+	/** Time to stay on each channel */
+	u32 hop_time;
+
+	/** Channels to hop by when changing channel */
+	int hop_step;
+
+	/** List of best beacons for each network found so far */
+	struct list_head *beacons;
+};
+
+/** Context for the association task */
+struct net80211_assoc_ctx {
+	/** Next authentication method to try using */
+	int method;
+
+	/** Time (in ticks) of the last sent association-related packet */
+	int last_packet;
+
+	/** Number of times we have tried sending it */
+	int times_tried;
+};
+
+/**
+ * @defgroup net80211_netdev Network device interface functions
+ * @{
+ */
+static int net80211_netdev_open ( struct net_device *netdev );
+static void net80211_netdev_close ( struct net_device *netdev );
+static int net80211_netdev_transmit ( struct net_device *netdev,
+				      struct io_buffer *iobuf );
+static void net80211_netdev_poll ( struct net_device *netdev );
+static void net80211_netdev_irq ( struct net_device *netdev, int enable );
+/** @} */
+
+/**
+ * @defgroup net80211_linklayer 802.11 link-layer protocol functions
+ * @{
+ */
+static int net80211_ll_push ( struct net_device *netdev,
+			      struct io_buffer *iobuf, const void *ll_dest,
+			      const void *ll_source, uint16_t net_proto );
+static int net80211_ll_pull ( struct net_device *netdev,
+			      struct io_buffer *iobuf, const void **ll_dest,
+			      const void **ll_source, uint16_t * net_proto );
+/** @} */
+
+/**
+ * @defgroup net80211_help 802.11 helper functions
+ * @{
+ */
+static void net80211_add_channels ( struct net80211_device *dev, int start,
+				    int len, int txpower );
+static void net80211_filter_hw_channels ( struct net80211_device *dev );
+static void net80211_set_rtscts_rate ( struct net80211_device *dev );
+static int net80211_process_capab ( struct net80211_device *dev,
+				    u16 capab );
+static int net80211_process_ie ( struct net80211_device *dev,
+				 union ieee80211_ie *ie, void *ie_end );
+static union ieee80211_ie *
+net80211_marshal_request_info ( struct net80211_device *dev,
+				union ieee80211_ie *ie );
+/** @} */
+
+/**
+ * @defgroup net80211_assoc_ll 802.11 association handling functions
+ * @{
+ */
+static void net80211_step_associate ( struct process *proc );
+static void net80211_handle_auth ( struct net80211_device *dev,
+				   struct io_buffer *iob );
+static void net80211_handle_assoc_reply ( struct net80211_device *dev,
+					  struct io_buffer *iob );
+static int net80211_send_disassoc ( struct net80211_device *dev, int reason,
+				    int deauth );
+static void net80211_handle_mgmt ( struct net80211_device *dev,
+				   struct io_buffer *iob, int signal );
+/** @} */
+
+/**
+ * @defgroup net80211_frag 802.11 fragment handling functions
+ * @{
+ */
+static void net80211_free_frags ( struct net80211_device *dev, int fcid );
+static struct io_buffer *net80211_accum_frags ( struct net80211_device *dev,
+						int fcid, int nfrags, int size );
+static void net80211_rx_frag ( struct net80211_device *dev,
+			       struct io_buffer *iob, int signal );
+/** @} */
+
+/**
+ * @defgroup net80211_settings 802.11 settings handlers
+ * @{
+ */
+static int net80211_check_settings_update ( void );
+
+/** 802.11 settings applicator
+ *
+ * When the SSID is changed, this will cause any open devices to
+ * re-associate; when the encryption key is changed, we similarly
+ * update their state.
+ */
+struct settings_applicator net80211_applicator __settings_applicator = {
+	.apply = net80211_check_settings_update,
+};
+
+/** The network name to associate with
+ *
+ * If this is blank, we scan for all networks and use the one with the
+ * greatest signal strength.
+ */
+struct setting net80211_ssid_setting __setting = {
+	.name = "ssid",
+	.description = "802.11 SSID (network name)",
+	.type = &setting_type_string,
+};
+
+/** Whether to use active scanning
+ *
+ * In order to associate with a hidden SSID, it's necessary to use an
+ * active scan (send probe packets). If this setting is nonzero, an
+ * active scan on the 2.4GHz band will be used to associate.
+ */
+struct setting net80211_active_setting __setting = {
+	.name = "active-scan",
+	.description = "Use an active scan during 802.11 association",
+	.type = &setting_type_int8,
+};
+
+/** The cryptographic key to use
+ *
+ * For hex WEP keys, as is common, this must be entered using the
+ * normal gPXE method for entering hex settings; an ASCII string of
+ * hex characters will not behave as expected.
+ */
+struct setting net80211_key_setting __setting = {
+	.name = "key",
+	.description = "Encryption key for protected 802.11 networks",
+	.type = &setting_type_string,
+};
+
+/** @} */
+
+
+/* ---------- net_device wrapper ---------- */
+
+/**
+ * Open 802.11 device and start association
+ *
+ * @v netdev	Wrapping network device
+ * @ret rc	Return status code
+ *
+ * This sets up a default conservative set of channels for probing,
+ * and starts the auto-association task unless the @c
+ * NET80211_NO_ASSOC flag is set in the wrapped 802.11 device's @c
+ * state field.
+ */
+static int net80211_netdev_open ( struct net_device *netdev )
+{
+	struct net80211_device *dev = netdev->priv;
+	int rc = 0;
+
+	if ( dev->op == &net80211_null_ops )
+		return -EFAULT;
+
+	if ( dev->op->open )
+		rc = dev->op->open ( dev );
+
+	if ( rc < 0 )
+		return rc;
+
+	if ( ! ( dev->state & NET80211_NO_ASSOC ) )
+		net80211_autoassociate ( dev );
+
+	return 0;
+}
+
+/**
+ * Close 802.11 device
+ *
+ * @v netdev	Wrapping network device.
+ *
+ * If the association task is running, this will stop it.
+ */
+static void net80211_netdev_close ( struct net_device *netdev )
+{
+	struct net80211_device *dev = netdev->priv;
+
+	if ( dev->state & NET80211_WORKING )
+		process_del ( &dev->proc_assoc );
+
+	/* Send disassociation frame to AP, to be polite */
+	if ( dev->state & NET80211_ASSOCIATED )
+		net80211_send_disassoc ( dev, IEEE80211_REASON_LEAVING, 0 );
+
+	if ( dev->handshaker && dev->handshaker->stop &&
+	     dev->handshaker->started )
+		dev->handshaker->stop ( dev );
+
+	free ( dev->crypto );
+	free ( dev->handshaker );
+	dev->crypto = NULL;
+	dev->handshaker = NULL;
+
+	netdev_link_down ( netdev );
+	dev->state = 0;
+
+	if ( dev->op->close )
+		dev->op->close ( dev );
+}
+
+/**
+ * Transmit packet on 802.11 device
+ *
+ * @v netdev	Wrapping network device
+ * @v iobuf	I/O buffer
+ * @ret rc	Return status code
+ *
+ * If encryption is enabled for the currently associated network, the
+ * packet will be encrypted prior to transmission.
+ */
+static int net80211_netdev_transmit ( struct net_device *netdev,
+				      struct io_buffer *iobuf )
+{
+	struct net80211_device *dev = netdev->priv;
+	struct ieee80211_frame *hdr = iobuf->data;
+	int rc = -ENOSYS;
+
+	if ( dev->crypto && ! ( hdr->fc & IEEE80211_FC_PROTECTED ) &&
+	     ( ( hdr->fc & IEEE80211_FC_TYPE ) == IEEE80211_TYPE_DATA ) ) {
+		struct io_buffer *niob = dev->crypto->encrypt ( dev->crypto,
+								iobuf );
+		if ( ! niob )
+			return -ENOMEM;	/* only reason encryption could fail */
+
+		/* Free the non-encrypted iob */
+		netdev_tx_complete ( netdev, iobuf );
+
+		/* Transmit the encrypted iob; the Protected flag is
+		   set, so we won't recurse into here again */
+		netdev_tx ( netdev, niob );
+
+		/* Don't transmit the freed packet */
+		return 0;
+	}
+
+	if ( dev->op->transmit )
+		rc = dev->op->transmit ( dev, iobuf );
+
+	return rc;
+}
+
+/**
+ * Poll 802.11 device for received packets and completed transmissions
+ *
+ * @v netdev	Wrapping network device
+ */
+static void net80211_netdev_poll ( struct net_device *netdev )
+{
+	struct net80211_device *dev = netdev->priv;
+
+	if ( dev->op->poll )
+		dev->op->poll ( dev );
+}
+
+/**
+ * Enable or disable interrupts for 802.11 device
+ *
+ * @v netdev	Wrapping network device
+ * @v enable	Whether to enable interrupts
+ */
+static void net80211_netdev_irq ( struct net_device *netdev, int enable )
+{
+	struct net80211_device *dev = netdev->priv;
+
+	if ( dev->op->irq )
+		dev->op->irq ( dev, enable );
+}
+
+/** Network device operations for a wrapped 802.11 device */
+static struct net_device_operations net80211_netdev_ops = {
+	.open = net80211_netdev_open,
+	.close = net80211_netdev_close,
+	.transmit = net80211_netdev_transmit,
+	.poll = net80211_netdev_poll,
+	.irq = net80211_netdev_irq,
+};
+
+
+/* ---------- 802.11 link-layer protocol ---------- */
+
+/** 802.11 broadcast MAC address */
+static u8 net80211_ll_broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+/**
+ * Determine whether a transmission rate uses ERP/OFDM
+ *
+ * @v rate	Rate in 100 kbps units
+ * @ret is_erp	TRUE if the rate is an ERP/OFDM rate
+ *
+ * 802.11b supports rates of 1.0, 2.0, 5.5, and 11.0 Mbps; any other
+ * rate than these on the 2.4GHz spectrum is an ERP (802.11g) rate.
+ */
+static inline int net80211_rate_is_erp ( u16 rate )
+{
+	if ( rate == 10 || rate == 20 || rate == 55 || rate == 110 )
+		return 0;
+	return 1;
+}
+
+
+/**
+ * Calculate one frame's contribution to 802.11 duration field
+ *
+ * @v dev	802.11 device
+ * @v bytes	Amount of data to calculate duration for
+ * @ret dur	Duration field in microseconds
+ *
+ * To avoid multiple stations attempting to transmit at once, 802.11
+ * provides that every packet shall include a duration field
+ * specifying a length of time for which the wireless medium will be
+ * reserved after it is transmitted. The duration is measured in
+ * microseconds and is calculated with respect to the current
+ * physical-layer parameters of the 802.11 device.
+ *
+ * For an unfragmented data or management frame, or the last fragment
+ * of a fragmented frame, the duration captures only the 10 data bytes
+ * of one ACK; call once with bytes = 10.
+ *
+ * For a fragment of a data or management rame that will be followed
+ * by more fragments, the duration captures an ACK, the following
+ * fragment, and its ACK; add the results of three calls, two with
+ * bytes = 10 and one with bytes set to the next fragment's size.
+ *
+ * For an RTS control frame, the duration captures the responding CTS,
+ * the frame being sent, and its ACK; add the results of three calls,
+ * two with bytes = 10 and one with bytes set to the next frame's size
+ * (assuming unfragmented).
+ *
+ * For a CTS-to-self control frame, the duration captures the frame
+ * being protected and its ACK; add the results of two calls, one with
+ * bytes = 10 and one with bytes set to the next frame's size.
+ *
+ * No other frame types are currently supported by gPXE.
+ */
+u16 net80211_duration ( struct net80211_device *dev, int bytes, u16 rate )
+{
+	struct net80211_channel *chan = &dev->channels[dev->channel];
+	u32 kbps = rate * 100;
+
+	if ( chan->band == NET80211_BAND_5GHZ || net80211_rate_is_erp ( rate ) ) {
+		/* OFDM encoding (802.11a/g) */
+		int bits_per_symbol = ( kbps * 4 ) / 1000;	/* 4us/symbol */
+		int bits = 22 + ( bytes << 3 );	/* 22-bit PLCP */
+		int symbols = ( bits + bits_per_symbol - 1 ) / bits_per_symbol;
+
+		return 16 + 20 + ( symbols * 4 ); /* 16us SIFS, 20us preamble */
+	} else {
+		/* CCK encoding (802.11b) */
+		int phy_time = 144 + 48;	/* preamble + PLCP */
+		int bits = bytes << 3;
+		int data_time = ( bits * 1000 + kbps - 1 ) / kbps;
+
+		if ( dev->phy_flags & NET80211_PHY_USE_SHORT_PREAMBLE )
+			phy_time >>= 1;
+
+		return 10 + phy_time + data_time; /* 10us SIFS */
+	}
+}
+
+/**
+ * Add 802.11 link-layer header
+ *
+ * @v netdev		Wrapping network device
+ * @v iobuf		I/O buffer
+ * @v ll_dest		Link-layer destination address
+ * @v ll_source		Link-layer source address
+ * @v net_proto		Network-layer protocol, in network byte order
+ * @ret rc		Return status code
+ *
+ * This adds both the 802.11 frame header and the 802.2 LLC/SNAP
+ * header used on data packets.
+ *
+ * We also check here for state of the link that would make it invalid
+ * to send a data packet; every data packet must pass through here,
+ * and no non-data packet (e.g. management frame) should.
+ */
+static int net80211_ll_push ( struct net_device *netdev,
+			      struct io_buffer *iobuf, const void *ll_dest,
+			      const void *ll_source, uint16_t net_proto )
+{
+	struct net80211_device *dev = netdev->priv;
+	struct ieee80211_frame *hdr = iob_push ( iobuf,
+						 IEEE80211_LLC_HEADER_LEN +
+						 IEEE80211_TYP_FRAME_HEADER_LEN );
+	struct ieee80211_llc_snap_header *lhdr =
+		( void * ) hdr + IEEE80211_TYP_FRAME_HEADER_LEN;
+
+	/* We can't send data packets if we're not associated. */
+	if ( ! ( dev->state & NET80211_ASSOCIATED ) ) {
+		if ( dev->assoc_rc )
+			return dev->assoc_rc;
+		return -ENETUNREACH;
+	}
+
+	hdr->fc = IEEE80211_THIS_VERSION | IEEE80211_TYPE_DATA |
+	    IEEE80211_STYPE_DATA | IEEE80211_FC_TODS;
+
+	/* We don't send fragmented frames, so duration is the time
+	   for an SIFS + 10-byte ACK. */
+	hdr->duration = net80211_duration ( dev, 10, dev->rates[dev->rate] );
+
+	memcpy ( hdr->addr1, dev->bssid, ETH_ALEN );
+	memcpy ( hdr->addr2, ll_source, ETH_ALEN );
+	memcpy ( hdr->addr3, ll_dest, ETH_ALEN );
+
+	hdr->seq = IEEE80211_MAKESEQ ( ++dev->last_tx_seqnr, 0 );
+
+	lhdr->dsap = IEEE80211_LLC_DSAP;
+	lhdr->ssap = IEEE80211_LLC_SSAP;
+	lhdr->ctrl = IEEE80211_LLC_CTRL;
+	memset ( lhdr->oui, 0x00, 3 );
+	lhdr->ethertype = net_proto;
+
+	return 0;
+}
+
+/**
+ * Remove 802.11 link-layer header
+ *
+ * @v netdev		Wrapping network device
+ * @v iobuf		I/O buffer
+ * @ret ll_dest		Link-layer destination address
+ * @ret ll_source	Link-layer source
+ * @ret net_proto	Network-layer protocol, in network byte order
+ * @ret rc		Return status code
+ *
+ * This expects and removes both the 802.11 frame header and the 802.2
+ * LLC/SNAP header that are used on data packets.
+ */
+static int net80211_ll_pull ( struct net_device *netdev __unused,
+			      struct io_buffer *iobuf,
+			      const void **ll_dest, const void **ll_source,
+			      uint16_t * net_proto )
+{
+	struct ieee80211_frame *hdr = iobuf->data;
+	struct ieee80211_llc_snap_header *lhdr =
+		( void * ) hdr + IEEE80211_TYP_FRAME_HEADER_LEN;
+
+	/* Bunch of sanity checks */
+	if ( iob_len ( iobuf ) < IEEE80211_TYP_FRAME_HEADER_LEN +
+	     IEEE80211_LLC_HEADER_LEN ) {
+		DBGC ( netdev->priv, "802.11 %p packet too short (%zd bytes)\n",
+		       netdev->priv, iob_len ( iobuf ) );
+		return -EINVAL_PKT_TOO_SHORT;
+	}
+
+	if ( ( hdr->fc & IEEE80211_FC_VERSION ) != IEEE80211_THIS_VERSION ) {
+		DBGC ( netdev->priv, "802.11 %p packet invalid version %04x\n",
+		       netdev->priv, hdr->fc & IEEE80211_FC_VERSION );
+		return -EINVAL_PKT_VERSION;
+	}
+
+	if ( ( hdr->fc & IEEE80211_FC_TYPE ) != IEEE80211_TYPE_DATA ||
+	     ( hdr->fc & IEEE80211_FC_SUBTYPE ) != IEEE80211_STYPE_DATA ) {
+		DBGC ( netdev->priv, "802.11 %p packet not data/data (fc=%04x)\n",
+		       netdev->priv, hdr->fc );
+		return -EINVAL_PKT_NOT_DATA;
+	}
+
+	if ( ( hdr->fc & ( IEEE80211_FC_TODS | IEEE80211_FC_FROMDS ) ) !=
+	     IEEE80211_FC_FROMDS ) {
+		DBGC ( netdev->priv, "802.11 %p packet not from DS (fc=%04x)\n",
+		       netdev->priv, hdr->fc );
+		return -EINVAL_PKT_NOT_FROMDS;
+	}
+
+	if ( lhdr->dsap != IEEE80211_LLC_DSAP || lhdr->ssap != IEEE80211_LLC_SSAP ||
+	     lhdr->ctrl != IEEE80211_LLC_CTRL || lhdr->oui[0] || lhdr->oui[1] ||
+	     lhdr->oui[2] ) {
+		DBGC ( netdev->priv, "802.11 %p LLC header is not plain EtherType "
+		       "encapsulator: %02x->%02x [%02x] %02x:%02x:%02x %04x\n",
+		       netdev->priv, lhdr->dsap, lhdr->ssap, lhdr->ctrl,
+		       lhdr->oui[0], lhdr->oui[1], lhdr->oui[2], lhdr->ethertype );
+		return -EINVAL_PKT_LLC_HEADER;
+	}
+
+	iob_pull ( iobuf, sizeof ( *hdr ) + sizeof ( *lhdr ) );
+
+	*ll_dest = hdr->addr1;
+	*ll_source = hdr->addr3;
+	*net_proto = lhdr->ethertype;
+	return 0;
+}
+
+/** 802.11 link-layer protocol */
+static struct ll_protocol net80211_ll_protocol __ll_protocol = {
+	.name = "802.11",
+	.push = net80211_ll_push,
+	.pull = net80211_ll_pull,
+	.init_addr = eth_init_addr,
+	.ntoa = eth_ntoa,
+	.mc_hash = eth_mc_hash,
+	.eth_addr = eth_eth_addr,
+	.ll_proto = htons ( ARPHRD_ETHER ),	/* "encapsulated Ethernet" */
+	.hw_addr_len = ETH_ALEN,
+	.ll_addr_len = ETH_ALEN,
+	.ll_header_len = IEEE80211_TYP_FRAME_HEADER_LEN +
+				IEEE80211_LLC_HEADER_LEN,
+};
+
+
+/* ---------- 802.11 network management API ---------- */
+
+/**
+ * Get 802.11 device from wrapping network device
+ *
+ * @v netdev	Wrapping network device
+ * @ret dev	802.11 device wrapped by network device, or NULL
+ *
+ * Returns NULL if the network device does not wrap an 802.11 device.
+ */
+struct net80211_device * net80211_get ( struct net_device *netdev )
+{
+	struct net80211_device *dev;
+
+	list_for_each_entry ( dev, &net80211_devices, list ) {
+		if ( netdev->priv == dev )
+			return netdev->priv;
+	}
+
+	return NULL;
+}
+
+/**
+ * Set state of 802.11 device keeping management frames
+ *
+ * @v dev	802.11 device
+ * @v enable	Whether to keep management frames
+ * @ret oldenab	Whether management frames were enabled before this call
+ *
+ * If enable is TRUE, beacon, probe, and action frames will be kept
+ * and may be retrieved by calling net80211_mgmt_dequeue().
+ */
+int net80211_keep_mgmt ( struct net80211_device *dev, int enable )
+{
+	int oldenab = dev->keep_mgmt;
+
+	dev->keep_mgmt = enable;
+	return oldenab;
+}
+
+/**
+ * Get 802.11 management frame
+ *
+ * @v dev	802.11 device
+ * @ret signal	Signal strength of returned management frame
+ * @ret iob	I/O buffer, or NULL if no management frame is queued
+ *
+ * Frames will only be returned by this function if
+ * net80211_keep_mgmt() has been previously called with enable set to
+ * TRUE.
+ *
+ * The calling function takes ownership of the returned I/O buffer.
+ */
+struct io_buffer * net80211_mgmt_dequeue ( struct net80211_device *dev,
+					   int *signal )
+{
+	struct io_buffer *iobuf;
+	struct net80211_rx_info *rxi;
+
+	list_for_each_entry ( rxi, &dev->mgmt_info_queue, list ) {
+		list_del ( &rxi->list );
+		if ( signal )
+			*signal = rxi->signal;
+		free ( rxi );
+
+		list_for_each_entry ( iobuf, &dev->mgmt_queue, list ) {
+			list_del ( &iobuf->list );
+			return iobuf;
+		}
+		assert ( 0 );
+	}
+
+	return NULL;
+}
+
+/**
+ * Transmit 802.11 management frame
+ *
+ * @v dev	802.11 device
+ * @v fc	Frame Control flags for management frame
+ * @v dest	Destination access point
+ * @v iob	I/O buffer
+ * @ret rc	Return status code
+ *
+ * The @a fc argument must contain at least an IEEE 802.11 management
+ * subtype number (e.g. IEEE80211_STYPE_PROBE_REQ). If it contains
+ * IEEE80211_FC_PROTECTED, the frame will be encrypted prior to
+ * transmission.
+ *
+ * It is required that @a iob have at least 24 bytes of headroom
+ * reserved before its data start.
+ */
+int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc, u8 dest[6],
+		       struct io_buffer *iob )
+{
+	struct ieee80211_frame *hdr = iob_push ( iob,
+						 IEEE80211_TYP_FRAME_HEADER_LEN );
+
+	hdr->fc = IEEE80211_THIS_VERSION | IEEE80211_TYPE_MGMT |
+	    ( fc & ~IEEE80211_FC_PROTECTED );
+	hdr->duration = net80211_duration ( dev, 10, dev->rates[dev->rate] );
+	hdr->seq = IEEE80211_MAKESEQ ( ++dev->last_tx_seqnr, 0 );
+
+	memcpy ( hdr->addr1, dest, ETH_ALEN );	/* DA = RA */
+	memcpy ( hdr->addr2, dev->netdev->ll_addr, ETH_ALEN );	/* SA = TA */
+	memcpy ( hdr->addr3, dest, ETH_ALEN );	/* BSSID */
+
+	if ( fc & IEEE80211_FC_PROTECTED ) {
+		if ( ! dev->crypto )
+			return -EINVAL_CRYPTO_REQUEST;
+
+		struct io_buffer *eiob = dev->crypto->encrypt ( dev->crypto,
+								iob );
+		free_iob ( iob );
+		iob = eiob;
+	}
+
+	return netdev_tx ( dev->netdev, iob );
+}
+
+
+/* ---------- Driver API ---------- */
+
+/**
+ * Allocate 802.11 device
+ *
+ * @v priv_size		Size of driver-private allocation area
+ * @ret dev		Newly allocated 802.11 device
+ *
+ * This function allocates a net_device with space in its private area
+ * for both the net80211_device it will wrap and the driver-private
+ * data space requested. It initializes the link-layer-specific parts
+ * of the net_device, and links the net80211_device to the net_device
+ * appropriately.
+ */
+struct net80211_device * net80211_alloc ( size_t priv_size )
+{
+	struct net80211_device *dev;
+	struct net_device *netdev =
+		alloc_netdev ( sizeof ( *dev ) + priv_size );
+
+	if ( ! netdev )
+		return NULL;
+
+	netdev->ll_protocol = &net80211_ll_protocol;
+	netdev->ll_broadcast = net80211_ll_broadcast;
+	netdev->max_pkt_len = IEEE80211_MAX_DATA_LEN;
+	netdev_init ( netdev, &net80211_netdev_ops );
+
+	dev = netdev->priv;
+	dev->netdev = netdev;
+	dev->priv = ( u8 * ) dev + sizeof ( *dev );
+	dev->op = &net80211_null_ops;
+
+	process_init_stopped ( &dev->proc_assoc, net80211_step_associate,
+			       &netdev->refcnt );
+	INIT_LIST_HEAD ( &dev->mgmt_queue );
+	INIT_LIST_HEAD ( &dev->mgmt_info_queue );
+
+	return dev;
+}
+
+/**
+ * Register 802.11 device with network stack
+ *
+ * @v dev	802.11 device
+ * @v ops	802.11 device operations
+ * @v hw	802.11 hardware information
+ *
+ * This also registers the wrapping net_device with the higher network
+ * layers.
+ */
+int net80211_register ( struct net80211_device *dev,
+			struct net80211_device_operations *ops,
+			struct net80211_hw_info *hw )
+{
+	dev->op = ops;
+	dev->hw = malloc ( sizeof ( *hw ) );
+	if ( ! dev->hw )
+		return -ENOMEM;
+
+	memcpy ( dev->hw, hw, sizeof ( *hw ) );
+	memcpy ( dev->netdev->hw_addr, hw->hwaddr, ETH_ALEN );
+
+	/* Set some sensible channel defaults for driver's open() function */
+	memcpy ( dev->channels, dev->hw->channels,
+		 NET80211_MAX_CHANNELS * sizeof ( dev->channels[0] ) );
+	dev->channel = 0;
+
+	list_add_tail ( &dev->list, &net80211_devices );
+	return register_netdev ( dev->netdev );
+}
+
+/**
+ * Unregister 802.11 device from network stack
+ *
+ * @v dev	802.11 device
+ *
+ * After this call, the device operations are cleared so that they
+ * will not be called.
+ */
+void net80211_unregister ( struct net80211_device *dev )
+{
+	unregister_netdev ( dev->netdev );
+	list_del ( &dev->list );
+	dev->op = &net80211_null_ops;
+}
+
+/**
+ * Free 802.11 device
+ *
+ * @v dev	802.11 device
+ *
+ * The device should be unregistered before this function is called.
+ */
+void net80211_free ( struct net80211_device *dev )
+{
+	free ( dev->hw );
+	rc80211_free ( dev->rctl );
+	netdev_nullify ( dev->netdev );
+	netdev_put ( dev->netdev );
+}
+
+
+/* ---------- 802.11 network management workhorse code ---------- */
+
+/**
+ * Set state of 802.11 device
+ *
+ * @v dev	802.11 device
+ * @v clear	Bitmask of flags to clear
+ * @v set	Bitmask of flags to set
+ * @v status	Status or reason code for most recent operation
+ *
+ * If @a status represents a reason code, it should be OR'ed with
+ * NET80211_IS_REASON.
+ *
+ * Clearing authentication also clears association; clearing
+ * association also clears security handshaking state. Clearing
+ * association removes the link-up flag from the wrapping net_device,
+ * but setting it does not automatically set the flag; that is left to
+ * the judgment of higher-level code.
+ */
+static inline void net80211_set_state ( struct net80211_device *dev,
+					short clear, short set,
+					u16 status )
+{
+	/* The conditions in this function are deliberately formulated
+	   to be decidable at compile-time in most cases. Since clear
+	   and set are generally passed as constants, the body of this
+	   function can be reduced down to a few statements by the
+	   compiler. */
+
+	const int statmsk = NET80211_STATUS_MASK | NET80211_IS_REASON;
+
+	if ( clear & NET80211_PROBED )
+		clear |= NET80211_AUTHENTICATED;
+
+	if ( clear & NET80211_AUTHENTICATED )
+		clear |= NET80211_ASSOCIATED;
+
+	if ( clear & NET80211_ASSOCIATED )
+		clear |= NET80211_CRYPTO_SYNCED;
+
+	dev->state = ( dev->state & ~clear ) | set;
+	dev->state = ( dev->state & ~statmsk ) | ( status & statmsk );
+
+	if ( clear & NET80211_ASSOCIATED )
+		netdev_link_down ( dev->netdev );
+
+	if ( ( clear | set ) & NET80211_ASSOCIATED )
+		dev->op->config ( dev, NET80211_CFG_ASSOC );
+
+	if ( status != 0 ) {
+		if ( status & NET80211_IS_REASON )
+			dev->assoc_rc = -E80211_REASON ( status );
+		else
+			dev->assoc_rc = -E80211_STATUS ( status );
+		netdev_link_err ( dev->netdev, dev->assoc_rc );
+	}
+}
+
+/**
+ * Add channels to 802.11 device
+ *
+ * @v dev	802.11 device
+ * @v start	First channel number to add
+ * @v len	Number of channels to add
+ * @v txpower	TX power (dBm) to allow on added channels
+ *
+ * To replace the current list of channels instead of adding to it,
+ * set the nr_channels field of the 802.11 device to 0 before calling
+ * this function.
+ */
+static void net80211_add_channels ( struct net80211_device *dev, int start,
+				    int len, int txpower )
+{
+	int i, chan = start;
+
+	for ( i = dev->nr_channels; len-- && i < NET80211_MAX_CHANNELS; i++ ) {
+		dev->channels[i].channel_nr = chan;
+		dev->channels[i].maxpower = txpower;
+		dev->channels[i].hw_value = 0;
+
+		if ( chan >= 1 && chan <= 14 ) {
+			dev->channels[i].band = NET80211_BAND_2GHZ;
+			if ( chan == 14 )
+				dev->channels[i].center_freq = 2484;
+			else
+				dev->channels[i].center_freq = 2407 + 5 * chan;
+			chan++;
+		} else {
+			dev->channels[i].band = NET80211_BAND_5GHZ;
+			dev->channels[i].center_freq = 5000 + 5 * chan;
+			chan += 4;
+		}
+	}
+
+	dev->nr_channels = i;
+}
+
+/**
+ * Filter 802.11 device channels for hardware capabilities
+ *
+ * @v dev	802.11 device
+ *
+ * Hardware may support fewer channels than regulatory restrictions
+ * allow; this function filters out channels in dev->channels that are
+ * not supported by the hardware list in dev->hwinfo. It also copies
+ * over the net80211_channel::hw_value and limits maximum TX power
+ * appropriately.
+ *
+ * Channels are matched based on center frequency, ignoring band and
+ * channel number.
+ *
+ * If the driver specifies no supported channels, the effect will be
+ * as though all were supported.
+ */
+static void net80211_filter_hw_channels ( struct net80211_device *dev )
+{
+	int delta = 0, i = 0;
+	int old_freq = dev->channels[dev->channel].center_freq;
+	struct net80211_channel *chan, *hwchan;
+
+	if ( ! dev->hw->nr_channels )
+		return;
+
+	dev->channel = 0;
+	for ( chan = dev->channels; chan < dev->channels + dev->nr_channels;
+	      chan++, i++ ) {
+		int ok = 0;
+		for ( hwchan = dev->hw->channels;
+		      hwchan < dev->hw->channels + dev->hw->nr_channels;
+		      hwchan++ ) {
+			if ( hwchan->center_freq == chan->center_freq ) {
+				ok = 1;
+				break;
+			}
+		}
+
+		if ( ! ok )
+			delta++;
+		else {
+			chan->hw_value = hwchan->hw_value;
+			if ( hwchan->maxpower != 0 &&
+			     chan->maxpower > hwchan->maxpower )
+				chan->maxpower = hwchan->maxpower;
+			if ( old_freq == chan->center_freq )
+				dev->channel = i - delta;
+			if ( delta )
+				chan[-delta] = *chan;
+		}
+	}
+
+	dev->nr_channels -= delta;
+
+	if ( dev->channels[dev->channel].center_freq != old_freq )
+		dev->op->config ( dev, NET80211_CFG_CHANNEL );
+}
+
+/**
+ * Update 802.11 device state to reflect received capabilities field
+ *
+ * @v dev	802.11 device
+ * @v capab	Capabilities field in beacon, probe, or association frame
+ * @ret rc	Return status code
+ */
+static int net80211_process_capab ( struct net80211_device *dev,
+				    u16 capab )
+{
+	u16 old_phy = dev->phy_flags;
+
+	if ( ( capab & ( IEEE80211_CAPAB_MANAGED | IEEE80211_CAPAB_ADHOC ) ) !=
+	     IEEE80211_CAPAB_MANAGED ) {
+		DBGC ( dev, "802.11 %p cannot handle IBSS network\n", dev );
+		return -ENOSYS;
+	}
+
+	dev->phy_flags &= ~( NET80211_PHY_USE_SHORT_PREAMBLE |
+			     NET80211_PHY_USE_SHORT_SLOT );
+
+	if ( capab & IEEE80211_CAPAB_SHORT_PMBL )
+		dev->phy_flags |= NET80211_PHY_USE_SHORT_PREAMBLE;
+
+	if ( capab & IEEE80211_CAPAB_SHORT_SLOT )
+		dev->phy_flags |= NET80211_PHY_USE_SHORT_SLOT;
+
+	if ( old_phy != dev->phy_flags )
+		dev->op->config ( dev, NET80211_CFG_PHY_PARAMS );
+
+	return 0;
+}
+
+/**
+ * Update 802.11 device state to reflect received information elements
+ *
+ * @v dev	802.11 device
+ * @v ie	Pointer to first information element
+ * @v ie_end	Pointer to tail of packet I/O buffer
+ * @ret rc	Return status code
+ */
+static int net80211_process_ie ( struct net80211_device *dev,
+				 union ieee80211_ie *ie, void *ie_end )
+{
+	u16 old_rate = dev->rates[dev->rate];
+	u16 old_phy = dev->phy_flags;
+	int have_rates = 0, i;
+	int ds_channel = 0;
+	int changed = 0;
+	int band = dev->channels[dev->channel].band;
+
+	if ( ! ieee80211_ie_bound ( ie, ie_end ) )
+		return 0;
+
+	for ( ; ie; ie = ieee80211_next_ie ( ie, ie_end ) ) {
+		switch ( ie->id ) {
+		case IEEE80211_IE_SSID:
+			if ( ie->len <= 32 ) {
+				memcpy ( dev->essid, ie->ssid, ie->len );
+				dev->essid[ie->len] = 0;
+			}
+			break;
+
+		case IEEE80211_IE_RATES:
+		case IEEE80211_IE_EXT_RATES:
+			if ( ! have_rates ) {
+				dev->nr_rates = 0;
+				dev->basic_rates = 0;
+				have_rates = 1;
+			}
+			for ( i = 0; i < ie->len &&
+			      dev->nr_rates < NET80211_MAX_RATES; i++ ) {
+				u8 rid = ie->rates[i];
+				u16 rate = ( rid & 0x7f ) * 5;
+
+				if ( rid & 0x80 )
+					dev->basic_rates |=
+						( 1 << dev->nr_rates );
+
+				dev->rates[dev->nr_rates++] = rate;
+			}
+
+			break;
+
+		case IEEE80211_IE_DS_PARAM:
+			if ( dev->channel < dev->nr_channels && ds_channel ==
+			     dev->channels[dev->channel].channel_nr )
+				break;
+			ds_channel = ie->ds_param.current_channel;
+			net80211_change_channel ( dev, ds_channel );
+			break;
+
+		case IEEE80211_IE_COUNTRY:
+			dev->nr_channels = 0;
+
+			DBGC ( dev, "802.11 %p setting country regulations "
+			       "for %c%c\n", dev, ie->country.name[0],
+			       ie->country.name[1] );
+			for ( i = 0; i < ( ie->len - 3 ) / 3; i++ ) {
+				union ieee80211_ie_country_triplet *t =
+					&ie->country.triplet[i];
+				if ( t->first > 200 ) {
+					DBGC ( dev, "802.11 %p ignoring regulatory "
+					       "extension information\n", dev );
+				} else {
+					net80211_add_channels ( dev,
+							t->band.first_channel,
+							t->band.nr_channels,
+							t->band.max_txpower );
+				}
+			}
+			net80211_filter_hw_channels ( dev );
+			break;
+
+		case IEEE80211_IE_ERP_INFO:
+			dev->phy_flags &= ~( NET80211_PHY_USE_PROTECTION |
+					     NET80211_PHY_USE_SHORT_PREAMBLE );
+			if ( ie->erp_info & IEEE80211_ERP_USE_PROTECTION )
+				dev->phy_flags |= NET80211_PHY_USE_PROTECTION;
+			if ( ! ( ie->erp_info & IEEE80211_ERP_BARKER_LONG ) )
+				dev->phy_flags |= NET80211_PHY_USE_SHORT_PREAMBLE;
+			break;
+		}
+	}
+
+	if ( have_rates ) {
+		/* Allow only those rates that are also supported by
+		   the hardware. */
+		int delta = 0, j;
+
+		dev->rate = 0;
+		for ( i = 0; i < dev->nr_rates; i++ ) {
+			int ok = 0;
+			for ( j = 0; j < dev->hw->nr_rates[band]; j++ ) {
+				if ( dev->hw->rates[band][j] == dev->rates[i] ){
+					ok = 1;
+					break;
+				}
+			}
+
+			if ( ! ok )
+				delta++;
+			else {
+				dev->rates[i - delta] = dev->rates[i];
+				if ( old_rate == dev->rates[i] )
+					dev->rate = i - delta;
+			}
+		}
+
+		dev->nr_rates -= delta;
+
+		/* Sort available rates - sorted subclumps tend to already
+		   exist, so insertion sort works well. */
+		for ( i = 1; i < dev->nr_rates; i++ ) {
+			u16 rate = dev->rates[i];
+			u32 tmp, br, mask;
+
+			for ( j = i - 1; j >= 0 && dev->rates[j] >= rate; j-- )
+				dev->rates[j + 1] = dev->rates[j];
+			dev->rates[j + 1] = rate;
+
+			/* Adjust basic_rates to match by rotating the
+			   bits from bit j+1 to bit i left one position. */
+			mask = ( ( 1 << i ) - 1 ) & ~( ( 1 << ( j + 1 ) ) - 1 );
+			br = dev->basic_rates;
+			tmp = br & ( 1 << i );
+			br = ( br & ~( mask | tmp ) ) | ( ( br & mask ) << 1 );
+			br |= ( tmp >> ( i - j - 1 ) );
+			dev->basic_rates = br;
+		}
+
+		net80211_set_rtscts_rate ( dev );
+
+		if ( dev->rates[dev->rate] != old_rate )
+			changed |= NET80211_CFG_RATE;
+	}
+
+	if ( dev->hw->flags & NET80211_HW_NO_SHORT_PREAMBLE )
+		dev->phy_flags &= ~NET80211_PHY_USE_SHORT_PREAMBLE;
+	if ( dev->hw->flags & NET80211_HW_NO_SHORT_SLOT )
+		dev->phy_flags &= ~NET80211_PHY_USE_SHORT_SLOT;
+
+	if ( old_phy != dev->phy_flags )
+		changed |= NET80211_CFG_PHY_PARAMS;
+
+	if ( changed )
+		dev->op->config ( dev, changed );
+
+	return 0;
+}
+
+/**
+ * Create information elements for outgoing probe or association packet
+ *
+ * @v dev		802.11 device
+ * @v ie		Pointer to start of information element area
+ * @ret next_ie		Pointer to first byte after added information elements
+ */
+static union ieee80211_ie *
+net80211_marshal_request_info ( struct net80211_device *dev,
+				union ieee80211_ie *ie )
+{
+	int i;
+
+	ie->id = IEEE80211_IE_SSID;
+	ie->len = strlen ( dev->essid );
+	memcpy ( ie->ssid, dev->essid, ie->len );
+
+	ie = ieee80211_next_ie ( ie, NULL );
+
+	ie->id = IEEE80211_IE_RATES;
+	ie->len = dev->nr_rates;
+	if ( ie->len > 8 )
+		ie->len = 8;
+
+	for ( i = 0; i < ie->len; i++ ) {
+		ie->rates[i] = dev->rates[i] / 5;
+		if ( dev->basic_rates & ( 1 << i ) )
+			ie->rates[i] |= 0x80;
+	}
+
+	ie = ieee80211_next_ie ( ie, NULL );
+
+	if ( dev->rsn_ie && dev->rsn_ie->id == IEEE80211_IE_RSN ) {
+		memcpy ( ie, dev->rsn_ie, dev->rsn_ie->len + 2 );
+		ie = ieee80211_next_ie ( ie, NULL );
+	}
+
+	if ( dev->nr_rates > 8 ) {
+		/* 802.11 requires we use an Extended Basic Rates IE
+		   for the rates beyond the eighth. */
+
+		ie->id = IEEE80211_IE_EXT_RATES;
+		ie->len = dev->nr_rates - 8;
+
+		for ( ; i < dev->nr_rates; i++ ) {
+			ie->rates[i - 8] = dev->rates[i] / 5;
+			if ( dev->basic_rates & ( 1 << i ) )
+				ie->rates[i - 8] |= 0x80;
+		}
+
+		ie = ieee80211_next_ie ( ie, NULL );
+	}
+
+	if ( dev->rsn_ie && dev->rsn_ie->id == IEEE80211_IE_VENDOR ) {
+		memcpy ( ie, dev->rsn_ie, dev->rsn_ie->len + 2 );
+		ie = ieee80211_next_ie ( ie, NULL );
+	}
+
+	return ie;
+}
+
+/** Seconds to wait after finding a network, to possibly find better APs for it
+ *
+ * This is used when a specific SSID to scan for is specified.
+ */
+#define NET80211_PROBE_GATHER    1
+
+/** Seconds to wait after finding a network, to possibly find other networks
+ *
+ * This is used when an empty SSID is specified, to scan for all
+ * networks.
+ */
+#define NET80211_PROBE_GATHER_ALL 2
+
+/** Seconds to allow a probe to take if no network has been found */
+#define NET80211_PROBE_TIMEOUT   6
+
+/**
+ * Begin probe of 802.11 networks
+ *
+ * @v dev	802.11 device
+ * @v essid	SSID to probe for, or "" to accept any (may not be NULL)
+ * @v active	Whether to use active scanning
+ * @ret ctx	Probe context
+ *
+ * Active scanning may only be used on channels 1-11 in the 2.4GHz
+ * band, due to gPXE's lack of a complete regulatory database. If
+ * active scanning is used, probe packets will be sent on each
+ * channel; this can allow association with hidden-SSID networks if
+ * the SSID is properly specified.
+ *
+ * A @c NULL return indicates an out-of-memory condition.
+ *
+ * The returned context must be periodically passed to
+ * net80211_probe_step() until that function returns zero.
+ */
+struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev,
+						   const char *essid,
+						   int active )
+{
+	struct net80211_probe_ctx *ctx = zalloc ( sizeof ( *ctx ) );
+
+	if ( ! ctx )
+		return NULL;
+
+	assert ( dev->netdev->state & NETDEV_OPEN );
+
+	ctx->dev = dev;
+	ctx->old_keep_mgmt = net80211_keep_mgmt ( dev, 1 );
+	ctx->essid = essid;
+	if ( dev->essid != ctx->essid )
+		strcpy ( dev->essid, ctx->essid );
+
+	if ( active ) {
+		struct ieee80211_probe_req *probe_req;
+		union ieee80211_ie *ie;
+
+		ctx->probe = alloc_iob ( 128 );
+		iob_reserve ( ctx->probe, IEEE80211_TYP_FRAME_HEADER_LEN );
+		probe_req = ctx->probe->data;
+
+		ie = net80211_marshal_request_info ( dev,
+						     probe_req->info_element );
+
+		iob_put ( ctx->probe, ( void * ) ie - ctx->probe->data );
+	}
+
+	ctx->ticks_start = currticks();
+	ctx->ticks_beacon = 0;
+	ctx->ticks_channel = currticks();
+	ctx->hop_time = ticks_per_sec() / ( active ? 2 : 6 );
+
+	/*
+	 * Channels on 2.4GHz overlap, and the most commonly used
+	 * are 1, 6, and 11. We'll get a result faster if we check
+	 * every 5 channels, but in order to hit all of them the
+	 * number of channels must be relatively prime to 5. If it's
+	 * not, tweak the hop.
+	 */
+	ctx->hop_step = 5;
+	while ( dev->nr_channels % ctx->hop_step == 0 && ctx->hop_step > 1 )
+		ctx->hop_step--;
+
+	ctx->beacons = malloc ( sizeof ( *ctx->beacons ) );
+	INIT_LIST_HEAD ( ctx->beacons );
+
+	dev->channel = 0;
+	dev->op->config ( dev, NET80211_CFG_CHANNEL );
+
+	return ctx;
+}
+
+/**
+ * Continue probe of 802.11 networks
+ *
+ * @v ctx	Probe context returned by net80211_probe_start()
+ * @ret rc	Probe status
+ *
+ * The return code will be 0 if the probe is still going on (and this
+ * function should be called again), a positive number if the probe
+ * completed successfully, or a negative error code if the probe
+ * failed for that reason.
+ *
+ * Whether the probe succeeded or failed, you must call
+ * net80211_probe_finish_all() or net80211_probe_finish_best()
+ * (depending on whether you want information on all networks or just
+ * the best-signal one) in order to release the probe context. A
+ * failed probe may still have acquired some valid data.
+ */
+int net80211_probe_step ( struct net80211_probe_ctx *ctx )
+{
+	struct net80211_device *dev = ctx->dev;
+	u32 start_timeout = NET80211_PROBE_TIMEOUT * ticks_per_sec();
+	u32 gather_timeout = ticks_per_sec();
+	u32 now = currticks();
+	struct io_buffer *iob;
+	int signal;
+	int rc;
+	char ssid[IEEE80211_MAX_SSID_LEN + 1];
+
+	gather_timeout *= ( ctx->essid[0] ? NET80211_PROBE_GATHER :
+			    NET80211_PROBE_GATHER_ALL );
+
+	/* Time out if necessary */
+	if ( now >= ctx->ticks_start + start_timeout )
+		return list_empty ( ctx->beacons ) ? -ETIMEDOUT : +1;
+
+	if ( ctx->ticks_beacon > 0 && now >= ctx->ticks_start + gather_timeout )
+		return +1;
+
+	/* Change channels if necessary */
+	if ( now >= ctx->ticks_channel + ctx->hop_time ) {
+		dev->channel = ( dev->channel + ctx->hop_step )
+			% dev->nr_channels;
+		dev->op->config ( dev, NET80211_CFG_CHANNEL );
+		udelay ( dev->hw->channel_change_time );
+
+		ctx->ticks_channel = now;
+
+		if ( ctx->probe ) {
+			struct io_buffer *siob = ctx->probe; /* to send */
+
+			/* make a copy for future use */
+			iob = alloc_iob ( siob->tail - siob->head );
+			iob_reserve ( iob, iob_headroom ( siob ) );
+			memcpy ( iob_put ( iob, iob_len ( siob ) ),
+				 siob->data, iob_len ( siob ) );
+
+			ctx->probe = iob;
+			rc = net80211_tx_mgmt ( dev, IEEE80211_STYPE_PROBE_REQ,
+						net80211_ll_broadcast,
+						iob_disown ( siob ) );
+			if ( rc ) {
+				DBGC ( dev, "802.11 %p send probe failed: "
+				       "%s\n", dev, strerror ( rc ) );
+				return rc;
+			}
+		}
+	}
+
+	/* Check for new management packets */
+	while ( ( iob = net80211_mgmt_dequeue ( dev, &signal ) ) != NULL ) {
+		struct ieee80211_frame *hdr;
+		struct ieee80211_beacon *beacon;
+		union ieee80211_ie *ie;
+		struct net80211_wlan *wlan;
+		u16 type;
+
+		hdr = iob->data;
+		type = hdr->fc & IEEE80211_FC_SUBTYPE;
+		beacon = ( struct ieee80211_beacon * ) hdr->data;
+
+		if ( type != IEEE80211_STYPE_BEACON &&
+		     type != IEEE80211_STYPE_PROBE_RESP ) {
+			DBGC2 ( dev, "802.11 %p probe: non-beacon\n", dev );
+			goto drop;
+		}
+
+		if ( ( void * ) beacon->info_element >= iob->tail ) {
+			DBGC ( dev, "802.11 %p probe: beacon with no IEs\n",
+			       dev );
+			goto drop;
+		}
+
+		ie = beacon->info_element;
+
+		if ( ! ieee80211_ie_bound ( ie, iob->tail ) )
+			ie = NULL;
+
+		while ( ie && ie->id != IEEE80211_IE_SSID )
+			ie = ieee80211_next_ie ( ie, iob->tail );
+
+		if ( ! ie ) {
+			DBGC ( dev, "802.11 %p probe: beacon with no SSID\n",
+			       dev );
+			goto drop;
+		}
+
+		memcpy ( ssid, ie->ssid, ie->len );
+		ssid[ie->len] = 0;
+
+		if ( ctx->essid[0] && strcmp ( ctx->essid, ssid ) != 0 ) {
+			DBGC2 ( dev, "802.11 %p probe: beacon with wrong SSID "
+				"(%s)\n", dev, ssid );
+			goto drop;
+		}
+
+		/* See if we've got an entry for this network */
+		list_for_each_entry ( wlan, ctx->beacons, list ) {
+			if ( strcmp ( wlan->essid, ssid ) != 0 )
+				continue;
+
+			if ( signal < wlan->signal ) {
+				DBGC2 ( dev, "802.11 %p probe: beacon for %s "
+					"(%s) with weaker signal %d\n", dev,
+					ssid, eth_ntoa ( hdr->addr3 ), signal );
+				goto drop;
+			}
+
+			goto fill;
+		}
+
+		/* No entry yet - make one */
+		wlan = zalloc ( sizeof ( *wlan ) );
+		strcpy ( wlan->essid, ssid );
+		list_add_tail ( &wlan->list, ctx->beacons );
+
+		/* Whether we're using an old entry or a new one, fill
+		   it with new data. */
+	fill:
+		memcpy ( wlan->bssid, hdr->addr3, ETH_ALEN );
+		wlan->signal = signal;
+		wlan->channel = dev->channels[dev->channel].channel_nr;
+
+		/* Copy this I/O buffer into a new wlan->beacon; the
+		 * iob we've got probably came from the device driver
+		 * and may have the full 2.4k allocation, which we
+		 * don't want to keep around wasting memory.
+		 */
+		free_iob ( wlan->beacon );
+		wlan->beacon = alloc_iob ( iob_len ( iob ) );
+		memcpy ( iob_put ( wlan->beacon, iob_len ( iob ) ),
+			 iob->data, iob_len ( iob ) );
+
+		if ( ( rc = sec80211_detect ( wlan->beacon, &wlan->handshaking,
+					      &wlan->crypto ) ) == -ENOTSUP ) {
+			struct ieee80211_beacon *beacon =
+				( struct ieee80211_beacon * ) hdr->data;
+
+			if ( beacon->capability & IEEE80211_CAPAB_PRIVACY ) {
+				DBG ( "802.11 %p probe: secured network %s but "
+				      "encryption support not compiled in\n",
+				      dev, wlan->essid );
+				wlan->handshaking = NET80211_SECPROT_UNKNOWN;
+				wlan->crypto = NET80211_CRYPT_UNKNOWN;
+			} else {
+				wlan->handshaking = NET80211_SECPROT_NONE;
+				wlan->crypto = NET80211_CRYPT_NONE;
+			}
+		} else if ( rc != 0 ) {
+			DBGC ( dev, "802.11 %p probe warning: network "
+			       "%s with unidentifiable security "
+			       "settings: %s\n", dev, wlan->essid,
+			       strerror ( rc ) );
+		}
+
+		ctx->ticks_beacon = now;
+
+		DBGC2 ( dev, "802.11 %p probe: good beacon for %s (%s)\n",
+			dev, wlan->essid, eth_ntoa ( wlan->bssid ) );
+
+	drop:
+		free_iob ( iob );
+	}
+
+	return 0;
+}
+
+
+/**
+ * Finish probe of 802.11 networks, returning best-signal network found
+ *
+ * @v ctx	Probe context
+ * @ret wlan	Best-signal network found, or @c NULL if none were found
+ *
+ * If net80211_probe_start() was called with a particular SSID
+ * parameter as filter, only a network with that SSID (matching
+ * case-sensitively) can be returned from this function.
+ */
+struct net80211_wlan *
+net80211_probe_finish_best ( struct net80211_probe_ctx *ctx )
+{
+	struct net80211_wlan *best = NULL, *wlan;
+
+	if ( ! ctx )
+		return NULL;
+
+	list_for_each_entry ( wlan, ctx->beacons, list ) {
+		if ( ! best || best->signal < wlan->signal )
+			best = wlan;
+	}
+
+	if ( best )
+		list_del ( &best->list );
+	else
+		DBGC ( ctx->dev, "802.11 %p probe: found nothing for '%s'\n",
+		       ctx->dev, ctx->essid );
+
+	net80211_free_wlanlist ( ctx->beacons );
+
+	net80211_keep_mgmt ( ctx->dev, ctx->old_keep_mgmt );
+
+	if ( ctx->probe )
+		free_iob ( ctx->probe );
+
+	free ( ctx );
+
+	return best;
+}
+
+
+/**
+ * Finish probe of 802.11 networks, returning all networks found
+ *
+ * @v ctx	Probe context
+ * @ret list	List of net80211_wlan detailing networks found
+ *
+ * If net80211_probe_start() was called with a particular SSID
+ * parameter as filter, this will always return either an empty or a
+ * one-element list.
+ */
+struct list_head *net80211_probe_finish_all ( struct net80211_probe_ctx *ctx )
+{
+	struct list_head *beacons = ctx->beacons;
+
+	if ( ! ctx )
+		return NULL;
+
+	net80211_keep_mgmt ( ctx->dev, ctx->old_keep_mgmt );
+
+	if ( ctx->probe )
+		free_iob ( ctx->probe );
+
+	free ( ctx );
+
+	return beacons;
+}
+
+
+/**
+ * Free WLAN structure
+ *
+ * @v wlan	WLAN structure to free
+ */
+void net80211_free_wlan ( struct net80211_wlan *wlan )
+{
+	if ( wlan ) {
+		free_iob ( wlan->beacon );
+		free ( wlan );
+	}
+}
+
+
+/**
+ * Free list of WLAN structures
+ *
+ * @v list	List of WLAN structures to free
+ */
+void net80211_free_wlanlist ( struct list_head *list )
+{
+	struct net80211_wlan *wlan, *tmp;
+
+	if ( ! list )
+		return;
+
+	list_for_each_entry_safe ( wlan, tmp, list, list ) {
+		list_del ( &wlan->list );
+		net80211_free_wlan ( wlan );
+	}
+
+	free ( list );
+}
+
+
+/** Number of ticks to wait for replies to association management frames */
+#define ASSOC_TIMEOUT	TICKS_PER_SEC
+
+/** Number of times to try sending a particular association management frame */
+#define ASSOC_RETRIES	2
+
+/**
+ * Step 802.11 association process
+ *
+ * @v proc	Association process
+ */
+static void net80211_step_associate ( struct process *proc )
+{
+	struct net80211_device *dev =
+	    container_of ( proc, struct net80211_device, proc_assoc );
+	int rc = 0;
+	int status = dev->state & NET80211_STATUS_MASK;
+
+	/*
+	 * We use a sort of state machine implemented using bits in
+	 * the dev->state variable. At each call, we take the
+	 * logically first step that has not yet succeeded; either it
+	 * has not been tried yet, it's being retried, or it failed.
+	 * If it failed, we return an error indication; otherwise we
+	 * perform the step. If it succeeds, RX handling code will set
+	 * the appropriate status bit for us.
+	 *
+	 * Probe works a bit differently, since we have to step it
+	 * on every call instead of waiting for a packet to arrive
+	 * that will set the completion bit for us.
+	 */
+
+	/* If we're waiting for a reply, check for timeout condition */
+	if ( dev->state & NET80211_WAITING ) {
+		/* Sanity check */
+		if ( ! dev->associating )
+			return;
+
+		if ( currticks() - dev->ctx.assoc->last_packet > ASSOC_TIMEOUT ) {
+			/* Timed out - fail if too many retries, or retry */
+			dev->ctx.assoc->times_tried++;
+			if ( ++dev->ctx.assoc->times_tried > ASSOC_RETRIES ) {
+				rc = -ETIMEDOUT;
+				goto fail;
+			}
+		} else {
+			/* Didn't time out - let it keep going */
+			return;
+		}
+	} else {
+		if ( dev->state & NET80211_PROBED )
+			dev->ctx.assoc->times_tried = 0;
+	}
+
+	if ( ! ( dev->state & NET80211_PROBED ) ) {
+		/* state: probe */
+
+		if ( ! dev->ctx.probe ) {
+			/* start probe */
+			int active = fetch_intz_setting ( NULL,
+						&net80211_active_setting );
+			int band = dev->hw->bands;
+
+			if ( active )
+				band &= ~NET80211_BAND_BIT_5GHZ;
+
+			rc = net80211_prepare_probe ( dev, band, active );
+			if ( rc )
+				goto fail;
+
+			dev->ctx.probe = net80211_probe_start ( dev, dev->essid,
+								active );
+			if ( ! dev->ctx.probe ) {
+				dev->assoc_rc = -ENOMEM;
+				goto fail;
+			}
+		}
+
+		rc = net80211_probe_step ( dev->ctx.probe );
+		if ( ! rc ) {
+			return;	/* still going */
+		}
+
+		dev->associating = net80211_probe_finish_best ( dev->ctx.probe );
+		dev->ctx.probe = NULL;
+		if ( ! dev->associating ) {
+			if ( rc > 0 ) /* "successful" probe found nothing */
+				rc = -ETIMEDOUT;
+			goto fail;
+		}
+
+		/* If we probed using a broadcast SSID, record that
+		   fact for the settings applicator before we clobber
+		   it with the specific SSID we've chosen. */
+		if ( ! dev->essid[0] )
+			dev->state |= NET80211_AUTO_SSID;
+
+		DBGC ( dev, "802.11 %p found network %s (%s)\n", dev,
+		       dev->associating->essid,
+		       eth_ntoa ( dev->associating->bssid ) );
+
+		dev->ctx.assoc = zalloc ( sizeof ( *dev->ctx.assoc ) );
+		if ( ! dev->ctx.assoc ) {
+			rc = -ENOMEM;
+			goto fail;
+		}
+
+		dev->state |= NET80211_PROBED;
+		dev->ctx.assoc->method = IEEE80211_AUTH_OPEN_SYSTEM;
+
+		return;
+	}
+
+	/* Record time of sending the packet we're about to send, for timeout */
+	dev->ctx.assoc->last_packet = currticks();
+
+	if ( ! ( dev->state & NET80211_AUTHENTICATED ) ) {
+		/* state: prepare and authenticate */
+
+		if ( status != IEEE80211_STATUS_SUCCESS ) {
+			/* we tried authenticating already, but failed */
+			int method = dev->ctx.assoc->method;
+
+			if ( method == IEEE80211_AUTH_OPEN_SYSTEM &&
+			     ( status == IEEE80211_STATUS_AUTH_CHALL_INVALID ||
+			       status == IEEE80211_STATUS_AUTH_ALGO_UNSUPP ) ) {
+				/* Maybe this network uses Shared Key? */
+				dev->ctx.assoc->method =
+					IEEE80211_AUTH_SHARED_KEY;
+			} else {
+				goto fail;
+			}
+		}
+
+		DBGC ( dev, "802.11 %p authenticating with method %d\n", dev,
+		       dev->ctx.assoc->method );
+
+		rc = net80211_prepare_assoc ( dev, dev->associating );
+		if ( rc )
+			goto fail;
+
+		rc = net80211_send_auth ( dev, dev->associating,
+					  dev->ctx.assoc->method );
+		if ( rc )
+			goto fail;
+
+		return;
+	}
+
+	if ( ! ( dev->state & NET80211_ASSOCIATED ) ) {
+		/* state: associate */
+
+		if ( status != IEEE80211_STATUS_SUCCESS )
+			goto fail;
+
+		DBGC ( dev, "802.11 %p associating\n", dev );
+
+		if ( dev->handshaker && dev->handshaker->start &&
+		     ! dev->handshaker->started ) {
+			rc = dev->handshaker->start ( dev );
+			if ( rc < 0 )
+				goto fail;
+			dev->handshaker->started = 1;
+		}
+
+		rc = net80211_send_assoc ( dev, dev->associating );
+		if ( rc )
+			goto fail;
+
+		return;
+	}
+
+	if ( ! ( dev->state & NET80211_CRYPTO_SYNCED ) ) {
+		/* state: crypto sync */
+		DBGC ( dev, "802.11 %p security handshaking\n", dev );
+
+		if ( ! dev->handshaker || ! dev->handshaker->step ) {
+			dev->state |= NET80211_CRYPTO_SYNCED;
+			return;
+		}
+
+		rc = dev->handshaker->step ( dev );
+
+		if ( rc < 0 ) {
+			/* Only record the returned error if we're
+			   still marked as associated, because an
+			   asynchronous error will have already been
+			   reported to net80211_deauthenticate() and
+			   assoc_rc thereby set. */
+			if ( dev->state & NET80211_ASSOCIATED )
+				dev->assoc_rc = rc;
+			rc = 0;
+			goto fail;
+		}
+
+		if ( rc > 0 ) {
+			dev->assoc_rc = 0;
+			dev->state |= NET80211_CRYPTO_SYNCED;
+		}
+		return;
+	}
+
+	/* state: done! */
+	netdev_link_up ( dev->netdev );
+	dev->assoc_rc = 0;
+	dev->state &= ~NET80211_WORKING;
+
+	free ( dev->ctx.assoc );
+	dev->ctx.assoc = NULL;
+
+	net80211_free_wlan ( dev->associating );
+	dev->associating = NULL;
+
+	dev->rctl = rc80211_init ( dev );
+
+	process_del ( proc );
+
+	DBGC ( dev, "802.11 %p associated with %s (%s)\n", dev,
+	       dev->essid, eth_ntoa ( dev->bssid ) );
+
+	return;
+
+ fail:
+	dev->state &= ~( NET80211_WORKING | NET80211_WAITING );
+	if ( rc )
+		dev->assoc_rc = rc;
+
+	netdev_link_err ( dev->netdev, dev->assoc_rc );
+
+	/* We never reach here from the middle of a probe, so we don't
+	   need to worry about freeing dev->ctx.probe. */
+
+	if ( dev->state & NET80211_PROBED ) {
+		free ( dev->ctx.assoc );
+		dev->ctx.assoc = NULL;
+	}
+
+	net80211_free_wlan ( dev->associating );
+	dev->associating = NULL;
+
+	process_del ( proc );
+
+	DBGC ( dev, "802.11 %p association failed (state=%04x): "
+	       "%s\n", dev, dev->state, strerror ( dev->assoc_rc ) );
+
+	/* Try it again: */
+	net80211_autoassociate ( dev );
+}
+
+/**
+ * Check for 802.11 SSID or key updates
+ *
+ * This acts as a settings applicator; if the user changes netX/ssid,
+ * and netX is currently open, the association task will be invoked
+ * again. If the user changes the encryption key, the current security
+ * handshaker will be asked to update its state to match; if that is
+ * impossible without reassociation, we reassociate.
+ */
+static int net80211_check_settings_update ( void )
+{
+	struct net80211_device *dev;
+	char ssid[IEEE80211_MAX_SSID_LEN + 1];
+	int key_reassoc;
+
+	list_for_each_entry ( dev, &net80211_devices, list ) {
+		if ( ! ( dev->netdev->state & NETDEV_OPEN ) )
+			continue;
+
+		key_reassoc = 0;
+		if ( dev->handshaker && dev->handshaker->change_key &&
+		     dev->handshaker->change_key ( dev ) < 0 )
+			key_reassoc = 1;
+
+		fetch_string_setting ( netdev_settings ( dev->netdev ),
+				       &net80211_ssid_setting, ssid,
+				       IEEE80211_MAX_SSID_LEN + 1 );
+
+		if ( key_reassoc ||
+		     ( ! ( ! ssid[0] && ( dev->state & NET80211_AUTO_SSID ) ) &&
+		       strcmp ( ssid, dev->essid ) != 0 ) ) {
+			DBGC ( dev, "802.11 %p updating association: "
+			       "%s -> %s\n", dev, dev->essid, ssid );
+			net80211_autoassociate ( dev );
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Start 802.11 association process
+ *
+ * @v dev	802.11 device
+ *
+ * If the association process is running, it will be restarted.
+ */
+void net80211_autoassociate ( struct net80211_device *dev )
+{
+	if ( ! ( dev->state & NET80211_WORKING ) ) {
+		DBGC2 ( dev, "802.11 %p spawning association process\n", dev );
+		process_add ( &dev->proc_assoc );
+	} else {
+		DBGC2 ( dev, "802.11 %p restarting association\n", dev );
+	}
+
+	/* Clean up everything an earlier association process might
+	   have been in the middle of using */
+	if ( dev->associating )
+		net80211_free_wlan ( dev->associating );
+
+	if ( ! ( dev->state & NET80211_PROBED ) )
+		net80211_free_wlan (
+			net80211_probe_finish_best ( dev->ctx.probe ) );
+	else
+		free ( dev->ctx.assoc );
+
+	/* Reset to a clean state */
+	fetch_string_setting ( netdev_settings ( dev->netdev ),
+			       &net80211_ssid_setting, dev->essid,
+			       IEEE80211_MAX_SSID_LEN + 1 );
+	dev->ctx.probe = NULL;
+	dev->associating = NULL;
+	dev->assoc_rc = 0;
+	net80211_set_state ( dev, NET80211_PROBED, NET80211_WORKING, 0 );
+}
+
+/**
+ * Pick TX rate for RTS/CTS packets based on data rate
+ *
+ * @v dev	802.11 device
+ *
+ * The RTS/CTS rate is the fastest TX rate marked as "basic" that is
+ * not faster than the data rate.
+ */
+static void net80211_set_rtscts_rate ( struct net80211_device *dev )
+{
+	u16 datarate = dev->rates[dev->rate];
+	u16 rtsrate = 0;
+	int rts_idx = -1;
+	int i;
+
+	for ( i = 0; i < dev->nr_rates; i++ ) {
+		u16 rate = dev->rates[i];
+
+		if ( ! ( dev->basic_rates & ( 1 << i ) ) || rate > datarate )
+			continue;
+
+		if ( rate > rtsrate ) {
+			rtsrate = rate;
+			rts_idx = i;
+		}
+	}
+
+	/* If this is in initialization, we might not have any basic
+	   rates; just use the first data rate in that case. */
+	if ( rts_idx < 0 )
+		rts_idx = 0;
+
+	dev->rtscts_rate = rts_idx;
+}
+
+/**
+ * Set data transmission rate for 802.11 device
+ *
+ * @v dev	802.11 device
+ * @v rate	Rate to set, as index into @c dev->rates array
+ */
+void net80211_set_rate_idx ( struct net80211_device *dev, int rate )
+{
+	assert ( dev->netdev->state & NETDEV_OPEN );
+
+	if ( rate >= 0 && rate < dev->nr_rates && rate != dev->rate ) {
+		DBGC2 ( dev, "802.11 %p changing rate from %d->%d Mbps\n",
+			dev, dev->rates[dev->rate] / 10,
+			dev->rates[rate] / 10 );
+
+		dev->rate = rate;
+		net80211_set_rtscts_rate ( dev );
+		dev->op->config ( dev, NET80211_CFG_RATE );
+	}
+}
+
+/**
+ * Configure 802.11 device to transmit on a certain channel
+ *
+ * @v dev	802.11 device
+ * @v channel	Channel number (1-11 for 2.4GHz) to transmit on
+ */
+int net80211_change_channel ( struct net80211_device *dev, int channel )
+{
+	int i, oldchan = dev->channel;
+
+	assert ( dev->netdev->state & NETDEV_OPEN );
+
+	for ( i = 0; i < dev->nr_channels; i++ ) {
+		if ( dev->channels[i].channel_nr == channel ) {
+			dev->channel = i;
+			break;
+		}
+	}
+
+	if ( i == dev->nr_channels )
+		return -ENOENT;
+
+	if ( i != oldchan )
+		return dev->op->config ( dev, NET80211_CFG_CHANNEL );
+
+	return 0;
+}
+
+/**
+ * Prepare 802.11 device channel and rate set for scanning
+ *
+ * @v dev	802.11 device
+ * @v band	RF band(s) on which to prepare for scanning
+ * @v active	Whether the scanning will be active
+ * @ret rc	Return status code
+ */
+int net80211_prepare_probe ( struct net80211_device *dev, int band,
+			     int active )
+{
+	assert ( dev->netdev->state & NETDEV_OPEN );
+
+	if ( active && ( band & NET80211_BAND_BIT_5GHZ ) ) {
+		DBGC ( dev, "802.11 %p cannot perform active scanning on "
+		       "5GHz band\n", dev );
+		return -EINVAL_ACTIVE_SCAN;
+	}
+
+	if ( band == 0 ) {
+		/* This can happen for a 5GHz-only card with 5GHz
+		   scanning masked out by an active request. */
+		DBGC ( dev, "802.11 %p asked to prepare for scanning nothing\n",
+		       dev );
+		return -EINVAL_ACTIVE_SCAN;
+	}
+
+	dev->nr_channels = 0;
+
+	if ( active )
+		net80211_add_channels ( dev, 1, 11, NET80211_REG_TXPOWER );
+	else {
+		if ( band & NET80211_BAND_BIT_2GHZ )
+			net80211_add_channels ( dev, 1, 14,
+						NET80211_REG_TXPOWER );
+		if ( band & NET80211_BAND_BIT_5GHZ )
+			net80211_add_channels ( dev, 36, 8,
+						NET80211_REG_TXPOWER );
+	}
+
+	net80211_filter_hw_channels ( dev );
+
+	/* Use channel 1 for now */
+	dev->channel = 0;
+	dev->op->config ( dev, NET80211_CFG_CHANNEL );
+
+	/* Always do active probes at lowest (presumably first) speed */
+	dev->rate = 0;
+	dev->nr_rates = 1;
+	dev->rates[0] = dev->hw->rates[dev->channels[0].band][0];
+	dev->op->config ( dev, NET80211_CFG_RATE );
+
+	return 0;
+}
+
+/**
+ * Prepare 802.11 device channel and rate set for communication
+ *
+ * @v dev	802.11 device
+ * @v wlan	WLAN to prepare for communication with
+ * @ret rc	Return status code
+ */
+int net80211_prepare_assoc ( struct net80211_device *dev,
+			     struct net80211_wlan *wlan )
+{
+	struct ieee80211_frame *hdr = wlan->beacon->data;
+	struct ieee80211_beacon *beacon =
+		( struct ieee80211_beacon * ) hdr->data;
+	struct net80211_handshaker *handshaker;
+	int rc;
+
+	assert ( dev->netdev->state & NETDEV_OPEN );
+
+	net80211_set_state ( dev, NET80211_ASSOCIATED, 0, 0 );
+	memcpy ( dev->bssid, wlan->bssid, ETH_ALEN );
+	strcpy ( dev->essid, wlan->essid );
+
+	free ( dev->rsn_ie );
+	dev->rsn_ie = NULL;
+
+	dev->last_beacon_timestamp = beacon->timestamp;
+	dev->tx_beacon_interval = 1024 * beacon->beacon_interval;
+
+	/* Barring an IE that tells us the channel outright, assume
+	   the channel we heard this AP best on is the channel it's
+	   communicating on. */
+	net80211_change_channel ( dev, wlan->channel );
+
+	rc = net80211_process_capab ( dev, beacon->capability );
+	if ( rc )
+		return rc;
+
+	rc = net80211_process_ie ( dev, beacon->info_element,
+				   wlan->beacon->tail );
+	if ( rc )
+		return rc;
+
+	/* Associate at the lowest rate so we know it'll get through */
+	dev->rate = 0;
+	dev->op->config ( dev, NET80211_CFG_RATE );
+
+	/* Free old handshaker and crypto, if they exist */
+	if ( dev->handshaker && dev->handshaker->stop &&
+	     dev->handshaker->started )
+		dev->handshaker->stop ( dev );
+	free ( dev->handshaker );
+	dev->handshaker = NULL;
+	free ( dev->crypto );
+	free ( dev->gcrypto );
+	dev->crypto = dev->gcrypto = NULL;
+
+	/* Find new security handshaker to use */
+	for_each_table_entry ( handshaker, NET80211_HANDSHAKERS ) {
+		if ( handshaker->protocol == wlan->handshaking ) {
+			dev->handshaker = zalloc ( sizeof ( *handshaker ) +
+						   handshaker->priv_len );
+			if ( ! dev->handshaker )
+				return -ENOMEM;
+
+			memcpy ( dev->handshaker, handshaker,
+				 sizeof ( *handshaker ) );
+			dev->handshaker->priv = ( ( void * ) dev->handshaker +
+						  sizeof ( *handshaker ) );
+			break;
+		}
+	}
+
+	if ( ( wlan->handshaking != NET80211_SECPROT_NONE ) &&
+	     ! dev->handshaker ) {
+		DBGC ( dev, "802.11 %p no support for handshaking scheme %d\n",
+		       dev, wlan->handshaking );
+		return -( ENOTSUP | ( wlan->handshaking << 8 ) );
+	}
+
+	/* Initialize security handshaker */
+	if ( dev->handshaker ) {
+		rc = dev->handshaker->init ( dev );
+		if ( rc < 0 )
+			return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * Send 802.11 initial authentication frame
+ *
+ * @v dev	802.11 device
+ * @v wlan	WLAN to authenticate with
+ * @v method	Authentication method
+ * @ret rc	Return status code
+ *
+ * @a method may be 0 for Open System authentication or 1 for Shared
+ * Key authentication. Open System provides no security in association
+ * whatsoever, relying on encryption for confidentiality, but Shared
+ * Key actively introduces security problems and is very rarely used.
+ */
+int net80211_send_auth ( struct net80211_device *dev,
+			 struct net80211_wlan *wlan, int method )
+{
+	struct io_buffer *iob = alloc_iob ( 64 );
+	struct ieee80211_auth *auth;
+
+	net80211_set_state ( dev, 0, NET80211_WAITING, 0 );
+	iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN );
+	auth = iob_put ( iob, sizeof ( *auth ) );
+	auth->algorithm = method;
+	auth->tx_seq = 1;
+	auth->status = 0;
+
+	return net80211_tx_mgmt ( dev, IEEE80211_STYPE_AUTH, wlan->bssid, iob );
+}
+
+/**
+ * Handle receipt of 802.11 authentication frame
+ *
+ * @v dev	802.11 device
+ * @v iob	I/O buffer
+ *
+ * If the authentication method being used is Shared Key, and the
+ * frame that was received included challenge text, the frame is
+ * encrypted using the cryptosystem currently in effect and sent back
+ * to the AP to complete the authentication.
+ */
+static void net80211_handle_auth ( struct net80211_device *dev,
+				   struct io_buffer *iob )
+{
+	struct ieee80211_frame *hdr = iob->data;
+	struct ieee80211_auth *auth =
+	    ( struct ieee80211_auth * ) hdr->data;
+
+	if ( auth->tx_seq & 1 ) {
+		DBGC ( dev, "802.11 %p authentication received improperly "
+		       "directed frame (seq. %d)\n", dev, auth->tx_seq );
+		net80211_set_state ( dev, NET80211_WAITING, 0,
+				     IEEE80211_STATUS_FAILURE );
+		return;
+	}
+
+	if ( auth->status != IEEE80211_STATUS_SUCCESS ) {
+		DBGC ( dev, "802.11 %p authentication failed: status %d\n",
+		       dev, auth->status );
+		net80211_set_state ( dev, NET80211_WAITING, 0,
+				     auth->status );
+		return;
+	}
+
+	if ( auth->algorithm == IEEE80211_AUTH_SHARED_KEY && ! dev->crypto ) {
+		DBGC ( dev, "802.11 %p can't perform shared-key authentication "
+		       "without a cryptosystem\n", dev );
+		net80211_set_state ( dev, NET80211_WAITING, 0,
+				     IEEE80211_STATUS_FAILURE );
+		return;
+	}
+
+	if ( auth->algorithm == IEEE80211_AUTH_SHARED_KEY &&
+	     auth->tx_seq == 2 ) {
+		/* Since the iob we got is going to be freed as soon
+		   as we return, we can do some in-place
+		   modification. */
+		auth->tx_seq = 3;
+		auth->status = 0;
+
+		memcpy ( hdr->addr2, hdr->addr1, ETH_ALEN );
+		memcpy ( hdr->addr1, hdr->addr3, ETH_ALEN );
+
+		netdev_tx ( dev->netdev,
+			    dev->crypto->encrypt ( dev->crypto, iob ) );
+		return;
+	}
+
+	net80211_set_state ( dev, NET80211_WAITING, NET80211_AUTHENTICATED,
+			     IEEE80211_STATUS_SUCCESS );
+
+	return;
+}
+
+/**
+ * Send 802.11 association frame
+ *
+ * @v dev	802.11 device
+ * @v wlan	WLAN to associate with
+ * @ret rc	Return status code
+ */
+int net80211_send_assoc ( struct net80211_device *dev,
+			  struct net80211_wlan *wlan )
+{
+	struct io_buffer *iob = alloc_iob ( 128 );
+	struct ieee80211_assoc_req *assoc;
+	union ieee80211_ie *ie;
+
+	net80211_set_state ( dev, 0, NET80211_WAITING, 0 );
+
+	iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN );
+	assoc = iob->data;
+
+	assoc->capability = IEEE80211_CAPAB_MANAGED;
+	if ( ! ( dev->hw->flags & NET80211_HW_NO_SHORT_PREAMBLE ) )
+		assoc->capability |= IEEE80211_CAPAB_SHORT_PMBL;
+	if ( ! ( dev->hw->flags & NET80211_HW_NO_SHORT_SLOT ) )
+		assoc->capability |= IEEE80211_CAPAB_SHORT_SLOT;
+	if ( wlan->crypto )
+		assoc->capability |= IEEE80211_CAPAB_PRIVACY;
+
+	assoc->listen_interval = 1;
+
+	ie = net80211_marshal_request_info ( dev, assoc->info_element );
+
+	DBGP ( "802.11 %p about to send association request:\n", dev );
+	DBGP_HD ( iob->data, ( void * ) ie - iob->data );
+
+	iob_put ( iob, ( void * ) ie - iob->data );
+
+	return net80211_tx_mgmt ( dev, IEEE80211_STYPE_ASSOC_REQ,
+				  wlan->bssid, iob );
+}
+
+/**
+ * Handle receipt of 802.11 association reply frame
+ *
+ * @v dev	802.11 device
+ * @v iob	I/O buffer
+ */
+static void net80211_handle_assoc_reply ( struct net80211_device *dev,
+					  struct io_buffer *iob )
+{
+	struct ieee80211_frame *hdr = iob->data;
+	struct ieee80211_assoc_resp *assoc =
+		( struct ieee80211_assoc_resp * ) hdr->data;
+
+	net80211_process_capab ( dev, assoc->capability );
+	net80211_process_ie ( dev, assoc->info_element, iob->tail );
+
+	if ( assoc->status != IEEE80211_STATUS_SUCCESS ) {
+		DBGC ( dev, "802.11 %p association failed: status %d\n",
+		       dev, assoc->status );
+		net80211_set_state ( dev, NET80211_WAITING, 0,
+				     assoc->status );
+		return;
+	}
+
+	/* ESSID was filled before the association request was sent */
+	memcpy ( dev->bssid, hdr->addr3, ETH_ALEN );
+	dev->aid = assoc->aid;
+
+	net80211_set_state ( dev, NET80211_WAITING, NET80211_ASSOCIATED,
+			     IEEE80211_STATUS_SUCCESS );
+}
+
+
+/**
+ * Send 802.11 disassociation frame
+ *
+ * @v dev	802.11 device
+ * @v reason	Reason for disassociation
+ * @v deauth	If TRUE, send deauthentication instead of disassociation
+ * @ret rc	Return status code
+ */
+static int net80211_send_disassoc ( struct net80211_device *dev, int reason,
+				    int deauth )
+{
+	struct io_buffer *iob = alloc_iob ( 64 );
+	struct ieee80211_disassoc *disassoc;
+
+	if ( ! ( dev->state & NET80211_ASSOCIATED ) )
+		return -EINVAL;
+
+	net80211_set_state ( dev, NET80211_ASSOCIATED, 0, 0 );
+	iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN );
+	disassoc = iob_put ( iob, sizeof ( *disassoc ) );
+	disassoc->reason = reason;
+
+	return net80211_tx_mgmt ( dev, deauth ? IEEE80211_STYPE_DEAUTH :
+				  IEEE80211_STYPE_DISASSOC, dev->bssid, iob );
+}
+
+
+/**
+ * Deauthenticate from current network and try again
+ *
+ * @v dev	802.11 device
+ * @v rc	Return status code indicating reason
+ *
+ * The deauthentication will be sent using an 802.11 "unspecified
+ * reason", as is common, but @a rc will be set as a link-up
+ * error to aid the user in debugging.
+ */
+void net80211_deauthenticate ( struct net80211_device *dev, int rc )
+{
+	net80211_send_disassoc ( dev, IEEE80211_REASON_UNSPECIFIED, 1 );
+	dev->assoc_rc = rc;
+	netdev_link_err ( dev->netdev, rc );
+
+	net80211_autoassociate ( dev );
+}
+
+
+/** Smoothing factor (1-7) for link quality calculation */
+#define LQ_SMOOTH	7
+
+/**
+ * Update link quality information based on received beacon
+ *
+ * @v dev	802.11 device
+ * @v iob	I/O buffer containing beacon
+ * @ret rc	Return status code
+ */
+static void net80211_update_link_quality ( struct net80211_device *dev,
+					   struct io_buffer *iob )
+{
+	struct ieee80211_frame *hdr = iob->data;
+	struct ieee80211_beacon *beacon;
+	u32 dt, rxi;
+
+	if ( ! ( dev->state & NET80211_ASSOCIATED ) )
+		return;
+
+	beacon = ( struct ieee80211_beacon * ) hdr->data;
+	dt = ( u32 ) ( beacon->timestamp - dev->last_beacon_timestamp );
+	rxi = dev->rx_beacon_interval;
+
+	rxi = ( LQ_SMOOTH * rxi ) + ( ( 8 - LQ_SMOOTH ) * dt );
+	dev->rx_beacon_interval = rxi >> 3;
+
+	dev->last_beacon_timestamp = beacon->timestamp;
+}
+
+
+/**
+ * Handle receipt of 802.11 management frame
+ *
+ * @v dev	802.11 device
+ * @v iob	I/O buffer
+ * @v signal	Signal strength of received frame
+ */
+static void net80211_handle_mgmt ( struct net80211_device *dev,
+				   struct io_buffer *iob, int signal )
+{
+	struct ieee80211_frame *hdr = iob->data;
+	struct ieee80211_disassoc *disassoc;
+	u16 stype = hdr->fc & IEEE80211_FC_SUBTYPE;
+	int keep = 0;
+	int is_deauth = ( stype == IEEE80211_STYPE_DEAUTH );
+
+	if ( ( hdr->fc & IEEE80211_FC_TYPE ) != IEEE80211_TYPE_MGMT ) {
+		free_iob ( iob );
+		return;		/* only handle management frames */
+	}
+
+	switch ( stype ) {
+		/* We reconnect on deauthentication and disassociation. */
+	case IEEE80211_STYPE_DEAUTH:
+	case IEEE80211_STYPE_DISASSOC:
+		disassoc = ( struct ieee80211_disassoc * ) hdr->data;
+		net80211_set_state ( dev, is_deauth ? NET80211_AUTHENTICATED :
+				     NET80211_ASSOCIATED, 0,
+				     NET80211_IS_REASON | disassoc->reason );
+		DBGC ( dev, "802.11 %p %s: reason %d\n",
+		       dev, is_deauth ? "deauthenticated" : "disassociated",
+		       disassoc->reason );
+
+		/* Try to reassociate, in case it's transient. */
+		net80211_autoassociate ( dev );
+
+		break;
+
+		/* We handle authentication and association. */
+	case IEEE80211_STYPE_AUTH:
+		if ( ! ( dev->state & NET80211_AUTHENTICATED ) )
+			net80211_handle_auth ( dev, iob );
+		break;
+
+	case IEEE80211_STYPE_ASSOC_RESP:
+	case IEEE80211_STYPE_REASSOC_RESP:
+		if ( ! ( dev->state & NET80211_ASSOCIATED ) )
+			net80211_handle_assoc_reply ( dev, iob );
+		break;
+
+		/* We pass probes and beacons onto network scanning
+		   code. Pass actions for future extensibility. */
+	case IEEE80211_STYPE_BEACON:
+		net80211_update_link_quality ( dev, iob );
+		/* fall through */
+	case IEEE80211_STYPE_PROBE_RESP:
+	case IEEE80211_STYPE_ACTION:
+		if ( dev->keep_mgmt ) {
+			struct net80211_rx_info *rxinf;
+			rxinf = zalloc ( sizeof ( *rxinf ) );
+			if ( ! rxinf ) {
+				DBGC ( dev, "802.11 %p out of memory\n", dev );
+				break;
+			}
+			rxinf->signal = signal;
+			list_add_tail ( &iob->list, &dev->mgmt_queue );
+			list_add_tail ( &rxinf->list, &dev->mgmt_info_queue );
+			keep = 1;
+		}
+		break;
+
+	case IEEE80211_STYPE_PROBE_REQ:
+		/* Some nodes send these broadcast. Ignore them. */
+		break;
+
+	case IEEE80211_STYPE_ASSOC_REQ:
+	case IEEE80211_STYPE_REASSOC_REQ:
+		/* We should never receive these, only send them. */
+		DBGC ( dev, "802.11 %p received strange management request "
+		       "(%04x)\n", dev, stype );
+		break;
+
+	default:
+		DBGC ( dev, "802.11 %p received unimplemented management "
+		       "packet (%04x)\n", dev, stype );
+		break;
+	}
+
+	if ( ! keep )
+		free_iob ( iob );
+}
+
+/* ---------- Packet handling functions ---------- */
+
+/**
+ * Free buffers used by 802.11 fragment cache entry
+ *
+ * @v dev	802.11 device
+ * @v fcid	Fragment cache entry index
+ *
+ * After this function, the referenced entry will be marked unused.
+ */
+static void net80211_free_frags ( struct net80211_device *dev, int fcid )
+{
+	int j;
+	struct net80211_frag_cache *frag = &dev->frags[fcid];
+
+	for ( j = 0; j < 16; j++ ) {
+		if ( frag->iob[j] ) {
+			free_iob ( frag->iob[j] );
+			frag->iob[j] = NULL;
+		}
+	}
+
+	frag->seqnr = 0;
+	frag->start_ticks = 0;
+	frag->in_use = 0;
+}
+
+/**
+ * Accumulate 802.11 fragments into one I/O buffer
+ *
+ * @v dev	802.11 device
+ * @v fcid	Fragment cache entry index
+ * @v nfrags	Number of fragments received
+ * @v size	Sum of sizes of all fragments, including headers
+ * @ret iob	I/O buffer containing reassembled packet
+ *
+ * This function does not free the fragment buffers.
+ */
+static struct io_buffer *net80211_accum_frags ( struct net80211_device *dev,
+						int fcid, int nfrags, int size )
+{
+	struct net80211_frag_cache *frag = &dev->frags[fcid];
+	int hdrsize = IEEE80211_TYP_FRAME_HEADER_LEN;
+	int nsize = size - hdrsize * ( nfrags - 1 );
+	int i;
+
+	struct io_buffer *niob = alloc_iob ( nsize );
+	struct ieee80211_frame *hdr;
+
+	/* Add the header from the first one... */
+	memcpy ( iob_put ( niob, hdrsize ), frag->iob[0]->data, hdrsize );
+
+	/* ... and all the data from all of them. */
+	for ( i = 0; i < nfrags; i++ ) {
+		int len = iob_len ( frag->iob[i] ) - hdrsize;
+		memcpy ( iob_put ( niob, len ),
+			 frag->iob[i]->data + hdrsize, len );
+	}
+
+	/* Turn off the fragment bit. */
+	hdr = niob->data;
+	hdr->fc &= ~IEEE80211_FC_MORE_FRAG;
+
+	return niob;
+}
+
+/**
+ * Handle receipt of 802.11 fragment
+ *
+ * @v dev	802.11 device
+ * @v iob	I/O buffer containing fragment
+ * @v signal	Signal strength with which fragment was received
+ */
+static void net80211_rx_frag ( struct net80211_device *dev,
+			       struct io_buffer *iob, int signal )
+{
+	struct ieee80211_frame *hdr = iob->data;
+	int fragnr = IEEE80211_FRAG ( hdr->seq );
+
+	if ( fragnr == 0 && ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) {
+		/* start a frag cache entry */
+		int i, newest = -1;
+		u32 curr_ticks = currticks(), newest_ticks = 0;
+		u32 timeout = ticks_per_sec() * NET80211_FRAG_TIMEOUT;
+
+		for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) {
+			if ( dev->frags[i].in_use == 0 )
+				break;
+
+			if ( dev->frags[i].start_ticks + timeout >=
+			     curr_ticks ) {
+				net80211_free_frags ( dev, i );
+				break;
+			}
+
+			if ( dev->frags[i].start_ticks > newest_ticks ) {
+				newest = i;
+				newest_ticks = dev->frags[i].start_ticks;
+			}
+		}
+
+		/* If we're being sent more concurrent fragmented
+		   packets than we can handle, drop the newest so the
+		   older ones have time to complete. */
+		if ( i == NET80211_NR_CONCURRENT_FRAGS ) {
+			i = newest;
+			net80211_free_frags ( dev, i );
+		}
+
+		dev->frags[i].in_use = 1;
+		dev->frags[i].seqnr = IEEE80211_SEQNR ( hdr->seq );
+		dev->frags[i].start_ticks = currticks();
+		dev->frags[i].iob[0] = iob;
+		return;
+	} else {
+		int i;
+		for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) {
+			if ( dev->frags[i].in_use && dev->frags[i].seqnr ==
+			     IEEE80211_SEQNR ( hdr->seq ) )
+				break;
+		}
+		if ( i == NET80211_NR_CONCURRENT_FRAGS ) {
+			/* Drop non-first not-in-cache fragments */
+			DBGC ( dev, "802.11 %p dropped fragment fc=%04x "
+			       "seq=%04x\n", dev, hdr->fc, hdr->seq );
+			free_iob ( iob );
+			return;
+		}
+
+		dev->frags[i].iob[fragnr] = iob;
+
+		if ( ! ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) {
+			int j, size = 0;
+			for ( j = 0; j < fragnr; j++ ) {
+				size += iob_len ( dev->frags[i].iob[j] );
+				if ( dev->frags[i].iob[j] == NULL )
+					break;
+			}
+			if ( j == fragnr ) {
+				/* We've got everything */
+				struct io_buffer *niob =
+				    net80211_accum_frags ( dev, i, fragnr,
+							   size );
+				net80211_free_frags ( dev, i );
+				net80211_rx ( dev, niob, signal, 0 );
+			} else {
+				DBGC ( dev, "802.11 %p dropping fragmented "
+				       "packet due to out-of-order arrival, "
+				       "fc=%04x seq=%04x\n", dev, hdr->fc,
+				       hdr->seq );
+				net80211_free_frags ( dev, i );
+			}
+		}
+	}
+}
+
+/**
+ * Handle receipt of 802.11 frame
+ *
+ * @v dev	802.11 device
+ * @v iob	I/O buffer
+ * @v signal	Received signal strength
+ * @v rate	Bitrate at which frame was received, in 100 kbps units
+ *
+ * If the rate or signal is unknown, 0 should be passed.
+ */
+void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
+		   int signal, u16 rate )
+{
+	struct ieee80211_frame *hdr = iob->data;
+	u16 type = hdr->fc & IEEE80211_FC_TYPE;
+	if ( ( hdr->fc & IEEE80211_FC_VERSION ) != IEEE80211_THIS_VERSION )
+		goto drop;	/* drop invalid-version packets */
+
+	if ( type == IEEE80211_TYPE_CTRL )
+		goto drop;	/* we don't handle control packets,
+				   the hardware does */
+
+	if ( dev->last_rx_seq == hdr->seq )
+		goto drop;	/* avoid duplicate packet */
+	dev->last_rx_seq = hdr->seq;
+
+	if ( dev->hw->flags & NET80211_HW_RX_HAS_FCS ) {
+		/* discard the FCS */
+		iob_unput ( iob, 4 );
+	}
+
+	/* Only decrypt packets from our BSSID, to avoid spurious errors */
+	if ( ( hdr->fc & IEEE80211_FC_PROTECTED ) &&
+	     ! memcmp ( hdr->addr2, dev->bssid, ETH_ALEN ) ) {
+		/* Decrypt packet; record and drop if it fails */
+		struct io_buffer *niob;
+		struct net80211_crypto *crypto = dev->crypto;
+
+		if ( ! dev->crypto ) {
+			DBGC ( dev, "802.11 %p cannot decrypt packet "
+			       "without a cryptosystem\n", dev );
+			goto drop_crypt;
+		}
+
+		if ( ( hdr->addr1[0] & 1 ) && dev->gcrypto ) {
+			/* Use group decryption if needed */
+			crypto = dev->gcrypto;
+		}
+
+		niob = crypto->decrypt ( crypto, iob );
+		if ( ! niob ) {
+			DBGC ( dev, "802.11 %p decryption error\n", dev );
+			goto drop_crypt;
+		}
+		free_iob ( iob );
+		iob = niob;
+	}
+
+	dev->last_signal = signal;
+
+	/* Fragments go into the frag cache or get dropped. */
+	if ( IEEE80211_FRAG ( hdr->seq ) != 0
+	     || ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) {
+		net80211_rx_frag ( dev, iob, signal );
+		return;
+	}
+
+	/* Management frames get handled, enqueued, or dropped. */
+	if ( type == IEEE80211_TYPE_MGMT ) {
+		net80211_handle_mgmt ( dev, iob, signal );
+		return;
+	}
+
+	/* Data frames get dropped or sent to the net_device. */
+	if ( ( hdr->fc & IEEE80211_FC_SUBTYPE ) != IEEE80211_STYPE_DATA )
+		goto drop;	/* drop QoS, CFP, or null data packets */
+
+	/* Update rate-control algorithm */
+	if ( dev->rctl )
+		rc80211_update_rx ( dev, hdr->fc & IEEE80211_FC_RETRY, rate );
+
+	/* Pass packet onward */
+	if ( dev->state & NET80211_ASSOCIATED ) {
+		netdev_rx ( dev->netdev, iob );
+		return;
+	}
+
+	/* No association? Drop it. */
+	goto drop;
+
+ drop_crypt:
+	netdev_rx_err ( dev->netdev, NULL, EINVAL_CRYPTO_REQUEST );
+ drop:
+	DBGC2 ( dev, "802.11 %p dropped packet fc=%04x seq=%04x\n", dev,
+		hdr->fc, hdr->seq );
+	free_iob ( iob );
+	return;
+}
+
+/** Indicate an error in receiving a packet
+ *
+ * @v dev	802.11 device
+ * @v iob	I/O buffer with received packet, or NULL
+ * @v rc	Error code
+ *
+ * This logs the error with the wrapping net_device, and frees iob if
+ * it is passed.
+ */
+void net80211_rx_err ( struct net80211_device *dev,
+		       struct io_buffer *iob, int rc )
+{
+	netdev_rx_err ( dev->netdev, iob, rc );
+}
+
+/** Indicate the completed transmission of a packet
+ *
+ * @v dev	802.11 device
+ * @v iob	I/O buffer of transmitted packet
+ * @v retries	Number of times this packet was retransmitted
+ * @v rc	Error code, or 0 for success
+ *
+ * This logs an error with the wrapping net_device if one occurred,
+ * and removes and frees the I/O buffer from its TX queue. The
+ * provided retry information is used to tune our transmission rate.
+ *
+ * If the packet did not need to be retransmitted because it was
+ * properly ACKed the first time, @a retries should be 0.
+ */
+void net80211_tx_complete ( struct net80211_device *dev,
+			    struct io_buffer *iob, int retries, int rc )
+{
+	/* Update rate-control algorithm */
+	if ( dev->rctl )
+		rc80211_update_tx ( dev, retries, rc );
+
+	/* Pass completion onward */
+	netdev_tx_complete_err ( dev->netdev, iob, rc );
+}
diff --git a/gpxe/src/net/80211/rc80211.c b/gpxe/src/net/80211/rc80211.c
new file mode 100644
index 0000000..5bd1914
--- /dev/null
+++ b/gpxe/src/net/80211/rc80211.c
@@ -0,0 +1,371 @@
+/*
+ * Simple 802.11 rate-control algorithm for gPXE.
+ *
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <gpxe/net80211.h>
+
+/**
+ * @file
+ *
+ * Simple 802.11 rate-control algorithm
+ */
+
+/** @page rc80211 Rate control philosophy
+ *
+ * We want to maximize our transmission speed, to the extent that we
+ * can do that without dropping undue numbers of packets. We also
+ * don't want to take up very much code space, so our algorithm has to
+ * be pretty simple
+ *
+ * When we receive a packet, we know what rate it was transmitted at,
+ * and whether it had to be retransmitted to get to us.
+ *
+ * When we send a packet, we hear back how many times it had to be
+ * retried to get through, and whether it got through at all.
+ *
+ * Indications of TX success are more reliable than RX success, but RX
+ * information helps us know where to start.
+ *
+ * To handle all of this, we keep for each rate and each direction (TX
+ * and RX separately) some state information for the most recent
+ * packets on that rate and the number of packets for which we have
+ * information. The state is a 32-bit unsigned integer in which two
+ * bits represent a packet: 11 if it went through well, 10 if it went
+ * through with one retry, 01 if it went through with more than one
+ * retry, or 00 if it didn't go through at all. We define the
+ * "goodness" for a particular (rate, direction) combination as the
+ * sum of all the 2-bit fields, times 33, divided by the number of
+ * 2-bit fields containing valid information (16 except when we're
+ * starting out). The number produced is between 0 and 99; we use -1
+ * for rates with less than 4 RX packets or 1 TX, as an indicator that
+ * we do not have enough information to rely on them.
+ *
+ * In deciding which rates are best, we find the weighted average of
+ * TX and RX goodness, where the weighting is by number of packets
+ * with data and TX packets are worth 4 times as much as RX packets.
+ * The weighted average is called "net goodness" and is also a number
+ * between 0 and 99.  If 3 consecutive packets fail transmission
+ * outright, we automatically ratchet down the rate; otherwise, we
+ * switch to the best rate whenever the current rate's goodness falls
+ * below some threshold, and try increasing our rate when the goodness
+ * is very high.
+ *
+ * This system is optimized for gPXE's style of usage. Because normal
+ * operation always involves receiving something, we'll make our way
+ * to the best rate pretty quickly. We tend to follow the lead of the
+ * sending AP in choosing rates, but we won't use rates for long that
+ * don't work well for us in transmission. We assume gPXE won't be
+ * running for long enough that rate patterns will change much, so we
+ * don't have to keep time counters or the like.  And if this doesn't
+ * work well in practice there are many ways it could be tweaked.
+ *
+ * To avoid staying at 1Mbps for a long time, we don't track any
+ * transmitted packets until we've set our rate based on received
+ * packets.
+ */
+
+/** Two-bit packet status indicator for a packet with no retries */
+#define RC_PKT_OK		0x3
+
+/** Two-bit packet status indicator for a packet with one retry */
+#define RC_PKT_RETRIED_ONCE	0x2
+
+/** Two-bit packet status indicator for a TX packet with multiple retries
+ *
+ * It is not possible to tell whether an RX packet had one or multiple
+ * retries; we rely instead on the fact that failed RX packets won't
+ * get to us at all, so if we receive a lot of RX packets on a certain
+ * rate it must be pretty good.
+ */
+#define RC_PKT_RETRIED_MULTI	0x1
+
+/** Two-bit packet status indicator for a TX packet that was never ACKed
+ *
+ * It is not possible to tell whether an RX packet was setn if it
+ * didn't get through to us, but if we don't see one we won't increase
+ * the goodness for its rate. This asymmetry is part of why TX packets
+ * are weighted much more heavily than RX.
+ */
+#define RC_PKT_FAILED		0x0
+
+/** Number of times to weight TX packets more heavily than RX packets */
+#define RC_TX_FACTOR		4
+
+/** Number of consecutive failed TX packets that cause an automatic rate drop */
+#define RC_TX_EMERG_FAIL	3
+
+/** Minimum net goodness below which we will search for a better rate */
+#define RC_GOODNESS_MIN		85
+
+/** Maximum net goodness above which we will try to increase our rate */
+#define RC_GOODNESS_MAX		95
+
+/** Minimum (num RX + @c RC_TX_FACTOR * num TX) to use a certain rate */
+#define RC_UNCERTAINTY_THRESH	4
+
+/** TX direction */
+#define TX	0
+
+/** RX direction */
+#define RX	1
+
+/** A rate control context */
+struct rc80211_ctx
+{
+	/** Goodness state for each rate, TX and RX */
+	u32 goodness[2][NET80211_MAX_RATES];
+
+	/** Number of packets recorded for each rate */
+	u8 count[2][NET80211_MAX_RATES];
+
+	/** Indication of whether we've set the device rate yet */
+	int started;
+
+	/** Counter of all packets sent and received */
+	int packets;
+};
+
+/**
+ * Initialize rate-control algorithm
+ *
+ * @v dev	802.11 device
+ * @ret ctx	Rate-control context, to be stored in @c dev->rctl
+ */
+struct rc80211_ctx * rc80211_init ( struct net80211_device *dev __unused )
+{
+	struct rc80211_ctx *ret = zalloc ( sizeof ( *ret ) );
+	return ret;
+}
+
+/**
+ * Calculate net goodness for a certain rate
+ *
+ * @v ctx	Rate-control context
+ * @v rate_idx	Index of rate to calculate net goodness for
+ */
+static int rc80211_calc_net_goodness ( struct rc80211_ctx *ctx,
+				       int rate_idx )
+{
+	int sum[2], num[2], dir, pkt;
+
+	for ( dir = 0; dir < 2; dir++ ) {
+		u32 good = ctx->goodness[dir][rate_idx];
+
+		num[dir] = ctx->count[dir][rate_idx];
+		sum[dir] = 0;
+
+		for ( pkt = 0; pkt < num[dir]; pkt++ )
+			sum[dir] += ( good >> ( 2 * pkt ) ) & 0x3;
+	}
+
+	if ( ( num[TX] * RC_TX_FACTOR + num[RX] ) < RC_UNCERTAINTY_THRESH )
+		return -1;
+
+	return ( 33 * ( sum[TX] * RC_TX_FACTOR + sum[RX] ) /
+		      ( num[TX] * RC_TX_FACTOR + num[RX] ) );
+}
+
+/**
+ * Determine the best rate to switch to and return it
+ *
+ * @v dev		802.11 device
+ * @ret rate_idx	Index of the best rate to switch to
+ */
+static int rc80211_pick_best ( struct net80211_device *dev )
+{
+	struct rc80211_ctx *ctx = dev->rctl;
+	int best_net_good = 0, best_rate = -1, i;
+
+	for ( i = 0; i < dev->nr_rates; i++ ) {
+		int net_good = rc80211_calc_net_goodness ( ctx, i );
+
+		if ( net_good > best_net_good ||
+		     ( best_net_good > RC_GOODNESS_MIN &&
+		       net_good > RC_GOODNESS_MIN ) ) {
+			best_net_good = net_good;
+			best_rate = i;
+		}
+	}
+
+	if ( best_rate >= 0 ) {
+		int old_good = rc80211_calc_net_goodness ( ctx, dev->rate );
+		if ( old_good != best_net_good )
+			DBGC ( ctx, "802.11 RC %p switching from goodness "
+			       "%d to %d\n", ctx, old_good, best_net_good );
+
+		ctx->started = 1;
+		return best_rate;
+	}
+
+	return dev->rate;
+}
+
+/**
+ * Set 802.11 device rate
+ *
+ * @v dev	802.11 device
+ * @v rate_idx	Index of rate to switch to
+ *
+ * This is a thin wrapper around net80211_set_rate_idx to insert a
+ * debugging message where appropriate.
+ */
+static inline void rc80211_set_rate ( struct net80211_device *dev,
+				      int rate_idx )
+{
+	DBGC ( dev->rctl, "802.11 RC %p changing rate %d->%d Mbps\n", dev->rctl,
+	       dev->rates[dev->rate] / 10, dev->rates[rate_idx] / 10 );
+
+	net80211_set_rate_idx ( dev, rate_idx );
+}
+
+/**
+ * Check rate-control state and change rate if necessary
+ *
+ * @v dev	802.11 device
+ */
+static void rc80211_maybe_set_new ( struct net80211_device *dev )
+{
+	struct rc80211_ctx *ctx = dev->rctl;
+	int net_good;
+
+	net_good = rc80211_calc_net_goodness ( ctx, dev->rate );
+
+	if ( ! ctx->started ) {
+		rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
+		return;
+	}
+
+	if ( net_good < 0 )	/* insufficient data */
+		return;
+
+	if ( net_good > RC_GOODNESS_MAX && dev->rate + 1 < dev->nr_rates ) {
+		int higher = rc80211_calc_net_goodness ( ctx, dev->rate + 1 );
+		if ( higher > net_good || higher < 0 )
+			rc80211_set_rate ( dev, dev->rate + 1 );
+		else
+			rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
+	}
+
+	if ( net_good < RC_GOODNESS_MIN ) {
+		rc80211_set_rate ( dev, rc80211_pick_best ( dev ) );
+	}
+}
+
+/**
+ * Update rate-control state
+ *
+ * @v dev		802.11 device
+ * @v direction		One of the direction constants TX or RX
+ * @v rate_idx		Index of rate at which packet was sent or received
+ * @v retries		Number of times packet was retried before success
+ * @v failed		If nonzero, the packet failed to get through
+ */
+static void rc80211_update ( struct net80211_device *dev, int direction,
+			     int rate_idx, int retries, int failed )
+{
+	struct rc80211_ctx *ctx = dev->rctl;
+	u32 goodness = ctx->goodness[direction][rate_idx];
+
+	if ( ctx->count[direction][rate_idx] < 16 )
+		ctx->count[direction][rate_idx]++;
+
+	goodness <<= 2;
+	if ( failed )
+		goodness |= RC_PKT_FAILED;
+	else if ( retries > 1 )
+		goodness |= RC_PKT_RETRIED_MULTI;
+	else if ( retries )
+		goodness |= RC_PKT_RETRIED_ONCE;
+	else
+		goodness |= RC_PKT_OK;
+
+	ctx->goodness[direction][rate_idx] = goodness;
+
+	ctx->packets++;
+
+	rc80211_maybe_set_new ( dev );
+}
+
+/**
+ * Update rate-control state for transmitted packet
+ *
+ * @v dev	802.11 device
+ * @v retries	Number of times packet was transmitted before success
+ * @v rc	Return status code for transmission
+ */
+void rc80211_update_tx ( struct net80211_device *dev, int retries, int rc )
+{
+	struct rc80211_ctx *ctx = dev->rctl;
+
+	if ( ! ctx->started )
+		return;
+
+	rc80211_update ( dev, TX, dev->rate, retries, rc );
+
+	/* Check if the last RC_TX_EMERG_FAIL packets have all failed */
+	if ( ! ( ctx->goodness[TX][dev->rate] &
+		 ( ( 1 << ( 2 * RC_TX_EMERG_FAIL ) ) - 1 ) ) ) {
+		if ( dev->rate == 0 )
+			DBGC ( dev->rctl, "802.11 RC %p saw %d consecutive "
+			       "failed TX, but cannot lower rate any further\n",
+			       dev->rctl, RC_TX_EMERG_FAIL );
+		else {
+			DBGC ( dev->rctl, "802.11 RC %p lowering rate (%d->%d "
+			       "Mbps) due to %d consecutive TX failures\n",
+			       dev->rctl, dev->rates[dev->rate] / 10,
+			       dev->rates[dev->rate - 1] / 10,
+			       RC_TX_EMERG_FAIL );
+
+			rc80211_set_rate ( dev, dev->rate - 1 );
+		}
+	}
+}
+
+/**
+ * Update rate-control state for received packet
+ *
+ * @v dev	802.11 device
+ * @v retry	Whether the received packet had been retransmitted
+ * @v rate	Rate at which packet was received, in 100 kbps units
+ */
+void rc80211_update_rx ( struct net80211_device *dev, int retry, u16 rate )
+{
+	int ridx;
+
+	for ( ridx = 0; ridx < dev->nr_rates && dev->rates[ridx] != rate;
+	      ridx++ )
+		;
+	if ( ridx >= dev->nr_rates )
+		return;		/* couldn't find the rate */
+
+	rc80211_update ( dev, RX, ridx, retry, 0 );
+}
+
+/**
+ * Free rate-control context
+ *
+ * @v ctx	Rate-control context
+ */
+void rc80211_free ( struct rc80211_ctx *ctx )
+{
+	free ( ctx );
+}
diff --git a/gpxe/src/net/80211/sec80211.c b/gpxe/src/net/80211/sec80211.c
new file mode 100644
index 0000000..c5aa118
--- /dev/null
+++ b/gpxe/src/net/80211/sec80211.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/ieee80211.h>
+#include <gpxe/net80211.h>
+#include <gpxe/sec80211.h>
+
+/** @file
+ *
+ * General secured-network routines required whenever any secure
+ * network support at all is compiled in. This involves things like
+ * installing keys, determining the type of security used by a probed
+ * network, and some small helper functions that take advantage of
+ * static data in this file.
+ */
+
+/** Mapping from net80211 crypto/secprot types to RSN OUI descriptors */
+struct descriptor_map {
+	/** Value of net80211_crypto_alg or net80211_security_proto */
+	u32 net80211_type;
+
+	/** OUI+type in appropriate byte order, masked to exclude vendor */
+	u32 oui_type;
+};
+
+/** Magic number in @a oui_type showing end of list */
+#define END_MAGIC	0xFFFFFFFF
+
+/** Mapping between net80211 cryptosystems and 802.11i cipher IDs */
+static struct descriptor_map rsn_cipher_map[] = {
+	{ .net80211_type = NET80211_CRYPT_WEP,
+	  .oui_type = IEEE80211_RSN_CTYPE_WEP40 },
+
+	{ .net80211_type = NET80211_CRYPT_WEP,
+	  .oui_type = IEEE80211_RSN_CTYPE_WEP104 },
+
+	{ .net80211_type = NET80211_CRYPT_TKIP,
+	  .oui_type = IEEE80211_RSN_CTYPE_TKIP },
+
+	{ .net80211_type = NET80211_CRYPT_CCMP,
+	  .oui_type = IEEE80211_RSN_CTYPE_CCMP },
+
+	{ .net80211_type = NET80211_CRYPT_UNKNOWN,
+	  .oui_type = END_MAGIC },
+};
+
+/** Mapping between net80211 handshakers and 802.11i AKM IDs */
+static struct descriptor_map rsn_akm_map[] = {
+	{ .net80211_type = NET80211_SECPROT_EAP,
+	  .oui_type = IEEE80211_RSN_ATYPE_8021X },
+
+	{ .net80211_type = NET80211_SECPROT_PSK,
+	  .oui_type = IEEE80211_RSN_ATYPE_PSK },
+
+	{ .net80211_type = NET80211_SECPROT_UNKNOWN,
+	  .oui_type = END_MAGIC },
+};
+
+
+/**
+ * Install 802.11 cryptosystem
+ *
+ * @v which	Pointer to the cryptosystem structure to install in
+ * @v crypt	Cryptosystem ID number
+ * @v key	Encryption key to use
+ * @v len	Length of encryption key
+ * @v rsc	Initial receive sequence counter, if applicable
+ * @ret rc	Return status code
+ *
+ * The encryption key will not be accessed via the provided pointer
+ * after this function returns, so you may keep it on the stack.
+ *
+ * @a which must point to either @c dev->crypto (for the normal case
+ * of installing a unicast cryptosystem) or @c dev->gcrypto (to
+ * install a cryptosystem that will be used only for decrypting
+ * group-source frames).
+ */
+int sec80211_install ( struct net80211_crypto **which,
+		       enum net80211_crypto_alg crypt,
+		       const void *key, int len, const void *rsc )
+{
+	struct net80211_crypto *crypto = *which;
+	struct net80211_crypto *tbl_crypto;
+
+	/* Remove old crypto if it exists */
+	free ( *which );
+	*which = NULL;
+
+	if ( crypt == NET80211_CRYPT_NONE ) {
+		DBG ( "802.11-Sec not installing null cryptography\n" );
+		return 0;
+	}
+
+	/* Find cryptosystem to use */
+	for_each_table_entry ( tbl_crypto, NET80211_CRYPTOS ) {
+		if ( tbl_crypto->algorithm == crypt ) {
+			crypto = zalloc ( sizeof ( *crypto ) +
+					  tbl_crypto->priv_len );
+			if ( ! crypto ) {
+				DBG ( "802.11-Sec out of memory\n" );
+				return -ENOMEM;
+			}
+
+			memcpy ( crypto, tbl_crypto, sizeof ( *crypto ) );
+			crypto->priv = ( ( void * ) crypto +
+					 sizeof ( *crypto ) );
+			break;
+		}
+	}
+
+	if ( ! crypto ) {
+		DBG ( "802.11-Sec no support for cryptosystem %d\n", crypt );
+		return -( ENOTSUP | EUNIQ_10 | ( crypt << 8 ) );
+	}
+
+	*which = crypto;
+
+	DBG ( "802.11-Sec installing cryptosystem %d as %p with key of "
+	      "length %d\n", crypt, crypto, len );
+
+	return crypto->init ( crypto, key, len, rsc );
+}
+
+
+/**
+ * Determine net80211 crypto or handshaking type value to return for RSN info
+ *
+ * @v rsnp		Pointer to next descriptor count field in RSN IE
+ * @v rsn_end		Pointer to end of RSN IE
+ * @v map		Descriptor map to use
+ * @v tbl_start		Start of linker table to examine for gPXE support
+ * @v tbl_end		End of linker table to examine for gPXE support
+ * @ret rsnp		Updated to point to first byte after descriptors
+ * @ret map_ent		Descriptor map entry of translation to use
+ *
+ * The entries in the linker table must be either net80211_crypto or
+ * net80211_handshaker structures, and @a tbl_stride must be set to
+ * sizeof() the appropriate one.
+ *
+ * This function expects @a rsnp to point at a two-byte descriptor
+ * count followed by a list of four-byte cipher or AKM descriptors; it
+ * will return @c NULL if the input packet is malformed, and otherwise
+ * set @a rsnp to the first byte it has not looked at. It will return
+ * the first cipher in the list that is supported by the current build
+ * of gPXE, or the first of all if none are supported.
+ *
+ * We play rather fast and loose with type checking, because this
+ * function is only called from two well-defined places in the
+ * RSN-checking code. Don't try to use it for anything else.
+ */
+static struct descriptor_map * rsn_pick_desc ( u8 **rsnp, u8 *rsn_end,
+					       struct descriptor_map *map,
+					       void *tbl_start, void *tbl_end )
+{
+	int ndesc;
+	int ok = 0;
+	struct descriptor_map *map_ent, *map_ret = NULL;
+	u8 *rsn = *rsnp;
+	void *tblp;
+	size_t tbl_stride = ( map == rsn_cipher_map ?
+			      sizeof ( struct net80211_crypto ) :
+			      sizeof ( struct net80211_handshaker ) );
+
+	if ( map != rsn_cipher_map && map != rsn_akm_map )
+		return NULL;
+
+	/* Determine which types we support */
+	for ( tblp = tbl_start; tblp < tbl_end; tblp += tbl_stride ) {
+		struct net80211_crypto *crypto = tblp;
+		struct net80211_handshaker *hs = tblp;
+
+		if ( map == rsn_cipher_map )
+			ok |= ( 1 << crypto->algorithm );
+		else
+			ok |= ( 1 << hs->protocol );
+	}
+
+	/* RSN sanity checks */
+	if ( rsn + 2 > rsn_end ) {
+		DBG ( "RSN detect: malformed descriptor count\n" );
+		return NULL;
+	}
+
+	ndesc = *( u16 * ) rsn;
+	rsn += 2;
+
+	if ( ! ndesc ) {
+		DBG ( "RSN detect: no descriptors\n" );
+		return NULL;
+	}
+
+	/* Determine which net80211 crypto types are listed */
+	while ( ndesc-- ) {
+		u32 desc;
+
+		if ( rsn + 4 > rsn_end ) {
+			DBG ( "RSN detect: malformed descriptor (%d left)\n",
+			      ndesc );
+			return NULL;
+		}
+
+		desc = *( u32 * ) rsn;
+		rsn += 4;
+
+		for ( map_ent = map; map_ent->oui_type != END_MAGIC; map_ent++ )
+			if ( map_ent->oui_type == ( desc & OUI_TYPE_MASK ) )
+				break;
+
+		/* Use first cipher as a fallback */
+		if ( ! map_ret )
+			map_ret = map_ent;
+
+		/* Once we find one we support, use it */
+		if ( ok & ( 1 << map_ent->net80211_type ) ) {
+			map_ret = map_ent;
+			break;
+		}
+	}
+
+	if ( ndesc > 0 )
+		rsn += 4 * ndesc;
+
+	*rsnp = rsn;
+	return map_ret;
+}
+
+
+/**
+ * Find the RSN or WPA information element in the provided beacon frame
+ *
+ * @v ie	Pointer to first information element to check
+ * @v ie_end	Pointer to end of information element space
+ * @ret is_rsn	TRUE if returned IE is RSN, FALSE if it's WPA
+ * @ret end	Pointer to byte immediately after last byte of data
+ * @ret data	Pointer to first byte of data (the `version' field)
+ *
+ * If both an RSN and a WPA information element are found, this
+ * function will return the first one seen, which by ordering rules
+ * should always prefer the newer RSN IE.
+ *
+ * If no RSN or WPA infomration element is found, returns @c NULL and
+ * leaves @a is_rsn and @a end in an undefined state.
+ *
+ * This function will not return a pointer to an information element
+ * that states it extends past the tail of the io_buffer, or whose @a
+ * version field is incorrect.
+ */
+u8 * sec80211_find_rsn ( union ieee80211_ie *ie, void *ie_end,
+			 int *is_rsn, u8 **end )
+{
+	u8 *rsn = NULL;
+
+	if ( ! ieee80211_ie_bound ( ie, ie_end ) )
+		return NULL;
+
+	while ( ie ) {
+		if ( ie->id == IEEE80211_IE_VENDOR &&
+		     ie->vendor.oui == IEEE80211_WPA_OUI_VEN ) {
+			DBG ( "RSN detect: old-style WPA IE found\n" );
+			rsn = &ie->vendor.data[0];
+			*end = rsn + ie->len - 4;
+			*is_rsn = 0;
+		} else if ( ie->id == IEEE80211_IE_RSN ) {
+			DBG ( "RSN detect: 802.11i RSN IE found\n" );
+			rsn = ( u8 * ) &ie->rsn.version;
+			*end = rsn + ie->len;
+			*is_rsn = 1;
+		}
+
+		if ( rsn && ( *end > ( u8 * ) ie_end || rsn >= *end ||
+			      *( u16 * ) rsn != IEEE80211_RSN_VERSION ) ) {
+			DBG ( "RSN detect: malformed RSN IE or unknown "
+			      "version, keep trying\n" );
+			rsn = NULL;
+		}
+
+		if ( rsn )
+			break;
+
+		ie = ieee80211_next_ie ( ie, ie_end );
+	}
+
+	if ( ! ie ) {
+		DBG ( "RSN detect: no RSN IE found\n" );
+		return NULL;
+	}
+
+	return rsn;
+}
+
+
+/**
+ * Detect crypto and AKM types from RSN information element
+ *
+ * @v is_rsn	If TRUE, IE is a new-style RSN information element
+ * @v start	Pointer to first byte of @a version field
+ * @v end	Pointer to first byte not in the RSN IE
+ * @ret secprot	Security handshaking protocol used by network
+ * @ret crypt	Cryptosystem used by network
+ * @ret rc	Return status code
+ *
+ * If the IE cannot be parsed, returns an error indication and leaves
+ * @a secprot and @a crypt unchanged.
+ */
+int sec80211_detect_ie ( int is_rsn, u8 *start, u8 *end,
+			 enum net80211_security_proto *secprot,
+			 enum net80211_crypto_alg *crypt )
+{
+	enum net80211_security_proto sp;
+	enum net80211_crypto_alg cr;
+	struct descriptor_map *map;
+	u8 *rsn = start;
+
+	/* Set some defaults */
+	cr = ( is_rsn ? NET80211_CRYPT_CCMP : NET80211_CRYPT_TKIP );
+	sp = NET80211_SECPROT_EAP;
+
+	rsn += 2;		/* version - already checked */
+	rsn += 4;		/* group cipher - we don't use it here */
+
+	if ( rsn >= end )
+		goto done;
+
+	/* Pick crypto algorithm */
+	map = rsn_pick_desc ( &rsn, end, rsn_cipher_map,
+			      table_start ( NET80211_CRYPTOS ),
+			      table_end ( NET80211_CRYPTOS ) );
+	if ( ! map )
+		goto invalid_rsn;
+
+	cr = map->net80211_type;
+
+	if ( rsn >= end )
+		goto done;
+
+	/* Pick handshaking algorithm */
+	map = rsn_pick_desc ( &rsn, end, rsn_akm_map,
+			      table_start ( NET80211_HANDSHAKERS ),
+			      table_end ( NET80211_HANDSHAKERS ) );
+	if ( ! map )
+		goto invalid_rsn;
+
+	sp = map->net80211_type;
+
+ done:
+	DBG ( "RSN detect: OK, crypto type %d, secprot type %d\n", cr, sp );
+	*secprot = sp;
+	*crypt = cr;
+	return 0;
+
+ invalid_rsn:
+	DBG ( "RSN detect: invalid RSN IE\n" );
+	return -EINVAL;
+}
+
+
+/**
+ * Detect the cryptosystem and handshaking protocol used by an 802.11 network
+ *
+ * @v iob	I/O buffer containing beacon frame
+ * @ret secprot	Security handshaking protocol used by network
+ * @ret crypt	Cryptosystem used by network
+ * @ret rc	Return status code
+ *
+ * This function uses weak linkage, as it must be called from generic
+ * contexts but should only be linked in if some encryption is
+ * supported; you must test its address against @c NULL before calling
+ * it. If it does not exist, any network with the PRIVACY bit set in
+ * beacon->capab should be considered unknown.
+ */
+int _sec80211_detect ( struct io_buffer *iob,
+		       enum net80211_security_proto *secprot,
+		       enum net80211_crypto_alg *crypt )
+{
+	struct ieee80211_frame *hdr = iob->data;
+	struct ieee80211_beacon *beacon =
+		( struct ieee80211_beacon * ) hdr->data;
+	u8 *rsn, *rsn_end;
+	int is_rsn, rc;
+
+	*crypt = NET80211_CRYPT_UNKNOWN;
+	*secprot = NET80211_SECPROT_UNKNOWN;
+
+	/* Find RSN or WPA IE */
+	if ( ! ( rsn = sec80211_find_rsn ( beacon->info_element, iob->tail,
+					   &is_rsn, &rsn_end ) ) ) {
+		/* No security IE at all; either WEP or no security. */
+		*secprot = NET80211_SECPROT_NONE;
+
+		if ( beacon->capability & IEEE80211_CAPAB_PRIVACY )
+			*crypt = NET80211_CRYPT_WEP;
+		else
+			*crypt = NET80211_CRYPT_NONE;
+
+		return 0;
+	}
+
+	/* Determine type of security */
+	if ( ( rc = sec80211_detect_ie ( is_rsn, rsn, rsn_end, secprot,
+					 crypt ) ) == 0 )
+		return 0;
+
+	/* If we get here, the RSN IE was invalid */
+
+	*crypt = NET80211_CRYPT_UNKNOWN;
+	*secprot = NET80211_SECPROT_UNKNOWN;
+	DBG ( "Failed to handle RSN IE:\n" );
+	DBG_HD ( rsn, rsn_end - rsn );
+	return rc;
+}
+
+
+/**
+ * Determine RSN descriptor for specified net80211 ID
+ *
+ * @v id	net80211 ID value
+ * @v rsnie	Whether to return a new-format (RSN IE) descriptor
+ * @v map	Map to use in translation
+ * @ret desc	RSN descriptor, or 0 on error
+ *
+ * If @a rsnie is false, returns an old-format (WPA vendor IE)
+ * descriptor.
+ */
+static u32 rsn_get_desc ( unsigned id, int rsnie, struct descriptor_map *map )
+{
+	u32 vendor = ( rsnie ? IEEE80211_RSN_OUI : IEEE80211_WPA_OUI );
+
+	for ( ; map->oui_type != END_MAGIC; map++ ) {
+		if ( map->net80211_type == id )
+			return map->oui_type | vendor;
+	}
+
+	return 0;
+}
+
+/**
+ * Determine RSN descriptor for specified net80211 cryptosystem number
+ *
+ * @v crypt	Cryptosystem number
+ * @v rsnie	Whether to return a new-format (RSN IE) descriptor
+ * @ret desc	RSN descriptor
+ *
+ * If @a rsnie is false, returns an old-format (WPA vendor IE)
+ * descriptor.
+ */
+u32 sec80211_rsn_get_crypto_desc ( enum net80211_crypto_alg crypt, int rsnie )
+{
+	return rsn_get_desc ( crypt, rsnie, rsn_cipher_map );
+}
+
+/**
+ * Determine RSN descriptor for specified net80211 handshaker number
+ *
+ * @v secprot	Handshaker number
+ * @v rsnie	Whether to return a new-format (RSN IE) descriptor
+ * @ret desc	RSN descriptor
+ *
+ * If @a rsnie is false, returns an old-format (WPA vendor IE)
+ * descriptor.
+ */
+u32 sec80211_rsn_get_akm_desc ( enum net80211_security_proto secprot,
+				int rsnie )
+{
+	return rsn_get_desc ( secprot, rsnie, rsn_akm_map );
+}
+
+/**
+ * Determine net80211 cryptosystem number from RSN descriptor
+ *
+ * @v desc	RSN descriptor
+ * @ret crypt	net80211 cryptosystem enumeration value
+ */
+enum net80211_crypto_alg sec80211_rsn_get_net80211_crypt ( u32 desc )
+{
+	struct descriptor_map *map = rsn_cipher_map;
+
+	for ( ; map->oui_type != END_MAGIC; map++ ) {
+		if ( map->oui_type == ( desc & OUI_TYPE_MASK ) )
+			break;
+	}
+
+	return map->net80211_type;
+}
diff --git a/gpxe/src/net/80211/wep.c b/gpxe/src/net/80211/wep.c
new file mode 100644
index 0000000..1c37e0c
--- /dev/null
+++ b/gpxe/src/net/80211/wep.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/net80211.h>
+#include <gpxe/sec80211.h>
+#include <gpxe/crypto.h>
+#include <gpxe/arc4.h>
+#include <gpxe/crc32.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/** @file
+ *
+ * The WEP wireless encryption method (insecure!)
+ *
+ * The data field in a WEP-encrypted packet contains a 3-byte
+ * initialisation vector, one-byte Key ID field (only the bottom two
+ * bits are ever used), encrypted data, and a 4-byte encrypted CRC of
+ * the plaintext data, called the ICV. To decrypt it, the IV is
+ * prepended to the shared key and the data stream (including ICV) is
+ * run through the ARC4 stream cipher; if the ICV matches a CRC32
+ * calculated on the plaintext, the packet is valid.
+ *
+ * For efficiency and code-size reasons, this file assumes it is
+ * running on a little-endian machine.
+ */
+
+/** Length of WEP initialisation vector */
+#define WEP_IV_LEN	3
+
+/** Length of WEP key ID byte */
+#define WEP_KID_LEN	1
+
+/** Length of WEP ICV checksum */
+#define WEP_ICV_LEN	4
+
+/** Maximum length of WEP key */
+#define WEP_MAX_KEY	16
+
+/** Amount of data placed before the encrypted bytes */
+#define WEP_HEADER_LEN	4
+
+/** Amount of data placed after the encrypted bytes */
+#define WEP_TRAILER_LEN	4
+
+/** Total WEP overhead bytes */
+#define WEP_OVERHEAD	8
+
+/** Context for WEP encryption and decryption */
+struct wep_ctx
+{
+	/** Encoded WEP key
+	 *
+	 * The actual key bytes are stored beginning at offset 3, to
+	 * leave room for easily inserting the IV before a particular
+	 * operation.
+	 */
+	u8 key[WEP_IV_LEN + WEP_MAX_KEY];
+
+	/** Length of WEP key (not including IV bytes) */
+	int keylen;
+
+	/** ARC4 context */
+	struct arc4_ctx arc4;
+};
+
+/**
+ * Initialize WEP algorithm
+ *
+ * @v crypto	802.11 cryptographic algorithm
+ * @v key	WEP key to use
+ * @v keylen	Length of WEP key
+ * @v rsc	Initial receive sequence counter (unused)
+ * @ret rc	Return status code
+ *
+ * Standard key lengths are 5 and 13 bytes; 16-byte keys are
+ * occasionally supported as an extension to the standard.
+ */
+static int wep_init ( struct net80211_crypto *crypto, const void *key,
+		      int keylen, const void *rsc __unused )
+{
+	struct wep_ctx *ctx = crypto->priv;
+
+	ctx->keylen = ( keylen > WEP_MAX_KEY ? WEP_MAX_KEY : keylen );
+	memcpy ( ctx->key + WEP_IV_LEN, key, ctx->keylen );
+
+	return 0;
+}
+
+/**
+ * Encrypt packet using WEP
+ *
+ * @v crypto	802.11 cryptographic algorithm
+ * @v iob	I/O buffer of plaintext packet
+ * @ret eiob	Newly allocated I/O buffer for encrypted packet, or NULL
+ *
+ * If memory allocation fails, @c NULL is returned.
+ */
+static struct io_buffer * wep_encrypt ( struct net80211_crypto *crypto,
+					struct io_buffer *iob )
+{
+	struct wep_ctx *ctx = crypto->priv;
+	struct io_buffer *eiob;
+	struct ieee80211_frame *hdr;
+	const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
+	int datalen = iob_len ( iob ) - hdrlen;
+	int newlen = hdrlen + datalen + WEP_OVERHEAD;
+	u32 iv, icv;
+
+	eiob = alloc_iob ( newlen );
+	if ( ! eiob )
+		return NULL;
+
+	memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen );
+	hdr = eiob->data;
+	hdr->fc |= IEEE80211_FC_PROTECTED;
+
+	/* Calculate IV, put it in the header (with key ID byte = 0), and
+	   set it up at the start of the encryption key. */
+	iv = random() & 0xffffff; /* IV in bottom 3 bytes, top byte = KID = 0 */
+	memcpy ( iob_put ( eiob, WEP_HEADER_LEN ), &iv, WEP_HEADER_LEN );
+	memcpy ( ctx->key, &iv, WEP_IV_LEN );
+
+	/* Encrypt the data using RC4 */
+	cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key,
+			ctx->keylen + WEP_IV_LEN );
+	cipher_encrypt ( &arc4_algorithm, &ctx->arc4, iob->data + hdrlen,
+			 iob_put ( eiob, datalen ), datalen );
+
+	/* Add ICV */
+	icv = ~crc32_le ( ~0, iob->data + hdrlen, datalen );
+	cipher_encrypt ( &arc4_algorithm, &ctx->arc4, &icv,
+			 iob_put ( eiob, WEP_ICV_LEN ), WEP_ICV_LEN );
+
+	return eiob;
+}
+
+/**
+ * Decrypt packet using WEP
+ *
+ * @v crypto	802.11 cryptographic algorithm
+ * @v eiob	I/O buffer of encrypted packet
+ * @ret iob	Newly allocated I/O buffer for plaintext packet, or NULL
+ *
+ * If a consistency check for the decryption fails (usually indicating
+ * an invalid key), @c NULL is returned.
+ */
+static struct io_buffer * wep_decrypt ( struct net80211_crypto *crypto,
+					struct io_buffer *eiob )
+{
+	struct wep_ctx *ctx = crypto->priv;
+	struct io_buffer *iob;
+	struct ieee80211_frame *hdr;
+	const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
+	int datalen = iob_len ( eiob ) - hdrlen - WEP_OVERHEAD;
+	int newlen = hdrlen + datalen;
+	u32 iv, icv, crc;
+
+	iob = alloc_iob ( newlen );
+	if ( ! iob )
+		return NULL;
+
+	memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen );
+	hdr = iob->data;
+	hdr->fc &= ~IEEE80211_FC_PROTECTED;
+
+	/* Strip off IV and use it to initialize cryptosystem */
+	memcpy ( &iv, eiob->data + hdrlen, 4 );
+	iv &= 0xffffff;		/* ignore key ID byte */
+	memcpy ( ctx->key, &iv, WEP_IV_LEN );
+
+	/* Decrypt the data using RC4 */
+	cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key,
+			ctx->keylen + WEP_IV_LEN );
+	cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen +
+			 WEP_HEADER_LEN, iob_put ( iob, datalen ), datalen );
+
+	/* Strip off ICV and verify it */
+	cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen +
+			 WEP_HEADER_LEN + datalen, &icv, WEP_ICV_LEN );
+	crc = ~crc32_le ( ~0, iob->data + hdrlen, datalen );
+	if ( crc != icv ) {
+		DBGC ( crypto, "WEP %p CRC mismatch: expect %08x, get %08x\n",
+		       crypto, icv, crc );
+		free_iob ( iob );
+		return NULL;
+	}
+	return iob;
+}
+
+/** WEP cryptosystem for 802.11 */
+struct net80211_crypto wep_crypto __net80211_crypto = {
+	.algorithm = NET80211_CRYPT_WEP,
+	.init = wep_init,
+	.encrypt = wep_encrypt,
+	.decrypt = wep_decrypt,
+	.priv_len = sizeof ( struct wep_ctx ),
+};
+
+/**
+ * Initialize trivial 802.11 security handshaker
+ *
+ * @v dev	802.11 device
+ * @v ctx	Security handshaker
+ *
+ * This simply fetches a WEP key from netX/key, and if it exists,
+ * installs WEP cryptography on the 802.11 device. No real handshaking
+ * is performed.
+ */
+static int trivial_init ( struct net80211_device *dev )
+{
+	u8 key[WEP_MAX_KEY];	/* support up to 128-bit keys */
+	int len;
+	int rc;
+
+	if ( dev->associating &&
+	     dev->associating->crypto == NET80211_CRYPT_NONE )
+		return 0;	/* no crypto? OK. */
+
+	len = fetch_setting ( netdev_settings ( dev->netdev ),
+			      &net80211_key_setting, key, WEP_MAX_KEY );
+
+	if ( len <= 0 ) {
+		DBGC ( dev, "802.11 %p cannot do WEP without a key\n", dev );
+		return -EACCES;
+	}
+
+	/* Full 128-bit keys are a nonstandard extension, but they're
+	   utterly trivial to support, so we do. */
+	if ( len != 5 && len != 13 && len != 16 ) {
+		DBGC ( dev, "802.11 %p invalid WEP key length %d\n",
+		       dev, len );
+		return -EINVAL;
+	}
+
+	DBGC ( dev, "802.11 %p installing %d-bit WEP\n", dev, len * 8 );
+
+	rc = sec80211_install ( &dev->crypto, NET80211_CRYPT_WEP, key, len,
+				NULL );
+	if ( rc < 0 )
+		return rc;
+
+	return 0;
+}
+
+/**
+ * Check for key change on trivial 802.11 security handshaker
+ *
+ * @v dev	802.11 device
+ * @v ctx	Security handshaker
+ */
+static int trivial_change_key ( struct net80211_device *dev )
+{
+	u8 key[WEP_MAX_KEY];
+	int len;
+	int change = 0;
+
+	/* If going from WEP to clear, or something else to WEP, reassociate. */
+	if ( ! dev->crypto || ( dev->crypto->init != wep_init ) )
+		change ^= 1;
+
+	len = fetch_setting ( netdev_settings ( dev->netdev ),
+			      &net80211_key_setting, key, WEP_MAX_KEY );
+	if ( len <= 0 )
+		change ^= 1;
+
+	/* Changing crypto type => return nonzero to reassociate. */
+	if ( change )
+		return -EINVAL;
+
+	/* Going from no crypto to still no crypto => nothing to do. */
+	if ( len <= 0 )
+		return 0;
+
+	/* Otherwise, reinitialise WEP with new key. */
+	return wep_init ( dev->crypto, key, len, NULL );
+}
+
+/** Trivial 802.11 security handshaker */
+struct net80211_handshaker trivial_handshaker __net80211_handshaker = {
+	.protocol = NET80211_SECPROT_NONE,
+	.init = trivial_init,
+	.change_key = trivial_change_key,
+	.priv_len = 0,
+};
diff --git a/gpxe/src/net/80211/wpa.c b/gpxe/src/net/80211/wpa.c
new file mode 100644
index 0000000..9bac8fe
--- /dev/null
+++ b/gpxe/src/net/80211/wpa.c
@@ -0,0 +1,973 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/net80211.h>
+#include <gpxe/sec80211.h>
+#include <gpxe/wpa.h>
+#include <gpxe/eapol.h>
+#include <gpxe/crypto.h>
+#include <gpxe/arc4.h>
+#include <gpxe/crc32.h>
+#include <gpxe/sha1.h>
+#include <gpxe/hmac.h>
+#include <gpxe/list.h>
+#include <gpxe/ethernet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/** @file
+ *
+ * Handler for the aspects of WPA handshaking that are independent of
+ * 802.1X/PSK or TKIP/CCMP; this mostly involves the 4-Way Handshake.
+ */
+
+/** List of WPA contexts in active use. */
+struct list_head wpa_contexts = LIST_HEAD_INIT ( wpa_contexts );
+
+
+/**
+ * Return an error code and deauthenticate
+ *
+ * @v ctx	WPA common context
+ * @v rc	Return status code
+ * @ret rc	The passed return status code
+ */
+static int wpa_fail ( struct wpa_common_ctx *ctx, int rc )
+{
+	net80211_deauthenticate ( ctx->dev, rc );
+	return rc;
+}
+
+
+/**
+ * Find a cryptosystem handler structure from a crypto ID
+ *
+ * @v crypt	Cryptosystem ID
+ * @ret crypto	Cryptosystem handler structure
+ *
+ * If support for @a crypt is not compiled in to gPXE, or if @a crypt
+ * is NET80211_CRYPT_UNKNOWN, returns @c NULL.
+ */
+static struct net80211_crypto *
+wpa_find_cryptosystem ( enum net80211_crypto_alg crypt )
+{
+	struct net80211_crypto *crypto;
+
+	for_each_table_entry ( crypto, NET80211_CRYPTOS ) {
+		if ( crypto->algorithm == crypt )
+			return crypto;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * Find WPA key integrity and encryption handler from key version field
+ *
+ * @v ver	Version bits of EAPOL-Key info field
+ * @ret kie	Key integrity and encryption handler
+ */
+struct wpa_kie * wpa_find_kie ( int version )
+{
+	struct wpa_kie *kie;
+
+	for_each_table_entry ( kie, WPA_KIES ) {
+		if ( kie->version == version )
+			return kie;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * Construct RSN or WPA information element
+ *
+ * @v dev	802.11 device
+ * @ret ie_ret	RSN or WPA information element
+ * @ret rc	Return status code
+ *
+ * This function allocates, fills, and returns a RSN or WPA
+ * information element suitable for including in an association
+ * request frame to the network identified by @c dev->associating.
+ * If it is impossible to construct an information element consistent
+ * with gPXE's capabilities that is compatible with that network, or
+ * if none should be sent because that network's beacon included no
+ * security information, returns an error indication and leaves
+ * @a ie_ret unchanged.
+ *
+ * The returned IE will be of the same type (RSN or WPA) as was
+ * included in the beacon for the network it is destined for.
+ */
+int wpa_make_rsn_ie ( struct net80211_device *dev, union ieee80211_ie **ie_ret )
+{
+	u8 *rsn, *rsn_end;
+	int is_rsn;
+	u32 group_cipher;
+	enum net80211_crypto_alg gcrypt;
+	int ie_len;
+	u8 *iep;
+	struct ieee80211_ie_rsn *ie;
+	struct ieee80211_frame *hdr;
+	struct ieee80211_beacon *beacon;
+
+	if ( ! dev->associating ) {
+		DBG ( "WPA: Can't make RSN IE for a non-associating device\n" );
+		return -EINVAL;
+	}
+
+	hdr = dev->associating->beacon->data;
+	beacon = ( struct ieee80211_beacon * ) hdr->data;
+	rsn = sec80211_find_rsn ( beacon->info_element,
+				  dev->associating->beacon->tail, &is_rsn,
+				  &rsn_end );
+	if ( ! rsn ) {
+		DBG ( "WPA: Can't make RSN IE when we didn't get one\n" );
+		return -EINVAL;
+	}
+
+	rsn += 2;		/* skip version */
+	group_cipher = *( u32 * ) rsn;
+	gcrypt = sec80211_rsn_get_net80211_crypt ( group_cipher );
+
+	if ( ! wpa_find_cryptosystem ( gcrypt ) ||
+	     ! wpa_find_cryptosystem ( dev->associating->crypto ) ) {
+		DBG ( "WPA: No support for (GC:%d, PC:%d)\n",
+		      gcrypt, dev->associating->crypto );
+		return -ENOTSUP;
+	}
+
+	/* Everything looks good - make our IE. */
+
+	/* WPA IEs need 4 more bytes for the OUI+type */
+	ie_len = ieee80211_rsn_size ( 1, 1, 0, is_rsn ) + ( 4 * ! is_rsn );
+	iep = malloc ( ie_len );
+	if ( ! iep )
+		return -ENOMEM;
+
+	*ie_ret = ( union ieee80211_ie * ) iep;
+
+	/* Store ID and length bytes. */
+	*iep++ = ( is_rsn ? IEEE80211_IE_RSN : IEEE80211_IE_VENDOR );
+	*iep++ = ie_len - 2;
+
+	/* Store OUI+type for WPA IEs. */
+	if ( ! is_rsn ) {
+		*( u32 * ) iep = IEEE80211_WPA_OUI_VEN;
+		iep += 4;
+	}
+
+	/* If this is a WPA IE, the id and len bytes in the
+	   ieee80211_ie_rsn structure will not be valid, but by doing
+	   the cast we can fill all the other fields much more
+	   readily. */
+
+	ie = ( struct ieee80211_ie_rsn * ) ( iep - 2 );
+	ie->version = IEEE80211_RSN_VERSION;
+	ie->group_cipher = group_cipher;
+	ie->pairwise_count = 1;
+	ie->pairwise_cipher[0] =
+		sec80211_rsn_get_crypto_desc ( dev->associating->crypto,
+					       is_rsn );
+	ie->akm_count = 1;
+	ie->akm_list[0] =
+		sec80211_rsn_get_akm_desc ( dev->associating->handshaking,
+					    is_rsn );
+	if ( is_rsn ) {
+		ie->rsn_capab = 0;
+		ie->pmkid_count = 0;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Set up generic WPA support to handle 4-Way Handshake
+ *
+ * @v dev	802.11 device
+ * @v ctx	WPA common context
+ * @v pmk	Pairwise Master Key to use for session
+ * @v pmk_len	Length of PMK, almost always 32
+ * @ret rc	Return status code
+ */
+int wpa_start ( struct net80211_device *dev, struct wpa_common_ctx *ctx,
+		const void *pmk, size_t pmk_len )
+{
+	struct io_buffer *iob;
+	struct ieee80211_frame *hdr;
+	struct ieee80211_beacon *beacon;
+	u8 *ap_rsn_ie = NULL, *ap_rsn_ie_end;
+
+	if ( ! dev->rsn_ie || ! dev->associating )
+		return -EINVAL;
+
+	ctx->dev = dev;
+	memcpy ( ctx->pmk, pmk, ctx->pmk_len = pmk_len );
+	ctx->state = WPA_READY;
+	ctx->replay = ~0ULL;
+
+	iob = dev->associating->beacon;
+	hdr = iob->data;
+	beacon = ( struct ieee80211_beacon * ) hdr->data;
+	ap_rsn_ie = sec80211_find_rsn ( beacon->info_element, iob->tail,
+					&ctx->ap_rsn_is_rsn, &ap_rsn_ie_end );
+	if ( ap_rsn_ie ) {
+		ctx->ap_rsn_ie = malloc ( ap_rsn_ie_end - ap_rsn_ie );
+		if ( ! ctx->ap_rsn_ie )
+			return -ENOMEM;
+		memcpy ( ctx->ap_rsn_ie, ap_rsn_ie, ap_rsn_ie_end - ap_rsn_ie );
+		ctx->ap_rsn_ie_len = ap_rsn_ie_end - ap_rsn_ie;
+	} else {
+		return -ENOENT;
+	}
+
+	ctx->crypt = dev->associating->crypto;
+	ctx->gcrypt = NET80211_CRYPT_UNKNOWN;
+
+	list_add_tail ( &ctx->list, &wpa_contexts );
+	return 0;
+}
+
+
+/**
+ * Disable handling of received WPA handshake frames
+ *
+ * @v dev	802.11 device
+ */
+void wpa_stop ( struct net80211_device *dev )
+{
+	struct wpa_common_ctx *ctx, *tmp;
+
+	list_for_each_entry_safe ( ctx, tmp, &wpa_contexts, list ) {
+		if ( ctx->dev == dev ) {
+			free ( ctx->ap_rsn_ie );
+			ctx->ap_rsn_ie = NULL;
+			list_del ( &ctx->list );
+		}
+	}
+}
+
+
+/**
+ * Check PMKID consistency
+ *
+ * @v ctx	WPA common context
+ * @v pmkid	PMKID to check against (16 bytes long)
+ * @ret rc	Zero if they match, or a negative error code if not
+ */
+int wpa_check_pmkid ( struct wpa_common_ctx *ctx, const u8 *pmkid )
+{
+	u8 sha1_ctx[SHA1_CTX_SIZE];
+	u8 my_pmkid[SHA1_SIZE];
+	u8 pmk[ctx->pmk_len];
+	size_t pmk_len;
+	struct {
+		char name[8];
+		u8 aa[ETH_ALEN];
+		u8 spa[ETH_ALEN];
+	} __attribute__ (( packed )) pmkid_data;
+
+	memcpy ( pmk, ctx->pmk, ctx->pmk_len );
+	pmk_len = ctx->pmk_len;
+
+	memcpy ( pmkid_data.name, "PMK Name", 8 );
+	memcpy ( pmkid_data.aa, ctx->dev->bssid, ETH_ALEN );
+	memcpy ( pmkid_data.spa, ctx->dev->netdev->ll_addr, ETH_ALEN );
+
+	hmac_init ( &sha1_algorithm, sha1_ctx, pmk, &pmk_len );
+	hmac_update ( &sha1_algorithm, sha1_ctx, &pmkid_data,
+		      sizeof ( pmkid_data ) );
+	hmac_final ( &sha1_algorithm, sha1_ctx, pmk, &pmk_len, my_pmkid );
+
+	if ( memcmp ( my_pmkid, pmkid, WPA_PMKID_LEN ) != 0 )
+		return -EACCES;
+
+	return 0;
+}
+
+
+/**
+ * Derive pairwise transient key
+ *
+ * @v ctx	WPA common context
+ */
+static void wpa_derive_ptk ( struct wpa_common_ctx *ctx )
+{
+	struct {
+		u8 mac1[ETH_ALEN];
+		u8 mac2[ETH_ALEN];
+		u8 nonce1[WPA_NONCE_LEN];
+		u8 nonce2[WPA_NONCE_LEN];
+	} __attribute__ (( packed )) ptk_data;
+
+	/* The addresses and nonces are stored in numerical order (!) */
+
+	if ( memcmp ( ctx->dev->netdev->ll_addr, ctx->dev->bssid,
+		      ETH_ALEN ) < 0 ) {
+		memcpy ( ptk_data.mac1, ctx->dev->netdev->ll_addr, ETH_ALEN );
+		memcpy ( ptk_data.mac2, ctx->dev->bssid, ETH_ALEN );
+	} else {
+		memcpy ( ptk_data.mac1, ctx->dev->bssid, ETH_ALEN );
+		memcpy ( ptk_data.mac2, ctx->dev->netdev->ll_addr, ETH_ALEN );
+	}
+
+	if ( memcmp ( ctx->Anonce, ctx->Snonce, WPA_NONCE_LEN ) < 0 ) {
+		memcpy ( ptk_data.nonce1, ctx->Anonce, WPA_NONCE_LEN );
+		memcpy ( ptk_data.nonce2, ctx->Snonce, WPA_NONCE_LEN );
+	} else {
+		memcpy ( ptk_data.nonce1, ctx->Snonce, WPA_NONCE_LEN );
+		memcpy ( ptk_data.nonce2, ctx->Anonce, WPA_NONCE_LEN );
+	}
+
+	DBGC2 ( ctx, "WPA %p A1 %s, A2 %s\n", ctx, eth_ntoa ( ptk_data.mac1 ),
+	       eth_ntoa ( ptk_data.mac2 ) );
+	DBGC2 ( ctx, "WPA %p Nonce1, Nonce2:\n", ctx );
+	DBGC2_HD ( ctx, ptk_data.nonce1, WPA_NONCE_LEN );
+	DBGC2_HD ( ctx, ptk_data.nonce2, WPA_NONCE_LEN );
+
+	prf_sha1 ( ctx->pmk, ctx->pmk_len,
+		   "Pairwise key expansion",
+		   &ptk_data, sizeof ( ptk_data ),
+		   &ctx->ptk, sizeof ( ctx->ptk ) );
+
+	DBGC2 ( ctx, "WPA %p PTK:\n", ctx );
+	DBGC2_HD ( ctx, &ctx->ptk, sizeof ( ctx->ptk ) );
+}
+
+
+/**
+ * Install pairwise transient key
+ *
+ * @v ctx	WPA common context
+ * @v len	Key length (16 for CCMP, 32 for TKIP)
+ * @ret rc	Return status code
+ */
+static inline int wpa_install_ptk ( struct wpa_common_ctx *ctx, int len )
+{
+	DBGC ( ctx, "WPA %p: installing %d-byte pairwise transient key\n",
+	       ctx, len );
+	DBGC2_HD ( ctx, &ctx->ptk.tk, len );
+
+	return sec80211_install ( &ctx->dev->crypto, ctx->crypt,
+				  &ctx->ptk.tk, len, NULL );
+}
+
+/**
+ * Install group transient key
+ *
+ * @v ctx	WPA common context
+ * @v len	Key length (16 for CCMP, 32 for TKIP)
+ * @v rsc	Receive sequence counter field in EAPOL-Key packet
+ * @ret rc	Return status code
+ */
+static inline int wpa_install_gtk ( struct wpa_common_ctx *ctx, int len,
+				    const void *rsc )
+{
+	DBGC ( ctx, "WPA %p: installing %d-byte group transient key\n",
+	       ctx, len );
+	DBGC2_HD ( ctx, &ctx->gtk.tk, len );
+
+	return sec80211_install ( &ctx->dev->gcrypto, ctx->gcrypt,
+				  &ctx->gtk.tk, len, rsc );
+}
+
+/**
+ * Search for group transient key, and install it if found
+ *
+ * @v ctx	WPA common context
+ * @v ie	Pointer to first IE in key data field
+ * @v ie_end	Pointer to first byte not in key data field
+ * @v rsc	Receive sequence counter field in EAPOL-Key packet
+ * @ret rc	Return status code
+ */
+static int wpa_maybe_install_gtk ( struct wpa_common_ctx *ctx,
+				   union ieee80211_ie *ie, void *ie_end,
+				   const void *rsc )
+{
+	struct wpa_kde *kde;
+
+	if ( ! ieee80211_ie_bound ( ie, ie_end ) )
+		return -ENOENT;
+
+	while ( ie ) {
+		if ( ie->id == IEEE80211_IE_VENDOR &&
+		     ie->vendor.oui == WPA_KDE_GTK )
+			break;
+
+		ie = ieee80211_next_ie ( ie, ie_end );
+	}
+
+	if ( ! ie )
+		return -ENOENT;
+
+	if ( ie->len - 6u > sizeof ( ctx->gtk.tk ) ) {
+		DBGC ( ctx, "WPA %p: GTK KDE is too long (%d bytes, max %d)\n",
+		       ctx, ie->len - 4, sizeof ( ctx->gtk.tk ) );
+		return -EINVAL;
+	}
+
+	/* XXX We ignore key ID for now. */
+	kde = ( struct wpa_kde * ) ie;
+	memcpy ( &ctx->gtk.tk, &kde->gtk_encap.gtk, kde->len - 6 );
+
+	return wpa_install_gtk ( ctx, kde->len - 6, rsc );
+}
+
+
+/**
+ * Allocate I/O buffer for construction of outgoing EAPOL-Key frame
+ *
+ * @v kdlen	Maximum number of bytes in the Key Data field
+ * @ret iob	Newly allocated I/O buffer
+ *
+ * The returned buffer will have space reserved for the link-layer and
+ * EAPOL headers, and will have @c iob->tail pointing to the start of
+ * the Key Data field. Thus, it is necessary to use iob_put() in
+ * filling the Key Data.
+ */
+static struct io_buffer * wpa_alloc_frame ( int kdlen )
+{
+	struct io_buffer *ret = alloc_iob ( sizeof ( struct eapol_key_pkt ) +
+					    kdlen + EAPOL_HDR_LEN +
+					    MAX_LL_HEADER_LEN );
+	if ( ! ret )
+		return NULL;
+
+	iob_reserve ( ret, MAX_LL_HEADER_LEN + EAPOL_HDR_LEN );
+	memset ( iob_put ( ret, sizeof ( struct eapol_key_pkt ) ), 0,
+		 sizeof ( struct eapol_key_pkt ) );
+
+	return ret;
+}
+
+
+/**
+ * Send EAPOL-Key packet
+ *
+ * @v iob	I/O buffer, with sufficient headroom for headers
+ * @v dev	802.11 device
+ * @v kie	Key integrity and encryption handler
+ * @v is_rsn	If TRUE, handshake uses new RSN format
+ * @ret rc	Return status code
+ *
+ * If a KIE is specified, the MIC will be filled in before transmission.
+ */
+static int wpa_send_eapol ( struct io_buffer *iob, struct wpa_common_ctx *ctx,
+			    struct wpa_kie *kie )
+{
+	struct eapol_key_pkt *pkt = iob->data;
+	struct eapol_frame *eapol = iob_push ( iob, EAPOL_HDR_LEN );
+
+	pkt->info = htons ( pkt->info );
+	pkt->keysize = htons ( pkt->keysize );
+	pkt->datalen = htons ( pkt->datalen );
+	pkt->replay = cpu_to_be64 ( pkt->replay );
+	eapol->version = EAPOL_THIS_VERSION;
+	eapol->type = EAPOL_TYPE_KEY;
+	eapol->length = htons ( iob->tail - iob->data - sizeof ( *eapol ) );
+
+	memset ( pkt->mic, 0, sizeof ( pkt->mic ) );
+	if ( kie )
+		kie->mic ( &ctx->ptk.kck, eapol, EAPOL_HDR_LEN +
+			   sizeof ( *pkt ) + ntohs ( pkt->datalen ),
+			   pkt->mic );
+
+	return net_tx ( iob, ctx->dev->netdev, &eapol_protocol,
+			ctx->dev->bssid );
+}
+
+
+/**
+ * Send second frame in 4-Way Handshake
+ *
+ * @v ctx	WPA common context
+ * @v pkt	First frame, to which this is a reply
+ * @v is_rsn	If TRUE, handshake uses new RSN format
+ * @v kie	Key integrity and encryption handler
+ * @ret rc	Return status code
+ */
+static int wpa_send_2_of_4 ( struct wpa_common_ctx *ctx,
+			     struct eapol_key_pkt *pkt, int is_rsn,
+			     struct wpa_kie *kie )
+{
+	struct io_buffer *iob = wpa_alloc_frame ( ctx->dev->rsn_ie->len + 2 );
+	struct eapol_key_pkt *npkt;
+
+	if ( ! iob )
+		return -ENOMEM;
+
+	npkt = iob->data;
+	memcpy ( npkt, pkt, sizeof ( *pkt ) );
+	npkt->info &= ~EAPOL_KEY_INFO_KEY_ACK;
+	npkt->info |= EAPOL_KEY_INFO_KEY_MIC;
+	if ( is_rsn )
+		npkt->keysize = 0;
+	memcpy ( npkt->nonce, ctx->Snonce, sizeof ( npkt->nonce ) );
+	npkt->datalen = ctx->dev->rsn_ie->len + 2;
+	memcpy ( iob_put ( iob, npkt->datalen ), ctx->dev->rsn_ie,
+		 npkt->datalen );
+
+	DBGC ( ctx, "WPA %p: sending 2/4\n", ctx );
+
+	return wpa_send_eapol ( iob, ctx, kie );
+}
+
+
+/**
+ * Handle receipt of first frame in 4-Way Handshake
+ *
+ * @v ctx	WPA common context
+ * @v pkt	EAPOL-Key packet
+ * @v is_rsn	If TRUE, frame uses new RSN format
+ * @v kie	Key integrity and encryption handler
+ * @ret rc	Return status code
+ */
+static int wpa_handle_1_of_4 ( struct wpa_common_ctx *ctx,
+			       struct eapol_key_pkt *pkt, int is_rsn,
+			       struct wpa_kie *kie )
+{
+	int rc;
+
+	if ( ctx->state == WPA_WAITING )
+		return -EINVAL;
+
+	ctx->state = WPA_WORKING;
+	memcpy ( ctx->Anonce, pkt->nonce, sizeof ( ctx->Anonce ) );
+	if ( ! ctx->have_Snonce ) {
+		get_random_bytes ( ctx->Snonce, sizeof ( ctx->Snonce ) );
+		ctx->have_Snonce = 1;
+	}
+
+	if ( is_rsn && pkt->datalen ) {
+		union ieee80211_ie *ie = ( union ieee80211_ie * ) pkt->data;
+		void *ie_end = pkt->data + pkt->datalen;
+
+		if ( ! ieee80211_ie_bound ( ie, ie_end ) ) {
+			DBGC ( ctx, "WPA %p: malformed PMKID KDE\n", ctx );
+			return wpa_fail ( ctx, -EINVAL );
+		}
+
+		while ( ie ) {
+			if ( ie->id == IEEE80211_IE_VENDOR &&
+			     ie->vendor.oui == WPA_KDE_PMKID ) {
+				rc = wpa_check_pmkid ( ctx, ie->vendor.data );
+				if ( rc < 0 ) {
+					DBGC ( ctx, "WPA %p ALERT: PMKID "
+					       "mismatch in 1/4\n", ctx );
+					return wpa_fail ( ctx, rc );
+				}
+			}
+
+			ie = ieee80211_next_ie ( ie, ie_end );
+		}
+	}
+
+	DBGC ( ctx, "WPA %p: received 1/4, looks OK\n", ctx );
+
+	wpa_derive_ptk ( ctx );
+
+	return wpa_send_2_of_4 ( ctx, pkt, is_rsn, kie );
+}
+
+
+/**
+ * Send fourth frame in 4-Way Handshake, or second in Group Key Handshake
+ *
+ * @v ctx	WPA common context
+ * @v pkt	EAPOL-Key packet for frame to which we're replying
+ * @v is_rsn	If TRUE, frame uses new RSN format
+ * @v kie	Key integrity and encryption handler
+ * @ret rc	Return status code
+ */
+static int wpa_send_final ( struct wpa_common_ctx *ctx,
+			    struct eapol_key_pkt *pkt, int is_rsn,
+			    struct wpa_kie *kie )
+{
+	struct io_buffer *iob = wpa_alloc_frame ( 0 );
+	struct eapol_key_pkt *npkt;
+
+	if ( ! iob )
+		return -ENOMEM;
+
+	npkt = iob->data;
+	memcpy ( npkt, pkt, sizeof ( *pkt ) );
+	npkt->info &= ~( EAPOL_KEY_INFO_KEY_ACK | EAPOL_KEY_INFO_INSTALL |
+			 EAPOL_KEY_INFO_KEY_ENC );
+	if ( is_rsn )
+		npkt->keysize = 0;
+	memset ( npkt->nonce, 0, sizeof ( npkt->nonce ) );
+	memset ( npkt->iv, 0, sizeof ( npkt->iv ) );
+	npkt->datalen = 0;
+
+	if ( npkt->info & EAPOL_KEY_INFO_TYPE )
+		DBGC ( ctx, "WPA %p: sending 4/4\n", ctx );
+	else
+		DBGC ( ctx, "WPA %p: sending 2/2\n", ctx );
+
+	return wpa_send_eapol ( iob, ctx, kie );
+
+}
+
+
+/**
+ * Handle receipt of third frame in 4-Way Handshake
+ *
+ * @v ctx	WPA common context
+ * @v pkt	EAPOL-Key packet
+ * @v is_rsn	If TRUE, frame uses new RSN format
+ * @v kie	Key integrity and encryption handler
+ * @ret rc	Return status code
+ */
+static int wpa_handle_3_of_4 ( struct wpa_common_ctx *ctx,
+			       struct eapol_key_pkt *pkt, int is_rsn,
+			       struct wpa_kie *kie )
+{
+	int rc;
+	u8 *this_rsn, *this_rsn_end;
+	u8 *new_rsn, *new_rsn_end;
+	int this_is_rsn, new_is_rsn;
+
+	if ( ctx->state == WPA_WAITING )
+		return -EINVAL;
+
+	ctx->state = WPA_WORKING;
+
+	/* Check nonce */
+	if ( memcmp ( ctx->Anonce, pkt->nonce, WPA_NONCE_LEN ) != 0 ) {
+		DBGC ( ctx, "WPA %p ALERT: nonce mismatch in 3/4\n", ctx );
+		return wpa_fail ( ctx, -EACCES );
+	}
+
+	/* Check RSN IE */
+	this_rsn = sec80211_find_rsn ( ( union ieee80211_ie * ) pkt->data,
+				       pkt->data + pkt->datalen,
+				       &this_is_rsn, &this_rsn_end );
+	if ( this_rsn )
+		new_rsn = sec80211_find_rsn ( ( union ieee80211_ie * )
+					              this_rsn_end,
+					      pkt->data + pkt->datalen,
+					      &new_is_rsn, &new_rsn_end );
+	else
+		new_rsn = NULL;
+
+	if ( ! ctx->ap_rsn_ie || ! this_rsn ||
+	     ctx->ap_rsn_ie_len != ( this_rsn_end - this_rsn ) ||
+	     ctx->ap_rsn_is_rsn != this_is_rsn ||
+	     memcmp ( ctx->ap_rsn_ie, this_rsn, ctx->ap_rsn_ie_len ) != 0 ) {
+		DBGC ( ctx, "WPA %p ALERT: RSN mismatch in 3/4\n", ctx );
+		DBGC2 ( ctx, "WPA %p RSNs (in 3/4, in beacon):\n", ctx );
+		DBGC2_HD ( ctx, this_rsn, this_rsn_end - this_rsn );
+		DBGC2_HD ( ctx, ctx->ap_rsn_ie, ctx->ap_rsn_ie_len );
+		return wpa_fail ( ctx, -EACCES );
+	}
+
+	/* Don't switch if they just supplied both styles of IE
+	   simultaneously; we need two RSN IEs or two WPA IEs to
+	   switch ciphers. They'll be immediately consecutive because
+	   of ordering guarantees. */
+	if ( new_rsn && this_is_rsn == new_is_rsn ) {
+		struct net80211_wlan *assoc = ctx->dev->associating;
+		DBGC ( ctx, "WPA %p: accommodating bait-and-switch tactics\n",
+		       ctx );
+		DBGC2 ( ctx, "WPA %p RSNs (in 3/4+beacon, new in 3/4):\n",
+			ctx );
+		DBGC2_HD ( ctx, this_rsn, this_rsn_end - this_rsn );
+		DBGC2_HD ( ctx, new_rsn, new_rsn_end - new_rsn );
+
+		if ( ( rc = sec80211_detect_ie ( new_is_rsn, new_rsn,
+						 new_rsn_end,
+						 &assoc->handshaking,
+						 &assoc->crypto ) ) != 0 )
+			DBGC ( ctx, "WPA %p: bait-and-switch invalid, staying "
+			       "with original request\n", ctx );
+	} else {
+		new_rsn = this_rsn;
+		new_is_rsn = this_is_rsn;
+		new_rsn_end = this_rsn_end;
+	}
+
+	/* Grab group cryptosystem ID */
+	ctx->gcrypt = sec80211_rsn_get_net80211_crypt ( *( u32 * )
+							( new_rsn + 2 ) );
+
+	/* Check for a GTK, if info field is encrypted */
+	if ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) {
+		rc = wpa_maybe_install_gtk ( ctx,
+					     ( union ieee80211_ie * ) pkt->data,
+					     pkt->data + pkt->datalen,
+					     pkt->rsc );
+		if ( rc < 0 ) {
+			DBGC ( ctx, "WPA %p did not install GTK in 3/4: %s\n",
+			       ctx, strerror ( rc ) );
+			if ( rc != -ENOENT )
+				return wpa_fail ( ctx, rc );
+		}
+	}
+
+	DBGC ( ctx, "WPA %p: received 3/4, looks OK\n", ctx );
+
+	/* Send final message */
+	rc = wpa_send_final ( ctx, pkt, is_rsn, kie );
+	if ( rc < 0 )
+		return wpa_fail ( ctx, rc );
+
+	/* Install PTK */
+	rc = wpa_install_ptk ( ctx, pkt->keysize );
+	if ( rc < 0 ) {
+		DBGC ( ctx, "WPA %p failed to install PTK: %s\n", ctx,
+		       strerror ( rc ) );
+		return wpa_fail ( ctx, rc );
+	}
+
+	/* Mark us as needing a new Snonce if we rekey */
+	ctx->have_Snonce = 0;
+
+	/* Done! */
+	ctx->state = WPA_SUCCESS;
+	return 0;
+}
+
+
+/**
+ * Handle receipt of first frame in Group Key Handshake
+ *
+ * @v ctx	WPA common context
+ * @v pkt	EAPOL-Key packet
+ * @v is_rsn	If TRUE, frame uses new RSN format
+ * @v kie	Key integrity and encryption handler
+ * @ret rc	Return status code
+ */
+static int wpa_handle_1_of_2 ( struct wpa_common_ctx *ctx,
+			       struct eapol_key_pkt *pkt, int is_rsn,
+			       struct wpa_kie *kie )
+{
+	int rc;
+
+	/*
+	 * WPA and RSN do this completely differently.
+	 *
+	 * The idea of encoding the GTK (or PMKID, or various other
+	 * things) into a KDE that looks like an information element
+	 * is an RSN innovation; old WPA code never encapsulates
+	 * things like that. If it looks like an info element, it
+	 * really is (for the WPA IE check in frames 2/4 and 3/4). The
+	 * "key data encrypted" bit in the info field is also specific
+	 * to RSN.
+	 *
+	 * So from an old WPA host, 3/4 does not contain an
+	 * encapsulated GTK. The first frame of the GK handshake
+	 * contains it, encrypted, but without a KDE wrapper, and with
+	 * the key ID field (which gPXE doesn't use) shoved away in
+	 * the reserved bits in the info field, and the TxRx bit
+	 * stealing the Install bit's spot.
+	 */
+
+	if ( is_rsn && ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) ) {
+		rc = wpa_maybe_install_gtk ( ctx,
+					     ( union ieee80211_ie * ) pkt->data,
+					     pkt->data + pkt->datalen,
+					     pkt->rsc );
+		if ( rc < 0 ) {
+			DBGC ( ctx, "WPA %p: failed to install GTK in 1/2: "
+			       "%s\n", ctx, strerror ( rc ) );
+			return wpa_fail ( ctx, rc );
+		}
+	} else {
+		rc = kie->decrypt ( &ctx->ptk.kek, pkt->iv, pkt->data,
+				    &pkt->datalen );
+		if ( rc < 0 ) {
+			DBGC ( ctx, "WPA %p: failed to decrypt GTK: %s\n",
+			       ctx, strerror ( rc ) );
+			return rc; /* non-fatal */
+		}
+		if ( pkt->datalen > sizeof ( ctx->gtk.tk ) ) {
+			DBGC ( ctx, "WPA %p: too much GTK data (%d > %d)\n",
+			       ctx, pkt->datalen, sizeof ( ctx->gtk.tk ) );
+			return wpa_fail ( ctx, -EINVAL );
+		}
+
+		memcpy ( &ctx->gtk.tk, pkt->data, pkt->datalen );
+		wpa_install_gtk ( ctx, pkt->datalen, pkt->rsc );
+	}
+
+	DBGC ( ctx, "WPA %p: received 1/2, looks OK\n", ctx );
+
+	return wpa_send_final ( ctx, pkt, is_rsn, kie );
+}
+
+
+/**
+ * Handle receipt of EAPOL-Key frame for WPA
+ *
+ * @v iob	I/O buffer
+ * @v netdev	Network device
+ * @v ll_source	Source link-layer address
+ */
+static int eapol_key_rx ( struct io_buffer *iob, struct net_device *netdev,
+			  const void *ll_source )
+{
+	struct net80211_device *dev = net80211_get ( netdev );
+	struct eapol_key_pkt *pkt = iob->data;
+	int is_rsn, found_ctx;
+	struct wpa_common_ctx *ctx;
+	int rc = 0;
+	struct wpa_kie *kie;
+	u8 their_mic[16], our_mic[16];
+
+	if ( pkt->type != EAPOL_KEY_TYPE_WPA &&
+	     pkt->type != EAPOL_KEY_TYPE_RSN ) {
+		DBG ( "EAPOL-Key: packet not of 802.11 type\n" );
+		rc = -EINVAL;
+		goto drop;
+	}
+
+	is_rsn = ( pkt->type == EAPOL_KEY_TYPE_RSN );
+
+	if ( ! dev ) {
+		DBG ( "EAPOL-Key: packet not from 802.11\n" );
+		rc = -EINVAL;
+		goto drop;
+	}
+
+	if ( memcmp ( dev->bssid, ll_source, ETH_ALEN ) != 0 ) {
+		DBG ( "EAPOL-Key: packet not from associated AP\n" );
+		rc = -EINVAL;
+		goto drop;
+	}
+
+	if ( ! ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_KEY_ACK ) ) {
+		DBG ( "EAPOL-Key: packet sent in wrong direction\n" );
+		rc = -EINVAL;
+		goto drop;
+	}
+
+	found_ctx = 0;
+	list_for_each_entry ( ctx, &wpa_contexts, list ) {
+		if ( ctx->dev == dev ) {
+			found_ctx = 1;
+			break;
+		}
+	}
+
+	if ( ! found_ctx ) {
+		DBG ( "EAPOL-Key: no WPA context to handle packet for %p\n",
+		      dev );
+		rc = -ENOENT;
+		goto drop;
+	}
+
+	if ( ( void * ) ( pkt + 1 ) + ntohs ( pkt->datalen ) > iob->tail ) {
+		DBGC ( ctx, "WPA %p: packet truncated (has %d extra bytes, "
+		       "states %d)\n", ctx, iob->tail - ( void * ) ( pkt + 1 ),
+		       ntohs ( pkt->datalen ) );
+		rc = -EINVAL;
+		goto drop;
+	}
+
+	/* Get a handle on key integrity/encryption handler */
+	kie = wpa_find_kie ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_VERSION );
+	if ( ! kie ) {
+		DBGC ( ctx, "WPA %p: no support for packet version %d\n", ctx,
+		       ntohs ( pkt->info ) & EAPOL_KEY_INFO_VERSION );
+		rc = wpa_fail ( ctx, -ENOTSUP );
+		goto drop;
+	}
+
+	/* Check MIC */
+	if ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_KEY_MIC ) {
+		memcpy ( their_mic, pkt->mic, sizeof ( pkt->mic ) );
+		memset ( pkt->mic, 0, sizeof ( pkt->mic ) );
+		kie->mic ( &ctx->ptk.kck, ( void * ) pkt - EAPOL_HDR_LEN,
+			   EAPOL_HDR_LEN + sizeof ( *pkt ) +
+			   ntohs ( pkt->datalen ), our_mic );
+		DBGC2 ( ctx, "WPA %p MIC comparison (theirs, ours):\n", ctx );
+		DBGC2_HD ( ctx, their_mic, 16 );
+		DBGC2_HD ( ctx, our_mic, 16 );
+		if ( memcmp ( their_mic, our_mic, sizeof ( pkt->mic ) ) != 0 ) {
+			DBGC ( ctx, "WPA %p: EAPOL MIC failure\n", ctx );
+			goto drop;
+		}
+	}
+
+	/* Fix byte order to local */
+	pkt->info = ntohs ( pkt->info );
+	pkt->keysize = ntohs ( pkt->keysize );
+	pkt->datalen = ntohs ( pkt->datalen );
+	pkt->replay = be64_to_cpu ( pkt->replay );
+
+	/* Check replay counter */
+	if ( ctx->replay != ~0ULL && ctx->replay >= pkt->replay ) {
+		DBGC ( ctx, "WPA %p ALERT: Replay detected! "
+		       "(%08x:%08x >= %08x:%08x)\n", ctx,
+		       ( u32 ) ( ctx->replay >> 32 ), ( u32 ) ctx->replay,
+		       ( u32 ) ( pkt->replay >> 32 ), ( u32 ) pkt->replay );
+		rc = 0;		/* ignore without error */
+		goto drop;
+	}
+	ctx->replay = pkt->replay;
+
+	/* Decrypt key data */
+	if ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) {
+		rc = kie->decrypt ( &ctx->ptk.kek, pkt->iv, pkt->data,
+				    &pkt->datalen );
+		if ( rc < 0 ) {
+			DBGC ( ctx, "WPA %p: failed to decrypt packet: %s\n",
+			       ctx, strerror ( rc ) );
+			goto drop;
+		}
+	}
+
+	/* Hand it off to appropriate handler */
+	switch ( pkt->info & ( EAPOL_KEY_INFO_TYPE |
+			       EAPOL_KEY_INFO_KEY_MIC ) ) {
+	case EAPOL_KEY_TYPE_PTK:
+		rc = wpa_handle_1_of_4 ( ctx, pkt, is_rsn, kie );
+		break;
+
+	case EAPOL_KEY_TYPE_PTK | EAPOL_KEY_INFO_KEY_MIC:
+		rc = wpa_handle_3_of_4 ( ctx, pkt, is_rsn, kie );
+		break;
+
+	case EAPOL_KEY_TYPE_GTK | EAPOL_KEY_INFO_KEY_MIC:
+		rc = wpa_handle_1_of_2 ( ctx, pkt, is_rsn, kie );
+		break;
+
+	default:
+		DBGC ( ctx, "WPA %p: Invalid combination of key flags %04x\n",
+		       ctx, pkt->info );
+		rc = -EINVAL;
+		break;
+	}
+
+ drop:
+	free_iob ( iob );
+	return rc;
+}
+
+struct eapol_handler eapol_key_handler __eapol_handler = {
+	.type = EAPOL_TYPE_KEY,
+	.rx = eapol_key_rx,
+};
+
+/* WPA always needs EAPOL in order to be useful */
+REQUIRE_OBJECT ( eapol );
diff --git a/gpxe/src/net/80211/wpa_ccmp.c b/gpxe/src/net/80211/wpa_ccmp.c
new file mode 100644
index 0000000..08b1e17
--- /dev/null
+++ b/gpxe/src/net/80211/wpa_ccmp.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/net80211.h>
+#include <gpxe/crypto.h>
+#include <gpxe/hmac.h>
+#include <gpxe/sha1.h>
+#include <gpxe/aes.h>
+#include <gpxe/wpa.h>
+#include <byteswap.h>
+#include <errno.h>
+
+/** @file
+ *
+ * Backend for WPA using the CCMP encryption method
+ */
+
+/** Context for CCMP encryption and decryption */
+struct ccmp_ctx
+{
+	/** AES context - only ever used for encryption */
+	u8 aes_ctx[AES_CTX_SIZE];
+
+	/** Most recently sent packet number */
+	u64 tx_seq;
+
+	/** Most recently received packet number */
+	u64 rx_seq;
+};
+
+/** Header structure at the beginning of CCMP frame data */
+struct ccmp_head
+{
+	u8 pn_lo[2];		/**< Bytes 0 and 1 of packet number */
+	u8 _rsvd;		/**< Reserved byte */
+	u8 kid;			/**< Key ID and ExtIV byte */
+	u8 pn_hi[4];		/**< Bytes 2-5 (2 first) of packet number */
+} __attribute__ (( packed ));
+
+
+/** CCMP header overhead */
+#define CCMP_HEAD_LEN	8
+
+/** CCMP MIC trailer overhead */
+#define CCMP_MIC_LEN	8
+
+/** CCMP nonce length */
+#define CCMP_NONCE_LEN	13
+
+/** CCMP nonce structure */
+struct ccmp_nonce
+{
+	u8 prio;		/**< Packet priority, 0 for non-QoS */
+	u8 a2[ETH_ALEN];	/**< Address 2 from packet header (sender) */
+	u8 pn[6];		/**< Packet number */
+} __attribute__ (( packed ));
+
+/** CCMP additional authentication data length (for non-QoS, non-WDS frames) */
+#define CCMP_AAD_LEN	22
+
+/** CCMP additional authentication data structure */
+struct ccmp_aad
+{
+	u16 fc;			/**< Frame Control field */
+	u8 a1[6];		/**< Address 1 */
+	u8 a2[6];		/**< Address 2 */
+	u8 a3[6];		/**< Address 3 */
+	u16 seq;		/**< Sequence Control field */
+	/* Address 4 and QoS Control are included if present */
+} __attribute__ (( packed ));
+
+/** Mask for Frame Control field in AAD */
+#define CCMP_AAD_FC_MASK	0xC38F
+
+/** Mask for Sequence Control field in AAD */
+#define CCMP_AAD_SEQ_MASK	0x000F
+
+
+/**
+ * Convert 6-byte LSB packet number to 64-bit integer
+ *
+ * @v pn	Pointer to 6-byte packet number
+ * @ret v	64-bit integer value of @a pn
+ */
+static u64 pn_to_u64 ( const u8 *pn )
+{
+	int i;
+	u64 ret = 0;
+
+	for ( i = 5; i >= 0; i-- ) {
+		ret <<= 8;
+		ret |= pn[i];
+	}
+
+	return ret;
+}
+
+/**
+ * Convert 64-bit integer to 6-byte packet number
+ *
+ * @v v		64-bit integer
+ * @v msb	If TRUE, reverse the output PN to be in MSB order
+ * @ret pn	6-byte packet number
+ *
+ * The PN is stored in LSB order in the packet header and in MSB order
+ * in the nonce. WHYYYYY?
+ */
+static void u64_to_pn ( u64 v, u8 *pn, int msb )
+{
+	int i;
+	u8 *pnp = pn + ( msb ? 5 : 0 );
+	int delta = ( msb ? -1 : +1 );
+
+	for ( i = 0; i < 6; i++ ) {
+		*pnp = v & 0xFF;
+		pnp += delta;
+		v >>= 8;
+	}
+}
+
+/** Value for @a msb argument of u64_to_pn() for MSB output */
+#define PN_MSB	1
+
+/** Value for @a msb argument of u64_to_pn() for LSB output */
+#define PN_LSB	0
+
+
+
+/**
+ * Initialise CCMP state and install key
+ *
+ * @v crypto	CCMP cryptosystem structure
+ * @v key	Pointer to 16-byte temporal key to install
+ * @v keylen	Length of key (16 bytes)
+ * @v rsc	Initial receive sequence counter
+ */
+static int ccmp_init ( struct net80211_crypto *crypto, const void *key,
+		       int keylen, const void *rsc )
+{
+	struct ccmp_ctx *ctx = crypto->priv;
+
+	if ( keylen != 16 )
+		return -EINVAL;
+
+	if ( rsc )
+		ctx->rx_seq = pn_to_u64 ( rsc );
+
+	cipher_setkey ( &aes_algorithm, ctx->aes_ctx, key, keylen );
+
+	return 0;
+}
+
+
+/**
+ * Encrypt or decrypt data stream using AES in Counter mode
+ *
+ * @v ctx	CCMP cryptosystem context
+ * @v nonce	Nonce value, 13 bytes
+ * @v srcv	Data to encrypt or decrypt
+ * @v len	Number of bytes pointed to by @a src
+ * @v msrcv	MIC value to encrypt or decrypt (may be NULL)
+ * @ret destv	Encrypted or decrypted data
+ * @ret mdestv	Encrypted or decrypted MIC value
+ *
+ * This assumes CCMP parameters of L=2 and M=8. The algorithm is
+ * defined in RFC 3610.
+ */
+static void ccmp_ctr_xor ( struct ccmp_ctx *ctx, const void *nonce,
+			   const void *srcv, void *destv, int len,
+			   const void *msrcv, void *mdestv )
+{
+	u8 A[16], S[16];
+	u16 ctr;
+	int i;
+	const u8 *src = srcv, *msrc = msrcv;
+	u8 *dest = destv, *mdest = mdestv;
+
+	A[0] = 0x01;		/* flags, L' = L - 1 = 1, other bits rsvd */
+	memcpy ( A + 1, nonce, CCMP_NONCE_LEN );
+
+	if ( msrcv ) {
+		A[14] = A[15] = 0;
+
+		cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, A, S, 16 );
+
+		for ( i = 0; i < 8; i++ ) {
+			*mdest++ = *msrc++ ^ S[i];
+		}
+	}
+
+	for ( ctr = 1 ;; ctr++ ) {
+		A[14] = ctr >> 8;
+		A[15] = ctr & 0xFF;
+
+		cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, A, S, 16 );
+
+		for ( i = 0; i < len && i < 16; i++ )
+			*dest++ = *src++ ^ S[i];
+
+		if ( len <= 16 )
+			break;	/* we're done */
+
+		len -= 16;
+	}
+}
+
+
+/**
+ * Advance one block in CBC-MAC calculation
+ *
+ * @v aes_ctx	AES encryption context with key set
+ * @v B		Cleartext block to incorporate (16 bytes)
+ * @v X		Previous ciphertext block (16 bytes)
+ * @ret B	Clobbered
+ * @ret X	New ciphertext block (16 bytes)
+ *
+ * This function does X := E[key] ( X ^ B ).
+ */
+static void ccmp_feed_cbc_mac ( void *aes_ctx, u8 *B, u8 *X )
+{
+	int i;
+	for ( i = 0; i < 16; i++ )
+		B[i] ^= X[i];
+	cipher_encrypt ( &aes_algorithm, aes_ctx, B, X, 16 );
+}
+
+
+/**
+ * Calculate MIC on plaintext data using CBC-MAC
+ *
+ * @v ctx	CCMP cryptosystem context
+ * @v nonce	Nonce value, 13 bytes
+ * @v data	Data to calculate MIC over
+ * @v datalen	Length of @a data
+ * @v aad	Additional authentication data, for MIC but not encryption
+ * @ret mic	MIC value (unencrypted), 8 bytes
+ *
+ * @a aadlen is assumed to be 22 bytes long, as it always is for
+ * 802.11 use when transmitting non-QoS, not-between-APs frames (the
+ * only type we deal with).
+ */
+static void ccmp_cbc_mac ( struct ccmp_ctx *ctx, const void *nonce,
+			   const void *data, u16 datalen,
+			   const void *aad, void *mic )
+{
+	u8 X[16], B[16];
+
+	/* Zeroth block: flags, nonce, length */
+
+	/* Rsv AAD - M'-  - L'-
+	 *  0   1  0 1 1  0 0 1   for an 8-byte MAC and 2-byte message length
+	 */
+	B[0] = 0x59;
+	memcpy ( B + 1, nonce, CCMP_NONCE_LEN );
+	B[14] = datalen >> 8;
+	B[15] = datalen & 0xFF;
+
+	cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, B, X, 16 );
+
+	/* First block: AAD length field and 14 bytes of AAD */
+	B[0] = 0;
+	B[1] = CCMP_AAD_LEN;
+	memcpy ( B + 2, aad, 14 );
+
+	ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );
+
+	/* Second block: Remaining 8 bytes of AAD, 8 bytes zero pad */
+	memcpy ( B, aad + 14, 8 );
+	memset ( B + 8, 0, 8 );
+
+	ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );
+
+	/* Message blocks */
+	while ( datalen ) {
+		if ( datalen >= 16 ) {
+			memcpy ( B, data, 16 );
+			datalen -= 16;
+		} else {
+			memcpy ( B, data, datalen );
+			memset ( B + datalen, 0, 16 - datalen );
+			datalen = 0;
+		}
+
+		ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );
+
+		data += 16;
+	}
+
+	/* Get MIC from final value of X */
+	memcpy ( mic, X, 8 );
+}
+
+
+/**
+ * Encapsulate and encrypt a packet using CCMP
+ *
+ * @v crypto	CCMP cryptosystem
+ * @v iob	I/O buffer containing cleartext packet
+ * @ret eiob	I/O buffer containing encrypted packet
+ */
+struct io_buffer * ccmp_encrypt ( struct net80211_crypto *crypto,
+				  struct io_buffer *iob )
+{
+	struct ccmp_ctx *ctx = crypto->priv;
+	struct ieee80211_frame *hdr = iob->data;
+	struct io_buffer *eiob;
+	const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
+	int datalen = iob_len ( iob ) - hdrlen;
+	struct ccmp_head head;
+	struct ccmp_nonce nonce;
+	struct ccmp_aad aad;
+	u8 mic[8], tx_pn[6];
+	void *edata, *emic;
+
+	ctx->tx_seq++;
+	u64_to_pn ( ctx->tx_seq, tx_pn, PN_LSB );
+
+	/* Allocate memory */
+	eiob = alloc_iob ( iob_len ( iob ) + CCMP_HEAD_LEN + CCMP_MIC_LEN );
+	if ( ! eiob )
+		return NULL;
+
+	/* Copy frame header */
+	memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen );
+	hdr = eiob->data;
+	hdr->fc |= IEEE80211_FC_PROTECTED;
+
+	/* Fill in packet number and extended IV */
+	memcpy ( head.pn_lo, tx_pn, 2 );
+	memcpy ( head.pn_hi, tx_pn + 2, 4 );
+	head.kid = 0x20;	/* have Extended IV, key ID 0 */
+	head._rsvd = 0;
+	memcpy ( iob_put ( eiob, sizeof ( head ) ), &head, sizeof ( head ) );
+
+	/* Form nonce */
+	nonce.prio = 0;
+	memcpy ( nonce.a2, hdr->addr2, ETH_ALEN );
+	u64_to_pn ( ctx->tx_seq, nonce.pn, PN_MSB );
+
+	/* Form additional authentication data */
+	aad.fc = hdr->fc & CCMP_AAD_FC_MASK;
+	memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */
+	aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK;
+
+	/* Calculate MIC over the data */
+	ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad, mic );
+
+	/* Copy and encrypt data and MIC */
+	edata = iob_put ( eiob, datalen );
+	emic = iob_put ( eiob, CCMP_MIC_LEN );
+	ccmp_ctr_xor ( ctx, &nonce,
+		       iob->data + hdrlen, edata, datalen,
+		       mic, emic );
+
+	/* Done! */
+	DBGC2 ( ctx, "WPA-CCMP %p: encrypted packet %p -> %p\n", ctx,
+		iob, eiob );
+
+	return eiob;
+}
+
+/**
+ * Decrypt a packet using CCMP
+ *
+ * @v crypto	CCMP cryptosystem
+ * @v eiob	I/O buffer containing encrypted packet
+ * @ret iob	I/O buffer containing cleartext packet
+ */
+static struct io_buffer * ccmp_decrypt ( struct net80211_crypto *crypto,
+					 struct io_buffer *eiob )
+{
+	struct ccmp_ctx *ctx = crypto->priv;
+	struct ieee80211_frame *hdr;
+	struct io_buffer *iob;
+	const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
+	int datalen = iob_len ( eiob ) - hdrlen - CCMP_HEAD_LEN - CCMP_MIC_LEN;
+	struct ccmp_head *head;
+	struct ccmp_nonce nonce;
+	struct ccmp_aad aad;
+	u8 rx_pn[6], their_mic[8], our_mic[8];
+
+	iob = alloc_iob ( hdrlen + datalen );
+	if ( ! iob )
+		return NULL;
+
+	/* Copy frame header */
+	memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen );
+	hdr = iob->data;
+	hdr->fc &= ~IEEE80211_FC_PROTECTED;
+
+	/* Check and update RX packet number */
+	head = eiob->data + hdrlen;
+	memcpy ( rx_pn, head->pn_lo, 2 );
+	memcpy ( rx_pn + 2, head->pn_hi, 4 );
+
+	if ( pn_to_u64 ( rx_pn ) <= ctx->rx_seq ) {
+		DBGC ( ctx, "WPA-CCMP %p: packet received out of order "
+		       "(%012llx <= %012llx)\n", ctx, pn_to_u64 ( rx_pn ),
+		       ctx->rx_seq );
+		free_iob ( iob );
+		return NULL;
+	}
+
+	ctx->rx_seq = pn_to_u64 ( rx_pn );
+	DBGC2 ( ctx, "WPA-CCMP %p: RX packet number %012llx\n", ctx, ctx->rx_seq );
+
+	/* Form nonce */
+	nonce.prio = 0;
+	memcpy ( nonce.a2, hdr->addr2, ETH_ALEN );
+	u64_to_pn ( ctx->rx_seq, nonce.pn, PN_MSB );
+
+	/* Form additional authentication data */
+	aad.fc = ( hdr->fc & CCMP_AAD_FC_MASK ) | IEEE80211_FC_PROTECTED;
+	memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */
+	aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK;
+
+	/* Copy-decrypt data and MIC */
+	ccmp_ctr_xor ( ctx, &nonce, eiob->data + hdrlen + sizeof ( *head ),
+		       iob_put ( iob, datalen ), datalen,
+		       eiob->tail - CCMP_MIC_LEN, their_mic );
+
+	/* Check MIC */
+	ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad,
+		       our_mic );
+
+	if ( memcmp ( their_mic, our_mic, CCMP_MIC_LEN ) != 0 ) {
+		DBGC2 ( ctx, "WPA-CCMP %p: MIC failure\n", ctx );
+		free_iob ( iob );
+		return NULL;
+	}
+
+	DBGC2 ( ctx, "WPA-CCMP %p: decrypted packet %p -> %p\n", ctx,
+		eiob, iob );
+
+	return iob;
+}
+
+
+/** CCMP cryptosystem */
+struct net80211_crypto ccmp_crypto __net80211_crypto = {
+	.algorithm = NET80211_CRYPT_CCMP,
+	.init = ccmp_init,
+	.encrypt = ccmp_encrypt,
+	.decrypt = ccmp_decrypt,
+	.priv_len = sizeof ( struct ccmp_ctx ),
+};
+
+
+
+
+/**
+ * Calculate HMAC-SHA1 MIC for EAPOL-Key frame
+ *
+ * @v kck	Key Confirmation Key, 16 bytes
+ * @v msg	Message to calculate MIC over
+ * @v len	Number of bytes to calculate MIC over
+ * @ret mic	Calculated MIC, 16 bytes long
+ */
+static void ccmp_kie_mic ( const void *kck, const void *msg, size_t len,
+			   void *mic )
+{
+	u8 sha1_ctx[SHA1_CTX_SIZE];
+	u8 kckb[16];
+	u8 hash[SHA1_SIZE];
+	size_t kck_len = 16;
+
+	memcpy ( kckb, kck, kck_len );
+
+	hmac_init ( &sha1_algorithm, sha1_ctx, kckb, &kck_len );
+	hmac_update ( &sha1_algorithm, sha1_ctx, msg, len );
+	hmac_final ( &sha1_algorithm, sha1_ctx, kckb, &kck_len, hash );
+
+	memcpy ( mic, hash, 16 );
+}
+
+/**
+ * Decrypt key data in EAPOL-Key frame
+ *
+ * @v kek	Key Encryption Key, 16 bytes
+ * @v iv	Initialisation vector, 16 bytes (unused)
+ * @v msg	Message to decrypt
+ * @v len	Length of message
+ * @ret msg	Decrypted message in place of original
+ * @ret len	Adjusted downward for 8 bytes of overhead
+ * @ret rc	Return status code
+ *
+ * The returned message may still contain padding of 0xDD followed by
+ * zero or more 0x00 octets. It is impossible to remove the padding
+ * without parsing the IEs in the packet (another design decision that
+ * tends to make one question the 802.11i committee's intelligence...)
+ */
+static int ccmp_kie_decrypt ( const void *kek, const void *iv __unused,
+			      void *msg, u16 *len )
+{
+	if ( *len % 8 != 0 )
+		return -EINVAL;
+
+	if ( aes_unwrap ( kek, msg, msg, *len / 8 - 1 ) != 0 )
+		return -EINVAL;
+
+	*len -= 8;
+
+	return 0;
+}
+
+/** CCMP-style key integrity and encryption handler */
+struct wpa_kie ccmp_kie __wpa_kie = {
+	.version = EAPOL_KEY_VERSION_WPA2,
+	.mic = ccmp_kie_mic,
+	.decrypt = ccmp_kie_decrypt,
+};
diff --git a/gpxe/src/net/80211/wpa_psk.c b/gpxe/src/net/80211/wpa_psk.c
new file mode 100644
index 0000000..e752168
--- /dev/null
+++ b/gpxe/src/net/80211/wpa_psk.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/net80211.h>
+#include <gpxe/sha1.h>
+#include <gpxe/wpa.h>
+#include <errno.h>
+
+/** @file
+ *
+ * Frontend for WPA using a pre-shared key.
+ */
+
+/**
+ * Initialise WPA-PSK state
+ *
+ * @v dev	802.11 device
+ * @ret rc	Return status code
+ */
+static int wpa_psk_init ( struct net80211_device *dev )
+{
+	return wpa_make_rsn_ie ( dev, &dev->rsn_ie );
+}
+
+/**
+ * Start WPA-PSK authentication
+ *
+ * @v dev	802.11 device
+ * @ret rc	Return status code
+ */
+static int wpa_psk_start ( struct net80211_device *dev )
+{
+	char passphrase[64+1];
+	u8 pmk[WPA_PMK_LEN];
+	int len;
+	struct wpa_common_ctx *ctx = dev->handshaker->priv;
+
+	len = fetch_string_setting ( netdev_settings ( dev->netdev ),
+				     &net80211_key_setting, passphrase,
+				     64 + 1 );
+
+	if ( len <= 0 ) {
+		DBGC ( ctx, "WPA-PSK %p: no passphrase provided!\n", ctx );
+		net80211_deauthenticate ( dev, -EACCES );
+		return -EACCES;
+	}
+
+	pbkdf2_sha1 ( passphrase, len, dev->essid, strlen ( dev->essid ),
+		      4096, pmk, WPA_PMK_LEN );
+
+	DBGC ( ctx, "WPA-PSK %p: derived PMK from passphrase `%s':\n", ctx,
+	       passphrase );
+	DBGC_HD ( ctx, pmk, WPA_PMK_LEN );
+
+	return wpa_start ( dev, ctx, pmk, WPA_PMK_LEN );
+}
+
+/**
+ * Step WPA-PSK authentication
+ *
+ * @v dev	802.11 device
+ * @ret rc	Return status code
+ */
+static int wpa_psk_step ( struct net80211_device *dev )
+{
+	struct wpa_common_ctx *ctx = dev->handshaker->priv;
+
+	switch ( ctx->state ) {
+	case WPA_SUCCESS:
+		return 1;
+	case WPA_FAILURE:
+		return -EACCES;
+	default:
+		return 0;
+	}
+}
+
+/**
+ * Do-nothing function; you can't change a WPA key post-authentication
+ *
+ * @v dev	802.11 device
+ * @ret rc	Return status code
+ */
+static int wpa_psk_no_change_key ( struct net80211_device *dev __unused )
+{
+	return 0;
+}
+
+/**
+ * Disable handling of received WPA authentication frames
+ *
+ * @v dev	802.11 device
+ */
+static void wpa_psk_stop ( struct net80211_device *dev )
+{
+	wpa_stop ( dev );
+}
+
+/** WPA-PSK security handshaker */
+struct net80211_handshaker wpa_psk_handshaker __net80211_handshaker = {
+	.protocol = NET80211_SECPROT_PSK,
+	.init = wpa_psk_init,
+	.start = wpa_psk_start,
+	.step = wpa_psk_step,
+	.change_key = wpa_psk_no_change_key,
+	.stop = wpa_psk_stop,
+	.priv_len = sizeof ( struct wpa_common_ctx ),
+};
diff --git a/gpxe/src/net/80211/wpa_tkip.c b/gpxe/src/net/80211/wpa_tkip.c
new file mode 100644
index 0000000..0cb697f
--- /dev/null
+++ b/gpxe/src/net/80211/wpa_tkip.c
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/net80211.h>
+#include <gpxe/crypto.h>
+#include <gpxe/hmac.h>
+#include <gpxe/sha1.h>
+#include <gpxe/md5.h>
+#include <gpxe/crc32.h>
+#include <gpxe/arc4.h>
+#include <gpxe/wpa.h>
+#include <byteswap.h>
+#include <errno.h>
+
+/** @file
+ *
+ * Backend for WPA using the TKIP encryption standard.
+ */
+
+/** Context for one direction of TKIP, either encryption or decryption */
+struct tkip_dir_ctx
+{
+	/** High 32 bits of last sequence counter value used */
+	u32 tsc_hi;
+
+	/** Low 32 bits of last sequence counter value used */
+	u16 tsc_lo;
+
+	/** MAC address used to derive TTAK */
+	u8 mac[ETH_ALEN];
+
+	/** If TRUE, TTAK is valid */
+	u16 ttak_ok;
+
+	/** TKIP-mixed transmit address and key, depends on tsc_hi and MAC */
+	u16 ttak[5];
+};
+
+/** Context for TKIP encryption and decryption */
+struct tkip_ctx
+{
+	/** Temporal key to use */
+	struct tkip_tk tk;
+
+	/** State for encryption */
+	struct tkip_dir_ctx enc;
+
+	/** State for decryption */
+	struct tkip_dir_ctx dec;
+};
+
+/** Header structure at the beginning of TKIP frame data */
+struct tkip_head
+{
+	u8 tsc1;		/**< High byte of low 16 bits of TSC */
+	u8 seed1;		/**< Second byte of WEP seed */
+	u8 tsc0;		/**< Low byte of TSC */
+	u8 kid;			/**< Key ID and ExtIV byte */
+	u32 tsc_hi;		/**< High 32 bits of TSC, as an ExtIV */
+} __attribute__ (( packed ));
+
+
+/** TKIP header overhead (IV + KID + ExtIV) */
+#define TKIP_HEAD_LEN	8
+
+/** TKIP trailer overhead (MIC + ICV) [assumes unfragmented] */
+#define TKIP_FOOT_LEN	12
+
+/** TKIP MIC length */
+#define TKIP_MIC_LEN	8
+
+/** TKIP ICV length */
+#define TKIP_ICV_LEN	4
+
+
+/** TKIP S-box */
+static const u16 Sbox[256] = {
+	0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+	0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+	0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+	0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+	0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+	0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+	0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+	0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+	0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+	0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+	0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+	0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+	0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+	0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+	0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+	0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+	0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+	0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+	0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+	0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+	0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+	0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+	0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+	0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+	0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+	0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+	0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+	0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+	0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+	0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+	0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+	0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+/**
+ * Perform S-box mapping on a 16-bit value
+ *
+ * @v v		Value to perform S-box mapping on
+ * @ret Sv	S-box mapped value
+ */
+static inline u16 S ( u16 v )
+{
+	return Sbox[v & 0xFF] ^ swap16 ( Sbox[v >> 8] );
+}
+
+/**
+ * Rotate 16-bit value right
+ *
+ * @v v		Value to rotate
+ * @v bits	Number of bits to rotate by
+ * @ret rotv	Rotated value
+ */
+static inline u16 ror16 ( u16 v, int bits )
+{
+	return ( v >> bits ) | ( v << ( 16 - bits ) );
+}
+
+/**
+ * Rotate 32-bit value right
+ *
+ * @v v		Value to rotate
+ * @v bits	Number of bits to rotate by
+ * @ret rotv	Rotated value
+ */
+static inline u32 ror32 ( u32 v, int bits )
+{
+	return ( v >> bits ) | ( v << ( 32 - bits ) );
+}
+
+/**
+ * Rotate 32-bit value left
+ *
+ * @v v		Value to rotate
+ * @v bits	Number of bits to rotate by
+ * @ret rotv	Rotated value
+ */
+static inline u32 rol32 ( u32 v, int bits )
+{
+	return ( v << bits ) | ( v >> ( 32 - bits ) );
+}
+
+
+/**
+ * Initialise TKIP state and install key
+ *
+ * @v crypto	TKIP cryptosystem structure
+ * @v key	Pointer to tkip_tk to install
+ * @v keylen	Length of key (32 bytes)
+ * @v rsc	Initial receive sequence counter
+ */
+static int tkip_init ( struct net80211_crypto *crypto, const void *key,
+		       int keylen, const void *rsc )
+{
+	struct tkip_ctx *ctx = crypto->priv;
+	const u8 *rscb = rsc;
+
+	if ( keylen != sizeof ( ctx->tk ) )
+		return -EINVAL;
+
+	if ( rscb ) {
+		ctx->dec.tsc_lo =   ( rscb[1] <<  8 ) |   rscb[0];
+		ctx->dec.tsc_hi = ( ( rscb[5] << 24 ) | ( rscb[4] << 16 ) |
+				    ( rscb[3] <<  8 ) |   rscb[2] );
+	}
+
+	memcpy ( &ctx->tk, key, sizeof ( ctx->tk ) );
+
+	return 0;
+}
+
+/**
+ * Perform TKIP key mixing, phase 1
+ *
+ * @v dctx	TKIP directional context
+ * @v tk	TKIP temporal key
+ * @v mac	MAC address of transmitter
+ *
+ * This recomputes the TTAK in @a dctx if necessary, and sets
+ * @c dctx->ttak_ok.
+ */
+static void tkip_mix_1 ( struct tkip_dir_ctx *dctx, struct tkip_tk *tk, u8 *mac )
+{
+	int i, j;
+
+	if ( dctx->ttak_ok && ! memcmp ( mac, dctx->mac, ETH_ALEN ) )
+		return;
+
+	memcpy ( dctx->mac, mac, ETH_ALEN );
+
+	dctx->ttak[0] = dctx->tsc_hi & 0xFFFF;
+	dctx->ttak[1] = dctx->tsc_hi >> 16;
+	dctx->ttak[2] = ( mac[1] << 8 ) | mac[0];
+	dctx->ttak[3] = ( mac[3] << 8 ) | mac[2];
+	dctx->ttak[4] = ( mac[5] << 8 ) | mac[4];
+
+	for ( i = 0; i < 8; i++ ) {
+		j = 2 * ( i & 1 );
+
+		dctx->ttak[0] += S ( dctx->ttak[4] ^ ( ( tk->key[1 + j] << 8 ) |
+						         tk->key[0 + j] ) );
+		dctx->ttak[1] += S ( dctx->ttak[0] ^ ( ( tk->key[5 + j] << 8 ) |
+						         tk->key[4 + j] ) );
+		dctx->ttak[2] += S ( dctx->ttak[1] ^ ( ( tk->key[9 + j] << 8 ) |
+						         tk->key[8 + j] ) );
+		dctx->ttak[3] += S ( dctx->ttak[2] ^ ( ( tk->key[13+ j] << 8 ) |
+						         tk->key[12+ j] ) );
+		dctx->ttak[4] += S ( dctx->ttak[3] ^ ( ( tk->key[1 + j] << 8 ) |
+						         tk->key[0 + j] ) ) + i;
+	}
+
+	dctx->ttak_ok = 1;
+}
+
+/**
+ * Perform TKIP key mixing, phase 2
+ *
+ * @v dctx	TKIP directional context
+ * @v tk	TKIP temporal key
+ * @ret key	ARC4 key, 16 bytes long
+ */
+static void tkip_mix_2 ( struct tkip_dir_ctx *dctx, struct tkip_tk *tk,
+			 void *key )
+{
+	u8 *kb = key;
+	u16 ppk[6];
+	int i;
+
+	memcpy ( ppk, dctx->ttak, sizeof ( dctx->ttak ) );
+	ppk[5] = dctx->ttak[4] + dctx->tsc_lo;
+
+	ppk[0] += S ( ppk[5] ^ ( ( tk->key[1] << 8 ) | tk->key[0] ) );
+	ppk[1] += S ( ppk[0] ^ ( ( tk->key[3] << 8 ) | tk->key[2] ) );
+	ppk[2] += S ( ppk[1] ^ ( ( tk->key[5] << 8 ) | tk->key[4] ) );
+	ppk[3] += S ( ppk[2] ^ ( ( tk->key[7] << 8 ) | tk->key[6] ) );
+	ppk[4] += S ( ppk[3] ^ ( ( tk->key[9] << 8 ) | tk->key[8] ) );
+	ppk[5] += S ( ppk[4] ^ ( ( tk->key[11] << 8 ) | tk->key[10] ) );
+
+	ppk[0] += ror16 ( ppk[5] ^ ( ( tk->key[13] << 8 ) | tk->key[12] ), 1 );
+	ppk[1] += ror16 ( ppk[0] ^ ( ( tk->key[15] << 8 ) | tk->key[14] ), 1 );
+	ppk[2] += ror16 ( ppk[1], 1 );
+	ppk[3] += ror16 ( ppk[2], 1 );
+	ppk[4] += ror16 ( ppk[3], 1 );
+	ppk[5] += ror16 ( ppk[4], 1 );
+
+	kb[0] = dctx->tsc_lo >> 8;
+	kb[1] = ( ( dctx->tsc_lo >> 8 ) | 0x20 ) & 0x7F;
+	kb[2] = dctx->tsc_lo & 0xFF;
+	kb[3] = ( ( ppk[5] ^ ( ( tk->key[1] << 8 ) | tk->key[0] ) ) >> 1 )
+		& 0xFF;
+
+	for ( i = 0; i < 6; i++ ) {
+		kb[4 + 2*i] = ppk[i] & 0xFF;
+		kb[5 + 2*i] = ppk[i] >> 8;
+	}
+}
+
+/**
+ * Update Michael message integrity code based on next 32-bit word of data
+ *
+ * @v V		Michael code state (two 32-bit words)
+ * @v word	Next 32-bit word of data
+ */
+static void tkip_feed_michael ( u32 *V, u32 word )
+{
+	V[0] ^= word;
+	V[1] ^= rol32 ( V[0], 17 );
+	V[0] += V[1];
+	V[1] ^= ( ( V[0] & 0xFF00FF00 ) >> 8 ) | ( ( V[0] & 0x00FF00FF ) << 8 );
+	V[0] += V[1];
+	V[1] ^= rol32 ( V[0], 3 );
+	V[0] += V[1];
+	V[1] ^= ror32 ( V[0], 2 );
+	V[0] += V[1];
+}
+
+/**
+ * Calculate Michael message integrity code
+ *
+ * @v key	MIC key to use (8 bytes)
+ * @v da	Destination link-layer address
+ * @v sa	Source link-layer address
+ * @v data	Start of data to calculate over
+ * @v len	Length of header + data
+ * @ret mic	Calculated Michael MIC (8 bytes)
+ */
+static void tkip_michael ( const void *key, const void *da, const void *sa,
+			   const void *data, size_t len, void *mic )
+{
+	u32 V[2];		/* V[0] = "l", V[1] = "r" in 802.11 */
+	union {
+		u8 byte[12];
+		u32 word[3];
+	} cap;
+	const u8 *ptr = data;
+	const u8 *end = ptr + len;
+	int i;
+
+	memcpy ( V, key, sizeof ( V ) );
+	V[0] = le32_to_cpu ( V[0] );
+	V[1] = le32_to_cpu ( V[1] );
+
+	/* Feed in header (we assume non-QoS, so Priority = 0) */
+	memcpy ( &cap.byte[0], da, ETH_ALEN );
+	memcpy ( &cap.byte[6], sa, ETH_ALEN );
+	tkip_feed_michael ( V, le32_to_cpu ( cap.word[0] ) );
+	tkip_feed_michael ( V, le32_to_cpu ( cap.word[1] ) );
+	tkip_feed_michael ( V, le32_to_cpu ( cap.word[2] ) );
+	tkip_feed_michael ( V, 0 );
+
+	/* Feed in data */
+	while ( ptr + 4 <= end ) {
+		tkip_feed_michael ( V, le32_to_cpu ( *( u32 * ) ptr ) );
+		ptr += 4;
+	}
+
+	/* Add unaligned part and padding */
+	for ( i = 0; ptr < end; i++ )
+		cap.byte[i] = *ptr++;
+	cap.byte[i++] = 0x5a;
+	for ( ; i < 8; i++ )
+		cap.byte[i] = 0;
+
+	/* Feed in padding */
+	tkip_feed_michael ( V, le32_to_cpu ( cap.word[0] ) );
+	tkip_feed_michael ( V, le32_to_cpu ( cap.word[1] ) );
+
+	/* Output MIC */
+	V[0] = cpu_to_le32 ( V[0] );
+	V[1] = cpu_to_le32 ( V[1] );
+	memcpy ( mic, V, sizeof ( V ) );
+}
+
+/**
+ * Encrypt a packet using TKIP
+ *
+ * @v crypto	TKIP cryptosystem
+ * @v iob	I/O buffer containing cleartext packet
+ * @ret eiob	I/O buffer containing encrypted packet
+ */
+static struct io_buffer * tkip_encrypt ( struct net80211_crypto *crypto,
+					 struct io_buffer *iob )
+{
+	struct tkip_ctx *ctx = crypto->priv;
+	struct ieee80211_frame *hdr = iob->data;
+	struct io_buffer *eiob;
+	struct arc4_ctx arc4;
+	u8 key[16];
+	struct tkip_head head;
+	u8 mic[8];
+	u32 icv;
+	const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
+	int datalen = iob_len ( iob ) - hdrlen;
+
+	ctx->enc.tsc_lo++;
+	if ( ctx->enc.tsc_lo == 0 ) {
+		ctx->enc.tsc_hi++;
+		ctx->enc.ttak_ok = 0;
+	}
+
+	tkip_mix_1 ( &ctx->enc, &ctx->tk, hdr->addr2 );
+	tkip_mix_2 ( &ctx->enc, &ctx->tk, key );
+
+	eiob = alloc_iob ( iob_len ( iob ) + TKIP_HEAD_LEN + TKIP_FOOT_LEN );
+	if ( ! eiob )
+		return NULL;
+
+	/* Copy frame header */
+	memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen );
+	hdr = eiob->data;
+	hdr->fc |= IEEE80211_FC_PROTECTED;
+
+	/* Fill in IV and key ID byte, and extended IV */
+	memcpy ( &head, key, 3 );
+	head.kid = 0x20;		/* have Extended IV, key ID 0 */
+	head.tsc_hi = cpu_to_le32 ( ctx->enc.tsc_hi );
+	memcpy ( iob_put ( eiob, sizeof ( head ) ), &head, sizeof ( head ) );
+
+	/* Copy and encrypt the data */
+	cipher_setkey ( &arc4_algorithm, &arc4, key, 16 );
+	cipher_encrypt ( &arc4_algorithm, &arc4, iob->data + hdrlen,
+			 iob_put ( eiob, datalen ), datalen );
+
+	/* Add MIC */
+	hdr = iob->data;
+	tkip_michael ( &ctx->tk.mic.tx, hdr->addr3, hdr->addr2,
+		       iob->data + hdrlen, datalen, mic );
+	cipher_encrypt ( &arc4_algorithm, &arc4, mic,
+			 iob_put ( eiob, sizeof ( mic ) ), sizeof ( mic ) );
+
+	/* Add ICV */
+	icv = crc32_le ( ~0, iob->data + hdrlen, datalen );
+	icv = crc32_le ( icv, mic, sizeof ( mic ) );
+	icv = cpu_to_le32 ( ~icv );
+	cipher_encrypt ( &arc4_algorithm, &arc4, &icv,
+			 iob_put ( eiob, TKIP_ICV_LEN ), TKIP_ICV_LEN );
+
+	DBGC2 ( ctx, "WPA-TKIP %p: encrypted packet %p -> %p\n", ctx,
+		iob, eiob );
+
+	return eiob;
+}
+
+/**
+ * Decrypt a packet using TKIP
+ *
+ * @v crypto	TKIP cryptosystem
+ * @v eiob	I/O buffer containing encrypted packet
+ * @ret iob	I/O buffer containing cleartext packet
+ */
+static struct io_buffer * tkip_decrypt ( struct net80211_crypto *crypto,
+					 struct io_buffer *eiob )
+{
+	struct tkip_ctx *ctx = crypto->priv;
+	struct ieee80211_frame *hdr;
+	struct io_buffer *iob;
+	const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
+	int datalen = iob_len ( eiob ) - hdrlen - TKIP_HEAD_LEN - TKIP_FOOT_LEN;
+	struct tkip_head *head;
+	struct arc4_ctx arc4;
+	u16 rx_tsc_lo;
+	u8 key[16];
+	u8 mic[8];
+	u32 icv, crc;
+
+	iob = alloc_iob ( hdrlen + datalen + TKIP_FOOT_LEN );
+	if ( ! iob )
+		return NULL;
+
+	/* Copy frame header */
+	memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen );
+	hdr = iob->data;
+	hdr->fc &= ~IEEE80211_FC_PROTECTED;
+
+	/* Check and update TSC */
+	head = eiob->data + hdrlen;
+	rx_tsc_lo = ( head->tsc1 << 8 ) | head->tsc0;
+
+	if ( head->tsc_hi < ctx->dec.tsc_hi ||
+	     ( head->tsc_hi == ctx->dec.tsc_hi &&
+	       rx_tsc_lo <= ctx->dec.tsc_lo ) ) {
+		DBGC ( ctx, "WPA-TKIP %p: packet received out of order "
+		       "(%08x:%04x <= %08x:%04x)\n", ctx, head->tsc_hi,
+		       rx_tsc_lo, ctx->dec.tsc_hi, ctx->dec.tsc_lo );
+		free_iob ( iob );
+		return NULL;
+	}
+	ctx->dec.tsc_lo = rx_tsc_lo;
+	if ( ctx->dec.tsc_hi != head->tsc_hi ) {
+		ctx->dec.ttak_ok = 0;
+		ctx->dec.tsc_hi = head->tsc_hi;
+	}
+
+	/* Calculate key */
+	tkip_mix_1 ( &ctx->dec, &ctx->tk, hdr->addr2 );
+	tkip_mix_2 ( &ctx->dec, &ctx->tk, key );
+
+	/* Copy-decrypt data, MIC, ICV */
+	cipher_setkey ( &arc4_algorithm, &arc4, key, 16 );
+	cipher_decrypt ( &arc4_algorithm, &arc4,
+			 eiob->data + hdrlen + TKIP_HEAD_LEN,
+			 iob_put ( iob, datalen ), datalen + TKIP_FOOT_LEN );
+
+	/* Check ICV */
+	icv = le32_to_cpu ( *( u32 * ) ( iob->tail + TKIP_MIC_LEN ) );
+	crc = ~crc32_le ( ~0, iob->data + hdrlen, datalen + TKIP_MIC_LEN );
+	if ( crc != icv ) {
+		DBGC ( ctx, "WPA-TKIP %p CRC mismatch: expect %08x, get %08x\n",
+		       ctx, icv, crc );
+		free_iob ( iob );
+		return NULL;
+	}
+
+	/* Check MIC */
+	tkip_michael ( &ctx->tk.mic.rx, hdr->addr1, hdr->addr3,
+		       iob->data + hdrlen, datalen, mic );
+	if ( memcmp ( mic, iob->tail, TKIP_MIC_LEN ) != 0 ) {
+		DBGC ( ctx, "WPA-TKIP %p ALERT! MIC failure\n", ctx );
+		/* XXX we should do the countermeasures here */
+		free_iob ( iob );
+		return NULL;
+	}
+
+	DBGC2 ( ctx, "WPA-TKIP %p: decrypted packet %p -> %p\n", ctx,
+		eiob, iob );
+
+	return iob;
+}
+
+/** TKIP cryptosystem */
+struct net80211_crypto tkip_crypto __net80211_crypto = {
+	.algorithm = NET80211_CRYPT_TKIP,
+	.init = tkip_init,
+	.encrypt = tkip_encrypt,
+	.decrypt = tkip_decrypt,
+	.priv_len = sizeof ( struct tkip_ctx ),
+};
+
+
+
+
+/**
+ * Calculate HMAC-MD5 MIC for EAPOL-Key frame
+ *
+ * @v kck	Key Confirmation Key, 16 bytes
+ * @v msg	Message to calculate MIC over
+ * @v len	Number of bytes to calculate MIC over
+ * @ret mic	Calculated MIC, 16 bytes long
+ */
+static void tkip_kie_mic ( const void *kck, const void *msg, size_t len,
+			   void *mic )
+{
+	struct md5_ctx md5;
+	u8 kckb[16];
+	size_t kck_len = 16;
+
+	memcpy ( kckb, kck, kck_len );
+
+	hmac_init ( &md5_algorithm, &md5, kckb, &kck_len );
+	hmac_update ( &md5_algorithm, &md5, msg, len );
+	hmac_final ( &md5_algorithm, &md5, kckb, &kck_len, mic );
+}
+
+/**
+ * Decrypt key data in EAPOL-Key frame
+ *
+ * @v kek	Key Encryption Key, 16 bytes
+ * @v iv	Initialisation vector, 16 bytes
+ * @v msg	Message to decrypt
+ * @v len	Length of message
+ * @ret msg	Decrypted message in place of original
+ * @ret len	Unchanged
+ * @ret rc	Always 0 for success
+ */
+static int tkip_kie_decrypt ( const void *kek, const void *iv,
+			      void *msg, u16 *len )
+{
+	u8 key[32];
+	memcpy ( key, iv, 16 );
+	memcpy ( key + 16, kek, 16 );
+
+	arc4_skip ( key, 32, 256, msg, msg, *len );
+
+	return 0;
+}
+
+
+/** TKIP-style key integrity and encryption handler */
+struct wpa_kie tkip_kie __wpa_kie = {
+	.version = EAPOL_KEY_VERSION_WPA,
+	.mic = tkip_kie_mic,
+	.decrypt = tkip_kie_decrypt,
+};
diff --git a/gpxe/src/net/aoe.c b/gpxe/src/net/aoe.c
index 08887fe..839a875 100644
--- a/gpxe/src/net/aoe.c
+++ b/gpxe/src/net/aoe.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <string.h>
 #include <stdio.h>
@@ -66,6 +68,7 @@ static void aoe_done ( struct aoe_session *aoe, int rc ) {
 	/* Record overall command status */
 	if ( aoe->command ) {
 		aoe->command->cb.cmd_stat = aoe->status;
+		aoe->command->rc = rc;
 		aoe->command = NULL;
 	}
 
@@ -354,7 +357,6 @@ static int aoe_command ( struct ata_device *ata,
 			 struct ata_command *command ) {
 	struct aoe_session *aoe =
 		container_of ( ata->backend, struct aoe_session, refcnt );
-	int rc;
 
 	aoe->command = command;
 	aoe->status = 0;
@@ -363,15 +365,9 @@ static int aoe_command ( struct ata_device *ata,
 
 	aoe_send_command ( aoe );
 
-	aoe->rc = -EINPROGRESS;
-	while ( aoe->rc == -EINPROGRESS )
-		step();
-	rc = aoe->rc;
-
-	return rc;
+	return 0;
 }
 
-
 /**
  * Issue AoE config query for AoE target discovery
  *
@@ -444,8 +440,7 @@ int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
 		return -ENOMEM;
 	aoe->refcnt.free = aoe_free;
 	aoe->netdev = netdev_get ( netdev );
-	memcpy ( aoe->target, ethernet_protocol.ll_broadcast,
-		 sizeof ( aoe->target ) );
+	memcpy ( aoe->target, netdev->ll_broadcast, sizeof ( aoe->target ) );
 	aoe->tag = AOE_TAG_MAGIC;
 	aoe->timer.expired = aoe_timer_expired;
 
diff --git a/gpxe/src/net/arp.c b/gpxe/src/net/arp.c
index ba9ebf4..124a856 100644
--- a/gpxe/src/net/arp.c
+++ b/gpxe/src/net/arp.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <string.h>
 #include <byteswap.h>
@@ -36,12 +38,6 @@
  *
  */
 
-/** Registered ARP protocols */
-static struct arp_net_protocol arp_net_protocols[0]
-	__table_start ( struct arp_net_protocol, arp_net_protocols );
-static struct arp_net_protocol arp_net_protocols_end[0]
-	__table_end ( struct arp_net_protocol, arp_net_protocols );
-
 /** An ARP cache entry */
 struct arp_entry {
 	/** Network-layer protocol */
@@ -160,7 +156,7 @@ int arp_resolve ( struct net_device *netdev, struct net_protocol *net_protocol,
 
 	/* Transmit ARP request */
 	if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol, 
-			     ll_protocol->ll_broadcast ) ) != 0 )
+			     netdev->ll_broadcast ) ) != 0 )
 		return rc;
 
 	return -ENOENT;
@@ -176,8 +172,7 @@ int arp_resolve ( struct net_device *netdev, struct net_protocol *net_protocol,
 static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) {
 	struct arp_net_protocol *arp_net_protocol;
 
-	for ( arp_net_protocol = arp_net_protocols ;
-	      arp_net_protocol < arp_net_protocols_end ; arp_net_protocol++ ) {
+	for_each_table_entry ( arp_net_protocol, ARP_NET_PROTOCOLS ) {
 		if ( arp_net_protocol->net_protocol->net_proto == net_proto ) {
 			return arp_net_protocol;
 		}
diff --git a/gpxe/src/net/cachedhcp.c b/gpxe/src/net/cachedhcp.c
new file mode 100644
index 0000000..37f344b
--- /dev/null
+++ b/gpxe/src/net/cachedhcp.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/dhcppkt.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/uaccess.h>
+
+/** @file
+ *
+ * Cached DHCP packet handling
+ *
+ */
+
+/**
+ * Store cached DHCPACK packet
+ *
+ * @v data		User pointer to cached DHCP packet data
+ * @v len		Length of cached DHCP packet data
+ * @ret rc		Return status code
+ *
+ * This function should be called by the architecture-specific
+ * get_cached_dhcpack() handler.
+ */
+void store_cached_dhcpack ( userptr_t data, size_t len ) {
+	struct dhcp_packet *dhcppkt;
+	struct dhcphdr *dhcphdr;
+	struct settings *parent;
+	int rc;
+
+	/* Create DHCP packet */
+	dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len );
+	if ( ! dhcppkt )
+		return;
+
+	/* Fill in data for DHCP packet */
+	dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( * dhcppkt ) );
+	copy_from_user ( dhcphdr, data, 0, len );
+	dhcppkt_init ( dhcppkt, dhcphdr, len );
+	DBG_HD ( dhcppkt->options.data, dhcppkt->options.len );
+
+	/* Register settings on the last opened network device.
+	 * This will have the effect of registering cached settings
+	 * with a network device when "dhcp netX" is performed for that
+	 * device, which is usually what we want.
+	 */
+	parent = netdev_settings ( last_opened_netdev() );
+	if ( ( rc = register_settings ( &dhcppkt->settings, parent ) ) != 0 )
+		DBG ( "DHCP could not register cached settings: %s\n",
+		      strerror ( rc ) );
+
+	dhcppkt_put ( dhcppkt );
+
+	DBG ( "DHCP registered cached settings\n" );
+}
diff --git a/gpxe/src/net/dhcpopts.c b/gpxe/src/net/dhcpopts.c
index 1898011..6482c62 100644
--- a/gpxe/src/net/dhcpopts.c
+++ b/gpxe/src/net/dhcpopts.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -101,7 +103,7 @@ static unsigned int dhcp_option_len ( struct dhcp_option *option ) {
  * DHCP option block.  Encapsulated options may be searched for by
  * using DHCP_ENCAP_OPT() to construct the tag value.
  *
- * If the option is encapsulated, and @c encapsulator is non-NULL, it
+ * If the option is encapsulated, and @c encap_offset is non-NULL, it
  * will be filled in with the offset of the encapsulating option.
  *
  * This routine is designed to be paranoid.  It does not assume that
@@ -134,8 +136,15 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options,
 		if ( remaining < 0 )
 			break;
 		/* Check for explicit end marker */
-		if ( option->tag == DHCP_END )
-			break;
+		if ( option->tag == DHCP_END ) {
+			if ( tag == DHCP_END )
+				/* Special case where the caller is interested
+				 * in whether we have this marker or not.
+				 */
+				return offset;
+			else
+				break;
+		}
 		/* Check for matching tag */
 		if ( option->tag == tag ) {
 			DBGC ( options, "DHCPOPT %p found %s (length %d)\n",
@@ -254,7 +263,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
 	static const uint8_t empty_encapsulator[] = { DHCP_END };
 	int offset;
 	int encap_offset = -1;
-	int creation_offset = 0;
+	int creation_offset;
 	struct dhcp_option *option;
 	unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag );
 	size_t old_len = 0;
@@ -265,6 +274,10 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
 	if ( tag == DHCP_PAD )
 		return -ENOTTY;
 
+	creation_offset = find_dhcp_option_with_encap ( options, DHCP_END,
+							NULL );
+	if ( creation_offset < 0 )
+		creation_offset = options->len;
 	/* Find old instance of this option, if any */
 	offset = find_dhcp_option_with_encap ( options, tag, &encap_offset );
 	if ( offset >= 0 ) {
diff --git a/gpxe/src/net/dhcppkt.c b/gpxe/src/net/dhcppkt.c
index 1f2d373..20a0e66 100644
--- a/gpxe/src/net/dhcppkt.c
+++ b/gpxe/src/net/dhcppkt.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -155,6 +157,8 @@ int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
 		memset ( field_data, 0, field->len );
 		memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
 			 data, len );
+		/* Erase any equivalent option from the options block */
+		dhcpopt_store ( &dhcppkt->options, tag, NULL, 0 );
 		return 0;
 	}
 
@@ -181,14 +185,16 @@ int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
 		    void *data, size_t len ) {
 	struct dhcp_packet_field *field;
 	void *field_data;
-	size_t field_len;
+	size_t field_len = 0;
 	
-	/* If this is a special field, return it */
+	/* Identify special field, if any */
 	if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
 		field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field );
 		field_len = field->used_len ( field_data, field->len );
-		if ( ! field_len )
-			return -ENOENT;
+	}
+
+	/* Return special field, if it exists and is populated */
+	if ( field_len ) {
 		if ( len > field_len )
 			len = field_len;
 		memcpy ( data, field_data, len );
diff --git a/gpxe/src/net/eapol.c b/gpxe/src/net/eapol.c
new file mode 100644
index 0000000..507c8ce
--- /dev/null
+++ b/gpxe/src/net/eapol.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** @file
+ *
+ * 802.1X Extensible Authentication Protocol over LANs demultiplexer
+ *
+ */
+
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/eapol.h>
+#include <errno.h>
+#include <byteswap.h>
+
+/**
+ * Receive EAPOL network-layer packet
+ *
+ * @v iob	I/O buffer
+ * @v netdev	Network device
+ * @v ll_source	Link-layer source address
+ *
+ * This function takes ownership of the I/O buffer passed to it.
+ */
+static int eapol_rx ( struct io_buffer *iob, struct net_device *netdev,
+		      const void *ll_source )
+{
+	struct eapol_frame *eapol = iob->data;
+	struct eapol_handler *handler;
+
+	if ( iob_len ( iob ) < EAPOL_HDR_LEN ) {
+		free_iob ( iob );
+		return -EINVAL;
+	}
+
+	for_each_table_entry ( handler, EAPOL_HANDLERS ) {
+		if ( handler->type == eapol->type ) {
+			iob_pull ( iob, EAPOL_HDR_LEN );
+			return handler->rx ( iob, netdev, ll_source );
+		}
+	}
+
+	free_iob ( iob );
+	return -( ENOTSUP | ( ( eapol->type & 0x1f ) << 8 ) );
+}
+
+/**
+ * Transcribe EAPOL network-layer address
+ *
+ * @v net_addr	Network-layer address
+ * @ret str	String representation of network-layer address
+ *
+ * EAPOL doesn't have network-layer addresses, so we just return the
+ * string @c "<EAPOL>".
+ */
+static const char * eapol_ntoa ( const void *net_addr __unused )
+{
+	return "<EAPOL>";
+}
+
+/** EAPOL network protocol */
+struct net_protocol eapol_protocol __net_protocol = {
+	.name = "EAPOL",
+	.rx = eapol_rx,
+	.ntoa = eapol_ntoa,
+	.net_proto = htons ( ETH_P_EAPOL ),
+};
diff --git a/gpxe/src/net/ethernet.c b/gpxe/src/net/ethernet.c
index b16135a..79ed1dc 100644
--- a/gpxe/src/net/ethernet.c
+++ b/gpxe/src/net/ethernet.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -41,13 +43,15 @@ static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
 /**
  * Add Ethernet link-layer header
  *
+ * @v netdev		Network device
  * @v iobuf		I/O buffer
  * @v ll_dest		Link-layer destination address
  * @v ll_source		Source link-layer address
  * @v net_proto		Network-layer protocol, in network-byte order
  * @ret rc		Return status code
  */
-static int eth_push ( struct io_buffer *iobuf, const void *ll_dest,
+static int eth_push ( struct net_device *netdev __unused,
+		      struct io_buffer *iobuf, const void *ll_dest,
 		      const void *ll_source, uint16_t net_proto ) {
 	struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) );
 
@@ -62,13 +66,15 @@ static int eth_push ( struct io_buffer *iobuf, const void *ll_dest,
 /**
  * Remove Ethernet link-layer header
  *
+ * @v netdev		Network device
  * @v iobuf		I/O buffer
  * @ret ll_dest		Link-layer destination address
  * @ret ll_source	Source link-layer address
  * @ret net_proto	Network-layer protocol, in network-byte order
  * @ret rc		Return status code
  */
-static int eth_pull ( struct io_buffer *iobuf, const void **ll_dest,
+static int eth_pull ( struct net_device *netdev __unused, 
+		      struct io_buffer *iobuf, const void **ll_dest,
 		      const void **ll_source, uint16_t *net_proto ) {
 	struct ethhdr *ethhdr = iobuf->data;
 
@@ -91,6 +97,16 @@ static int eth_pull ( struct io_buffer *iobuf, const void **ll_dest,
 }
 
 /**
+ * Initialise Ethernet address
+ *
+ * @v hw_addr		Hardware address
+ * @v ll_addr		Link-layer address
+ */
+void eth_init_addr ( const void *hw_addr, void *ll_addr ) {
+	memcpy ( ll_addr, hw_addr, ETH_ALEN );
+}
+
+/**
  * Transcribe Ethernet address
  *
  * @v ll_addr		Link-layer address
@@ -114,8 +130,7 @@ const char * eth_ntoa ( const void *ll_addr ) {
  * @v ll_addr		Link-layer address to fill in
  * @ret rc		Return status code
  */
-static int eth_mc_hash ( unsigned int af, const void *net_addr,
-			 void *ll_addr ) {
+int eth_mc_hash ( unsigned int af, const void *net_addr, void *ll_addr ) {
 	const uint8_t *net_addr_bytes = net_addr;
 	uint8_t *ll_addr_bytes = ll_addr;
 
@@ -133,15 +148,46 @@ static int eth_mc_hash ( unsigned int af, const void *net_addr,
 	}
 }
 
+/**
+ * Generate Ethernet-compatible compressed link-layer address
+ *
+ * @v ll_addr		Link-layer address
+ * @v eth_addr		Ethernet-compatible address to fill in
+ */
+int eth_eth_addr ( const void *ll_addr, void *eth_addr ) {
+	memcpy ( eth_addr, ll_addr, ETH_ALEN );
+	return 0;
+}
+
 /** Ethernet protocol */
 struct ll_protocol ethernet_protocol __ll_protocol = {
 	.name		= "Ethernet",
 	.ll_proto	= htons ( ARPHRD_ETHER ),
+	.hw_addr_len	= ETH_ALEN,
 	.ll_addr_len	= ETH_ALEN,
 	.ll_header_len	= ETH_HLEN,
-	.ll_broadcast	= eth_broadcast,
 	.push		= eth_push,
 	.pull		= eth_pull,
+	.init_addr	= eth_init_addr,
 	.ntoa		= eth_ntoa,
 	.mc_hash	= eth_mc_hash,
+	.eth_addr	= eth_eth_addr,
 };
+
+/**
+ * Allocate Ethernet device
+ *
+ * @v priv_size		Size of driver private data
+ * @ret netdev		Network device, or NULL
+ */
+struct net_device * alloc_etherdev ( size_t priv_size ) {
+	struct net_device *netdev;
+
+	netdev = alloc_netdev ( priv_size );
+	if ( netdev ) {
+		netdev->ll_protocol = &ethernet_protocol;
+		netdev->ll_broadcast = eth_broadcast;
+		netdev->max_pkt_len = ETH_FRAME_LEN;
+	}
+	return netdev;
+}
diff --git a/gpxe/src/net/fakedhcp.c b/gpxe/src/net/fakedhcp.c
index 0518789..ad3f046 100644
--- a/gpxe/src/net/fakedhcp.c
+++ b/gpxe/src/net/fakedhcp.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
diff --git a/gpxe/src/net/icmp.c b/gpxe/src/net/icmp.c
index 3e45c1f..749c345 100644
--- a/gpxe/src/net/icmp.c
+++ b/gpxe/src/net/icmp.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <errno.h>
 #include <gpxe/iobuf.h>
diff --git a/gpxe/src/net/infiniband.c b/gpxe/src/net/infiniband.c
index d79bdc2..d781324 100644
--- a/gpxe/src/net/infiniband.c
+++ b/gpxe/src/net/infiniband.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -25,12 +27,15 @@
 #include <errno.h>
 #include <assert.h>
 #include <gpxe/list.h>
+#include <gpxe/errortab.h>
 #include <gpxe/if_arp.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/iobuf.h>
 #include <gpxe/ipoib.h>
 #include <gpxe/process.h>
 #include <gpxe/infiniband.h>
+#include <gpxe/ib_mi.h>
+#include <gpxe/ib_sma.h>
 
 /** @file
  *
@@ -41,6 +46,26 @@
 /** List of Infiniband devices */
 struct list_head ib_devices = LIST_HEAD_INIT ( ib_devices );
 
+/** List of open Infiniband devices, in reverse order of opening */
+static struct list_head open_ib_devices = LIST_HEAD_INIT ( open_ib_devices );
+
+/* Disambiguate the various possible EINPROGRESSes */
+#define EINPROGRESS_INIT ( EINPROGRESS | EUNIQ_01 )
+#define EINPROGRESS_ARMED ( EINPROGRESS | EUNIQ_02 )
+
+/** Human-readable message for the link statuses */
+struct errortab infiniband_errors[] __errortab = {
+	{ EINPROGRESS_INIT, "Initialising" },
+	{ EINPROGRESS_ARMED, "Armed" },
+};
+
+/***************************************************************************
+ *
+ * Completion queues
+ *
+ ***************************************************************************
+ */
+
 /**
  * Create completion queue
  *
@@ -61,6 +86,8 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes,
 	cq = zalloc ( sizeof ( *cq ) );
 	if ( ! cq )
 		goto err_alloc_cq;
+	cq->ibdev = ibdev;
+	list_add ( &cq->list, &ibdev->cqs );
 	cq->num_cqes = num_cqes;
 	INIT_LIST_HEAD ( &cq->work_queues );
 	cq->op = op;
@@ -79,6 +106,7 @@ ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes,
 
 	ibdev->op->destroy_cq ( ibdev, cq );
  err_dev_create_cq:
+	list_del ( &cq->list );
 	free ( cq );
  err_alloc_cq:
 	return NULL;
@@ -96,26 +124,57 @@ void ib_destroy_cq ( struct ib_device *ibdev,
 	       ibdev, cq->cqn );
 	assert ( list_empty ( &cq->work_queues ) );
 	ibdev->op->destroy_cq ( ibdev, cq );
+	list_del ( &cq->list );
 	free ( cq );
 }
 
 /**
+ * Poll completion queue
+ *
+ * @v ibdev		Infiniband device
+ * @v cq		Completion queue
+ */
+void ib_poll_cq ( struct ib_device *ibdev,
+		  struct ib_completion_queue *cq ) {
+	struct ib_work_queue *wq;
+
+	/* Poll completion queue */
+	ibdev->op->poll_cq ( ibdev, cq );
+
+	/* Refill receive work queues */
+	list_for_each_entry ( wq, &cq->work_queues, list ) {
+		if ( ! wq->is_send )
+			ib_refill_recv ( ibdev, wq->qp );
+	}
+}
+
+/***************************************************************************
+ *
+ * Work queues
+ *
+ ***************************************************************************
+ */
+
+/**
  * Create queue pair
  *
  * @v ibdev		Infiniband device
+ * @v type		Queue pair type
  * @v num_send_wqes	Number of send work queue entries
  * @v send_cq		Send completion queue
  * @v num_recv_wqes	Number of receive work queue entries
  * @v recv_cq		Receive completion queue
- * @v qkey		Queue key
  * @ret qp		Queue pair
+ *
+ * The queue pair will be left in the INIT state; you must call
+ * ib_modify_qp() before it is ready to use for sending and receiving.
  */
 struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
+				      enum ib_queue_pair_type type,
 				      unsigned int num_send_wqes,
 				      struct ib_completion_queue *send_cq,
 				      unsigned int num_recv_wqes,
-				      struct ib_completion_queue *recv_cq,
-				      unsigned long qkey ) {
+				      struct ib_completion_queue *recv_cq ) {
 	struct ib_queue_pair *qp;
 	size_t total_size;
 	int rc;
@@ -131,16 +190,18 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
 		goto err_alloc_qp;
 	qp->ibdev = ibdev;
 	list_add ( &qp->list, &ibdev->qps );
-	qp->qkey = qkey;
+	qp->type = type;
 	qp->send.qp = qp;
 	qp->send.is_send = 1;
 	qp->send.cq = send_cq;
 	list_add ( &qp->send.list, &send_cq->work_queues );
+	qp->send.psn = ( random() & 0xffffffUL );
 	qp->send.num_wqes = num_send_wqes;
 	qp->send.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) );
 	qp->recv.qp = qp;
 	qp->recv.cq = recv_cq;
 	list_add ( &qp->recv.list, &recv_cq->work_queues );
+	qp->recv.psn = ( random() & 0xffffffUL );
 	qp->recv.num_wqes = num_recv_wqes;
 	qp->recv.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) +
 			    ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ));
@@ -152,7 +213,6 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
 		       "%s\n", ibdev, strerror ( rc ) );
 		goto err_dev_create_qp;
 	}
-
 	DBGC ( ibdev, "IBDEV %p created queue pair %p (%p) with QPN %#lx\n",
 	       ibdev, qp, ib_qp_get_drvdata ( qp ), qp->qpn );
 	DBGC ( ibdev, "IBDEV %p QPN %#lx has %d send entries at [%p,%p)\n",
@@ -161,6 +221,24 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
 	DBGC ( ibdev, "IBDEV %p QPN %#lx has %d receive entries at [%p,%p)\n",
 	       ibdev, qp->qpn, num_recv_wqes, qp->recv.iobufs,
 	       ( ( ( void * ) qp ) + total_size ) );
+
+	/* Calculate externally-visible QPN */
+	switch ( type ) {
+	case IB_QPT_SMI:
+		qp->ext_qpn = IB_QPN_SMI;
+		break;
+	case IB_QPT_GSI:
+		qp->ext_qpn = IB_QPN_GSI;
+		break;
+	default:
+		qp->ext_qpn = qp->qpn;
+		break;
+	}
+	if ( qp->ext_qpn != qp->qpn ) {
+		DBGC ( ibdev, "IBDEV %p QPN %#lx has external QPN %#lx\n",
+		       ibdev, qp->qpn, qp->ext_qpn );
+	}
+
 	return qp;
 
 	ibdev->op->destroy_qp ( ibdev, qp );
@@ -178,20 +256,15 @@ struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
  *
  * @v ibdev		Infiniband device
  * @v qp		Queue pair
- * @v mod_list		Modification list
- * @v qkey		New queue key, if applicable
+ * @v av		New address vector, if applicable
  * @ret rc		Return status code
  */
-int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp,
-		   unsigned long mod_list, unsigned long qkey ) {
+int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) {
 	int rc;
 
 	DBGC ( ibdev, "IBDEV %p modifying QPN %#lx\n", ibdev, qp->qpn );
 
-	if ( mod_list & IB_MODIFY_QKEY )
-		qp->qkey = qkey;
-
-	if ( ( rc = ibdev->op->modify_qp ( ibdev, qp, mod_list ) ) != 0 ) {
+	if ( ( rc = ibdev->op->modify_qp ( ibdev, qp ) ) != 0 ) {
 		DBGC ( ibdev, "IBDEV %p could not modify QPN %#lx: %s\n",
 		       ibdev, qp->qpn, strerror ( rc ) );
 		return rc;
@@ -251,7 +324,7 @@ struct ib_queue_pair * ib_find_qp_qpn ( struct ib_device *ibdev,
 	struct ib_queue_pair *qp;
 
 	list_for_each_entry ( qp, &ibdev->qps, list ) {
-		if ( qp->qpn == qpn )
+		if ( ( qpn == qp->qpn ) || ( qpn == qp->ext_qpn ) )
 			return qp;
 	}
 	return NULL;
@@ -311,6 +384,7 @@ struct ib_work_queue * ib_find_wq ( struct ib_completion_queue *cq,
 int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 		   struct ib_address_vector *av,
 		   struct io_buffer *iobuf ) {
+	struct ib_address_vector av_copy;
 	int rc;
 
 	/* Check queue fill level */
@@ -320,6 +394,20 @@ int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 		return -ENOBUFS;
 	}
 
+	/* Use default address vector if none specified */
+	if ( ! av )
+		av = &qp->av;
+
+	/* Make modifiable copy of address vector */
+	memcpy ( &av_copy, av, sizeof ( av_copy ) );
+	av = &av_copy;
+
+	/* Fill in optional parameters in address vector */
+	if ( ! av->qkey )
+		av->qkey = qp->qkey;
+	if ( ! av->rate )
+		av->rate = IB_RATE_2_5;
+
 	/* Post to hardware */
 	if ( ( rc = ibdev->op->post_send ( ibdev, qp, av, iobuf ) ) != 0 ) {
 		DBGC ( ibdev, "IBDEV %p QPN %#lx could not post send WQE: "
@@ -343,6 +431,13 @@ int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 		   struct io_buffer *iobuf ) {
 	int rc;
 
+	/* Check packet length */
+	if ( iob_tailroom ( iobuf ) < IB_MAX_PAYLOAD_SIZE ) {
+		DBGC ( ibdev, "IBDEV %p QPN %#lx wrong RX buffer size (%zd)\n",
+		       ibdev, qp->qpn, iob_tailroom ( iobuf ) );
+		return -EINVAL;
+	}
+
 	/* Check queue fill level */
 	if ( qp->recv.fill >= qp->recv.num_wqes ) {
 		DBGC ( ibdev, "IBDEV %p QPN %#lx receive queue full\n",
@@ -371,7 +466,12 @@ int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
  */
 void ib_complete_send ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 			struct io_buffer *iobuf, int rc ) {
-	qp->send.cq->op->complete_send ( ibdev, qp, iobuf, rc );
+
+	if ( qp->send.cq->op->complete_send ) {
+		qp->send.cq->op->complete_send ( ibdev, qp, iobuf, rc );
+	} else {
+		free_iob ( iobuf );
+	}
 	qp->send.fill--;
 }
 
@@ -387,11 +487,54 @@ void ib_complete_send ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 void ib_complete_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 			struct ib_address_vector *av,
 			struct io_buffer *iobuf, int rc ) {
-	qp->recv.cq->op->complete_recv ( ibdev, qp, av, iobuf, rc );
+
+	if ( qp->recv.cq->op->complete_recv ) {
+		qp->recv.cq->op->complete_recv ( ibdev, qp, av, iobuf, rc );
+	} else {
+		free_iob ( iobuf );
+	}
 	qp->recv.fill--;
 }
 
 /**
+ * Refill receive work queue
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ */
+void ib_refill_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp ) {
+	struct io_buffer *iobuf;
+	int rc;
+
+	/* Keep filling while unfilled entries remain */
+	while ( qp->recv.fill < qp->recv.num_wqes ) {
+
+		/* Allocate I/O buffer */
+		iobuf = alloc_iob ( IB_MAX_PAYLOAD_SIZE );
+		if ( ! iobuf ) {
+			/* Non-fatal; we will refill on next attempt */
+			return;
+		}
+
+		/* Post I/O buffer */
+		if ( ( rc = ib_post_recv ( ibdev, qp, iobuf ) ) != 0 ) {
+			DBGC ( ibdev, "IBDEV %p could not refill: %s\n",
+			       ibdev, strerror ( rc ) );
+			free_iob ( iobuf );
+			/* Give up */
+			return;
+		}
+	}
+}
+
+/***************************************************************************
+ *
+ * Link control
+ *
+ ***************************************************************************
+ */
+
+/**
  * Open port
  *
  * @v ibdev		Infiniband device
@@ -400,16 +543,59 @@ void ib_complete_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 int ib_open ( struct ib_device *ibdev ) {
 	int rc;
 
-	/* Open device if this is the first requested opening */
-	if ( ibdev->open_count == 0 ) {
-		if ( ( rc = ibdev->op->open ( ibdev ) ) != 0 )
-			return rc;
+	/* Increment device open request counter */
+	if ( ibdev->open_count++ > 0 ) {
+		/* Device was already open; do nothing */
+		return 0;
 	}
 
-	/* Increment device open request counter */
-	ibdev->open_count++;
+	/* Create subnet management interface */
+	ibdev->smi = ib_create_mi ( ibdev, IB_QPT_SMI );
+	if ( ! ibdev->smi ) {
+		DBGC ( ibdev, "IBDEV %p could not create SMI\n", ibdev );
+		rc = -ENOMEM;
+		goto err_create_smi;
+	}
 
+	/* Create subnet management agent */
+	if ( ( rc = ib_create_sma ( ibdev, ibdev->smi ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p could not create SMA: %s\n",
+		       ibdev, strerror ( rc ) );
+		goto err_create_sma;
+	}
+
+	/* Create general services interface */
+	ibdev->gsi = ib_create_mi ( ibdev, IB_QPT_GSI );
+	if ( ! ibdev->gsi ) {
+		DBGC ( ibdev, "IBDEV %p could not create GSI\n", ibdev );
+		rc = -ENOMEM;
+		goto err_create_gsi;
+	}
+
+	/* Open device */
+	if ( ( rc = ibdev->op->open ( ibdev ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p could not open: %s\n",
+		       ibdev, strerror ( rc ) );
+		goto err_open;
+	}
+
+	/* Add to head of open devices list */
+	list_add ( &ibdev->open_list, &open_ib_devices );
+
+	assert ( ibdev->open_count == 1 );
 	return 0;
+
+	ibdev->op->close ( ibdev );
+ err_open:
+	ib_destroy_mi ( ibdev, ibdev->gsi );
+ err_create_gsi:
+	ib_destroy_sma ( ibdev, ibdev->smi );
+ err_create_sma:
+	ib_destroy_mi ( ibdev, ibdev->smi );
+ err_create_smi:
+	assert ( ibdev->open_count == 1 );
+	ibdev->open_count = 0;
+	return rc;
 }
 
 /**
@@ -423,10 +609,38 @@ void ib_close ( struct ib_device *ibdev ) {
 	ibdev->open_count--;
 
 	/* Close device if this was the last remaining requested opening */
-	if ( ibdev->open_count == 0 )
+	if ( ibdev->open_count == 0 ) {
+		list_del ( &ibdev->open_list );
+		ib_destroy_mi ( ibdev, ibdev->gsi );
+		ib_destroy_sma ( ibdev, ibdev->smi );
+		ib_destroy_mi ( ibdev, ibdev->smi );
 		ibdev->op->close ( ibdev );
+	}
+}
+
+/**
+ * Get link state
+ *
+ * @v ibdev		Infiniband device
+ * @ret rc		Link status code
+ */
+int ib_link_rc ( struct ib_device *ibdev ) {
+	switch ( ibdev->port_state ) {
+	case IB_PORT_STATE_DOWN:	return -ENOTCONN;
+	case IB_PORT_STATE_INIT:	return -EINPROGRESS_INIT;
+	case IB_PORT_STATE_ARMED:	return -EINPROGRESS_ARMED;
+	case IB_PORT_STATE_ACTIVE:	return 0;
+	default:			return -EINVAL;
+	}
 }
 
+/***************************************************************************
+ *
+ * Multicast
+ *
+ ***************************************************************************
+ */
+
 /**
  * Attach to multicast group
  *
@@ -434,6 +648,10 @@ void ib_close ( struct ib_device *ibdev ) {
  * @v qp		Queue pair
  * @v gid		Multicast GID
  * @ret rc		Return status code
+ *
+ * Note that this function handles only the local device's attachment
+ * to the multicast GID; it does not issue the relevant MADs to join
+ * the multicast group on the subnet.
  */
 int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 		      struct ib_gid *gid ) {
@@ -486,6 +704,89 @@ void ib_mcast_detach ( struct ib_device *ibdev, struct ib_queue_pair *qp,
 	}
 }
 
+/***************************************************************************
+ *
+ * Miscellaneous
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Get Infiniband HCA information
+ *
+ * @v ibdev		Infiniband device
+ * @ret hca_guid	HCA GUID
+ * @ret num_ports	Number of ports
+ */
+int ib_get_hca_info ( struct ib_device *ibdev,
+		      struct ib_gid_half *hca_guid ) {
+	struct ib_device *tmp;
+	int num_ports = 0;
+
+	/* Search for IB devices with the same physical device to
+	 * identify port count and a suitable Node GUID.
+	 */
+	for_each_ibdev ( tmp ) {
+		if ( tmp->dev != ibdev->dev )
+			continue;
+		if ( num_ports == 0 ) {
+			memcpy ( hca_guid, &tmp->gid.u.half[1],
+				 sizeof ( *hca_guid ) );
+		}
+		num_ports++;
+	}
+	return num_ports;
+}
+
+/**
+ * Set port information
+ *
+ * @v ibdev		Infiniband device
+ * @v mad		Set port information MAD
+ */
+int ib_set_port_info ( struct ib_device *ibdev, union ib_mad *mad ) {
+	int rc;
+
+	/* Adapters with embedded SMAs do not need to support this method */
+	if ( ! ibdev->op->set_port_info ) {
+		DBGC ( ibdev, "IBDEV %p does not support setting port "
+		       "information\n", ibdev );
+		return -ENOTSUP;
+	}
+
+	if ( ( rc = ibdev->op->set_port_info ( ibdev, mad ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p could not set port information: %s\n",
+		       ibdev, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
+};
+
+/**
+ * Set partition key table
+ *
+ * @v ibdev		Infiniband device
+ * @v mad		Set partition key table MAD
+ */
+int ib_set_pkey_table ( struct ib_device *ibdev, union ib_mad *mad ) {
+	int rc;
+
+	/* Adapters with embedded SMAs do not need to support this method */
+	if ( ! ibdev->op->set_pkey_table ) {
+		DBGC ( ibdev, "IBDEV %p does not support setting partition "
+		       "key table\n", ibdev );
+		return -ENOTSUP;
+	}
+
+	if ( ( rc = ibdev->op->set_pkey_table ( ibdev, mad ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p could not set partition key table: "
+		       "%s\n", ibdev, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
+};
 
 /***************************************************************************
  *
@@ -506,6 +807,22 @@ void ib_link_state_changed ( struct ib_device *ibdev ) {
 }
 
 /**
+ * Poll event queue
+ *
+ * @v ibdev		Infiniband device
+ */
+void ib_poll_eq ( struct ib_device *ibdev ) {
+	struct ib_completion_queue *cq;
+
+	/* Poll device's event queue */
+	ibdev->op->poll_eq ( ibdev );
+
+	/* Poll all completion queues */
+	list_for_each_entry ( cq, &ibdev->cqs, list )
+		ib_poll_cq ( ibdev, cq );
+}
+
+/**
  * Single-step the Infiniband event queue
  *
  * @v process		Infiniband event queue process
@@ -513,13 +830,13 @@ void ib_link_state_changed ( struct ib_device *ibdev ) {
 static void ib_step ( struct process *process __unused ) {
 	struct ib_device *ibdev;
 
-	list_for_each_entry ( ibdev, &ib_devices, list ) {
-		ibdev->op->poll_eq ( ibdev );
-	}
+	for_each_ibdev ( ibdev )
+		ib_poll_eq ( ibdev );
 }
 
 /** Infiniband event queue process */
 struct process ib_process __permanent_process = {
+	.list = LIST_HEAD_INIT ( ib_process.list ),
 	.step = ib_step,
 };
 
@@ -546,9 +863,11 @@ struct ib_device * alloc_ibdev ( size_t priv_size ) {
 	if ( ibdev ) {
 		drv_priv = ( ( ( void * ) ibdev ) + sizeof ( *ibdev ) );
 		ib_set_drvdata ( ibdev, drv_priv );
+		INIT_LIST_HEAD ( &ibdev->cqs );
 		INIT_LIST_HEAD ( &ibdev->qps );
+		ibdev->port_state = IB_PORT_STATE_DOWN;
 		ibdev->lid = IB_LID_NONE;
-		ibdev->pkey = IB_PKEY_NONE;
+		ibdev->pkey = IB_PKEY_DEFAULT;
 	}
 	return ibdev;
 }
@@ -598,3 +917,35 @@ void unregister_ibdev ( struct ib_device *ibdev ) {
 	ibdev_put ( ibdev );
 	DBGC ( ibdev, "IBDEV %p unregistered\n", ibdev );
 }
+
+/**
+ * Find Infiniband device by GID
+ *
+ * @v gid		GID
+ * @ret ibdev		Infiniband device, or NULL
+ */
+struct ib_device * find_ibdev ( struct ib_gid *gid ) {
+	struct ib_device *ibdev;
+
+	for_each_ibdev ( ibdev ) {
+		if ( memcmp ( gid, &ibdev->gid, sizeof ( *gid ) ) == 0 )
+			return ibdev;
+	}
+	return NULL;
+}
+
+/**
+ * Get most recently opened Infiniband device
+ *
+ * @ret ibdev		Most recently opened Infiniband device, or NULL
+ */
+struct ib_device * last_opened_ibdev ( void ) {
+	struct ib_device *ibdev;
+
+	list_for_each_entry ( ibdev, &open_ib_devices, open_list ) {
+		assert ( ibdev->open_count != 0 );
+		return ibdev;
+	}
+
+	return NULL;
+}
diff --git a/gpxe/src/net/infiniband/ib_cm.c b/gpxe/src/net/infiniband/ib_cm.c
new file mode 100644
index 0000000..ebe65b3
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_cm.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2009 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ib_mi.h>
+#include <gpxe/ib_pathrec.h>
+#include <gpxe/ib_cm.h>
+
+/**
+ * @file
+ *
+ * Infiniband communication management
+ *
+ */
+
+/** List of connections */
+static LIST_HEAD ( ib_cm_conns );
+
+/**
+ * Send "ready to use" response
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v conn		Connection
+ * @v av		Address vector
+ * @ret rc		Return status code
+ */
+static int ib_cm_send_rtu ( struct ib_device *ibdev,
+			    struct ib_mad_interface *mi,
+			    struct ib_connection *conn,
+			    struct ib_address_vector *av ) {
+	union ib_mad mad;
+	struct ib_cm_ready_to_use *ready =
+		&mad.cm.cm_data.ready_to_use;
+	int rc;
+
+	/* Construct "ready to use" response */
+	memset ( &mad, 0, sizeof ( mad ) );
+	mad.hdr.mgmt_class = IB_MGMT_CLASS_CM;
+	mad.hdr.class_version = IB_CM_CLASS_VERSION;
+	mad.hdr.method = IB_MGMT_METHOD_SEND;
+	mad.hdr.attr_id = htons ( IB_CM_ATTR_READY_TO_USE );
+	ready->local_id = htonl ( conn->local_id );
+	ready->remote_id = htonl ( conn->remote_id );
+	if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){
+		DBGC ( conn, "CM %p could not send RTU: %s\n",
+		       conn, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * Handle duplicate connection replies
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @v av		Source address vector
+ * @ret rc		Return status code
+ *
+ * If a "ready to use" MAD is lost, the peer may resend the connection
+ * reply.  We have to respond to these with duplicate "ready to use"
+ * MADs, otherwise the peer may time out and drop the connection.
+ */
+static void ib_cm_connect_rep ( struct ib_device *ibdev,
+				struct ib_mad_interface *mi,
+				union ib_mad *mad,
+				struct ib_address_vector *av ) {
+	struct ib_cm_connect_reply *connect_rep =
+		&mad->cm.cm_data.connect_reply;
+	struct ib_connection *conn;
+	int rc;
+
+	/* Identify connection */
+	list_for_each_entry ( conn, &ib_cm_conns, list ) {
+		if ( ntohl ( connect_rep->remote_id ) != conn->local_id )
+			continue;
+		/* Try to send "ready to use" reply */
+		if ( ( rc = ib_cm_send_rtu ( ibdev, mi, conn, av ) ) != 0 ) {
+			/* Ignore errors */
+			return;
+		}
+		return;
+	}
+
+	DBG ( "CM unidentified connection %08x\n",
+	      ntohl ( connect_rep->remote_id ) );
+}
+
+/** Communication management agents */
+struct ib_mad_agent ib_cm_agent[] __ib_mad_agent = {
+	{
+		.mgmt_class = IB_MGMT_CLASS_CM,
+		.class_version = IB_CM_CLASS_VERSION,
+		.attr_id = htons ( IB_CM_ATTR_CONNECT_REPLY ),
+		.handle = ib_cm_connect_rep,
+	},
+};
+
+/**
+ * Convert connection rejection reason to return status code
+ *
+ * @v reason		Rejection reason (in network byte order)
+ * @ret rc		Return status code
+ */
+static int ib_cm_rejection_reason_to_rc ( uint16_t reason ) {
+	switch ( reason ) {
+	case htons ( IB_CM_REJECT_BAD_SERVICE_ID ) :
+		return -ENODEV;
+	case htons ( IB_CM_REJECT_STALE_CONN ) :
+		return -EALREADY;
+	case htons ( IB_CM_REJECT_CONSUMER ) :
+		return -ENOTTY;
+	default:
+		return -EPERM;
+	}
+}
+
+/**
+ * Handle connection request transaction completion
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v madx		Management transaction
+ * @v rc		Status code
+ * @v mad		Received MAD (or NULL on error)
+ * @v av		Source address vector (or NULL on error)
+ */
+static void ib_cm_req_complete ( struct ib_device *ibdev,
+				 struct ib_mad_interface *mi,
+				 struct ib_mad_transaction *madx,
+				 int rc, union ib_mad *mad,
+				 struct ib_address_vector *av ) {
+	struct ib_connection *conn = ib_madx_get_ownerdata ( madx );
+	struct ib_queue_pair *qp = conn->qp;
+	struct ib_cm_common *common = &mad->cm.cm_data.common;
+	struct ib_cm_connect_reply *connect_rep =
+		&mad->cm.cm_data.connect_reply;
+	struct ib_cm_connect_reject *connect_rej =
+		&mad->cm.cm_data.connect_reject;
+	void *private_data = NULL;
+	size_t private_data_len = 0;
+
+	/* Report failures */
+	if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ))
+		rc = -EIO;
+	if ( rc != 0 ) {
+		DBGC ( conn, "CM %p connection request failed: %s\n",
+		       conn, strerror ( rc ) );
+		goto out;
+	}
+
+	/* Record remote communication ID */
+	conn->remote_id = ntohl ( common->local_id );
+
+	/* Handle response */
+	switch ( mad->hdr.attr_id ) {
+
+	case htons ( IB_CM_ATTR_CONNECT_REPLY ) :
+		/* Extract fields */
+		qp->av.qpn = ( ntohl ( connect_rep->local_qpn ) >> 8 );
+		qp->send.psn = ( ntohl ( connect_rep->starting_psn ) >> 8 );
+		private_data = &connect_rep->private_data;
+		private_data_len = sizeof ( connect_rep->private_data );
+		DBGC ( conn, "CM %p connected to QPN %lx PSN %x\n",
+		       conn, qp->av.qpn, qp->send.psn );
+
+		/* Modify queue pair */
+		if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) {
+			DBGC ( conn, "CM %p could not modify queue pair: %s\n",
+			       conn, strerror ( rc ) );
+			goto out;
+		}
+
+		/* Send "ready to use" reply */
+		if ( ( rc = ib_cm_send_rtu ( ibdev, mi, conn, av ) ) != 0 ) {
+			/* Treat as non-fatal */
+			rc = 0;
+		}
+		break;
+
+	case htons ( IB_CM_ATTR_CONNECT_REJECT ) :
+		/* Extract fields */
+		DBGC ( conn, "CM %p connection rejected (reason %d)\n",
+		       conn, ntohs ( connect_rej->reason ) );
+		/* Private data is valid only for a Consumer Reject */
+		if ( connect_rej->reason == htons ( IB_CM_REJECT_CONSUMER ) ) {
+			private_data = &connect_rej->private_data;
+			private_data_len = sizeof (connect_rej->private_data);
+		}
+		rc = ib_cm_rejection_reason_to_rc ( connect_rej->reason );
+		break;
+
+	default:
+		DBGC ( conn, "CM %p unexpected response (attribute %04x)\n",
+		       conn, ntohs ( mad->hdr.attr_id ) );
+		rc = -ENOTSUP;
+		break;
+	}
+
+ out:
+	/* Destroy the completed transaction */
+	ib_destroy_madx ( ibdev, ibdev->gsi, madx );
+	conn->madx = NULL;
+
+	/* Hand off to the upper completion handler */
+	conn->op->changed ( ibdev, qp, conn, rc, private_data,
+			    private_data_len );
+}
+
+/** Connection request operations */
+static struct ib_mad_transaction_operations ib_cm_req_op = {
+	.complete = ib_cm_req_complete,
+};
+
+/**
+ * Handle connection path transaction completion
+ *
+ * @v ibdev		Infiniband device
+ * @v path		Path
+ * @v rc		Status code
+ * @v av		Address vector, or NULL on error
+ */
+static void ib_cm_path_complete ( struct ib_device *ibdev,
+				  struct ib_path *path, int rc,
+				  struct ib_address_vector *av ) {
+	struct ib_connection *conn = ib_path_get_ownerdata ( path );
+	struct ib_queue_pair *qp = conn->qp;
+	union ib_mad mad;
+	struct ib_cm_connect_request *connect_req =
+		&mad.cm.cm_data.connect_request;
+	size_t private_data_len;
+
+	/* Report failures */
+	if ( rc != 0 ) {
+		DBGC ( conn, "CM %p path lookup failed: %s\n",
+		       conn, strerror ( rc ) );
+		conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 );
+		goto out;
+	}
+
+	/* Update queue pair peer path */
+	memcpy ( &qp->av, av, sizeof ( qp->av ) );
+
+	/* Construct connection request */
+	memset ( &mad, 0, sizeof ( mad ) );
+	mad.hdr.mgmt_class = IB_MGMT_CLASS_CM;
+	mad.hdr.class_version = IB_CM_CLASS_VERSION;
+	mad.hdr.method = IB_MGMT_METHOD_SEND;
+	mad.hdr.attr_id = htons ( IB_CM_ATTR_CONNECT_REQUEST );
+	connect_req->local_id = htonl ( conn->local_id );
+	memcpy ( &connect_req->service_id, &conn->service_id,
+		 sizeof ( connect_req->service_id ) );
+	ib_get_hca_info ( ibdev, &connect_req->local_ca );
+	connect_req->local_qpn__responder_resources =
+		htonl ( ( qp->qpn << 8 ) | 1 );
+	connect_req->local_eecn__initiator_depth = htonl ( ( 0 << 8 ) | 1 );
+	connect_req->remote_eecn__remote_timeout__service_type__ee_flow_ctrl =
+		htonl ( ( 0x14 << 3 ) | ( IB_CM_TRANSPORT_RC << 1 ) |
+			( 0 << 0 ) );
+	connect_req->starting_psn__local_timeout__retry_count =
+		htonl ( ( qp->recv.psn << 8 ) | ( 0x14 << 3 ) |
+			( 0x07 << 0 ) );
+	connect_req->pkey = htons ( ibdev->pkey );
+	connect_req->payload_mtu__rdc_exists__rnr_retry =
+		( ( IB_MTU_2048 << 4 ) | ( 1 << 3 ) | ( 0x07 << 0 ) );
+	connect_req->max_cm_retries__srq =
+		( ( 0x0f << 4 ) | ( 0 << 3 ) );
+	connect_req->primary.local_lid = htons ( ibdev->lid );
+	connect_req->primary.remote_lid = htons ( conn->qp->av.lid );
+	memcpy ( &connect_req->primary.local_gid, &ibdev->gid,
+		 sizeof ( connect_req->primary.local_gid ) );
+	memcpy ( &connect_req->primary.remote_gid, &conn->qp->av.gid,
+		 sizeof ( connect_req->primary.remote_gid ) );
+	connect_req->primary.flow_label__rate =
+		htonl ( ( 0 << 12 ) | ( conn->qp->av.rate << 0 ) );
+	connect_req->primary.hop_limit = 0;
+	connect_req->primary.sl__subnet_local =
+		( ( conn->qp->av.sl << 4 ) | ( 1 << 3 ) );
+	connect_req->primary.local_ack_timeout = ( 0x13 << 3 );
+	private_data_len = conn->private_data_len;
+	if ( private_data_len > sizeof ( connect_req->private_data ) )
+		private_data_len = sizeof ( connect_req->private_data );
+	memcpy ( &connect_req->private_data, &conn->private_data,
+		 private_data_len );
+
+	/* Create connection request */
+	av->qpn = IB_QPN_GSI;
+	av->qkey = IB_QKEY_GSI;
+	conn->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, av,
+				      &ib_cm_req_op );
+	if ( ! conn->madx ) {
+		DBGC ( conn, "CM %p could not create connection request\n",
+		       conn );
+		conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 );
+		goto out;
+	}
+	ib_madx_set_ownerdata ( conn->madx, conn );
+
+ out:
+	/* Destroy the completed transaction */
+	ib_destroy_path ( ibdev, path );
+	conn->path = NULL;
+}
+
+/** Connection path operations */
+static struct ib_path_operations ib_cm_path_op = {
+	.complete = ib_cm_path_complete,
+};
+
+/**
+ * Create connection to remote QP
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v dgid		Target GID
+ * @v service_id	Target service ID
+ * @v private_data	Connection request private data
+ * @v private_data_len	Length of connection request private data
+ * @v op		Connection operations
+ * @ret conn		Connection
+ */
+struct ib_connection *
+ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+		 struct ib_gid *dgid, struct ib_gid_half *service_id,
+		 void *private_data, size_t private_data_len,
+		 struct ib_connection_operations *op ) {
+	struct ib_connection *conn;
+
+	/* Allocate and initialise request */
+	conn = zalloc ( sizeof ( *conn ) + private_data_len );
+	if ( ! conn )
+		goto err_alloc_conn;
+	conn->ibdev = ibdev;
+	conn->qp = qp;
+	memset ( &qp->av, 0, sizeof ( qp->av ) );
+	qp->av.gid_present = 1;
+	memcpy ( &qp->av.gid, dgid, sizeof ( qp->av.gid ) );
+	conn->local_id = random();
+	memcpy ( &conn->service_id, service_id, sizeof ( conn->service_id ) );
+	conn->op = op;
+	conn->private_data_len = private_data_len;
+	memcpy ( &conn->private_data, private_data, private_data_len );
+
+	/* Create path */
+	conn->path = ib_create_path ( ibdev, &qp->av, &ib_cm_path_op );
+	if ( ! conn->path )
+		goto err_create_path;
+	ib_path_set_ownerdata ( conn->path, conn );
+
+	/* Add to list of connections */
+	list_add ( &conn->list, &ib_cm_conns );
+
+	DBGC ( conn, "CM %p created for IBDEV %p QPN %lx\n",
+	       conn, ibdev, qp->qpn );
+	DBGC ( conn, "CM %p connecting to %08x:%08x:%08x:%08x %08x:%08x\n",
+	       conn, ntohl ( dgid->u.dwords[0] ), ntohl ( dgid->u.dwords[1] ),
+	       ntohl ( dgid->u.dwords[2] ), ntohl ( dgid->u.dwords[3] ),
+	       ntohl ( service_id->u.dwords[0] ),
+	       ntohl ( service_id->u.dwords[1] ) );
+
+	return conn;
+
+	ib_destroy_path ( ibdev, conn->path );
+ err_create_path:
+	free ( conn );
+ err_alloc_conn:
+	return NULL;
+}
+
+/**
+ * Destroy connection to remote QP
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v conn		Connection
+ */
+void ib_destroy_conn ( struct ib_device *ibdev,
+		       struct ib_queue_pair *qp __unused,
+		       struct ib_connection *conn ) {
+
+	list_del ( &conn->list );
+	if ( conn->madx )
+		ib_destroy_madx ( ibdev, ibdev->gsi, conn->madx );
+	if ( conn->path )
+		ib_destroy_path ( ibdev, conn->path );
+	free ( conn );
+}
diff --git a/gpxe/src/net/infiniband/ib_cmrc.c b/gpxe/src/net/infiniband/ib_cmrc.c
new file mode 100644
index 0000000..2d64811
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_cmrc.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2009 Fen Systems Ltd <mbrown at fensystems.co.uk>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/xfer.h>
+#include <gpxe/process.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ib_cm.h>
+#include <gpxe/ib_cmrc.h>
+
+/**
+ * @file
+ *
+ * Infiniband Communication-managed Reliable Connections
+ *
+ */
+
+/** CMRC number of send WQEs
+ *
+ * This is a policy decision.
+ */
+#define IB_CMRC_NUM_SEND_WQES 4
+
+/** CMRC number of receive WQEs
+ *
+ * This is a policy decision.
+ */
+#define IB_CMRC_NUM_RECV_WQES 2
+
+/** CMRC number of completion queue entries
+ *
+ * This is a policy decision
+ */
+#define IB_CMRC_NUM_CQES 8
+
+/** An Infiniband Communication-Managed Reliable Connection */
+struct ib_cmrc_connection {
+	/** Reference count */
+	struct refcnt refcnt;
+	/** Data transfer interface */
+	struct xfer_interface xfer;
+	/** Infiniband device */
+	struct ib_device *ibdev;
+	/** Completion queue */
+	struct ib_completion_queue *cq;
+	/** Queue pair */
+	struct ib_queue_pair *qp;
+	/** Connection */
+	struct ib_connection *conn;
+	/** Destination GID */
+	struct ib_gid dgid;
+	/** Service ID */
+	struct ib_gid_half service_id;
+	/** QP is connected */
+	int connected;
+	/** Shutdown process */
+	struct process shutdown;
+};
+
+/**
+ * Shut down CMRC connection gracefully
+ *
+ * @v process		Process
+ *
+ * The Infiniband data structures are not reference-counted or
+ * guarded.  It is therefore unsafe to shut them down while we may be
+ * in the middle of a callback from the Infiniband stack (e.g. in a
+ * receive completion handler).
+ *
+ * This shutdown process will run some time after the call to
+ * ib_cmrc_close(), after control has returned out of the Infiniband
+ * core, and will shut down the Infiniband interfaces cleanly.
+ *
+ * The shutdown process holds an implicit reference on the CMRC
+ * connection, ensuring that the structure is not freed before the
+ * shutdown process has run.
+ */
+static void ib_cmrc_shutdown ( struct process *process ) {
+	struct ib_cmrc_connection *cmrc =
+		container_of ( process, struct ib_cmrc_connection, shutdown );
+
+	DBGC ( cmrc, "CMRC %p shutting down\n", cmrc );
+
+	/* Shut down Infiniband interface */
+	ib_destroy_conn ( cmrc->ibdev, cmrc->qp, cmrc->conn );
+	ib_destroy_qp ( cmrc->ibdev, cmrc->qp );
+	ib_destroy_cq ( cmrc->ibdev, cmrc->cq );
+	ib_close ( cmrc->ibdev );
+
+	/* Remove process from run queue */
+	process_del ( &cmrc->shutdown );
+
+	/* Drop the remaining reference */
+	ref_put ( &cmrc->refcnt );
+}
+
+/**
+ * Close CMRC connection
+ *
+ * @v cmrc		Communication-Managed Reliable Connection
+ * @v rc		Reason for close
+ */
+static void ib_cmrc_close ( struct ib_cmrc_connection *cmrc, int rc ) {
+
+	/* Close data transfer interface */
+	xfer_nullify ( &cmrc->xfer );
+	xfer_close ( &cmrc->xfer, rc );
+
+	/* Schedule shutdown process */
+	process_add ( &cmrc->shutdown );
+}
+
+/**
+ * Handle change of CMRC connection status
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v conn		Connection
+ * @v rc_cm		Connection status code
+ * @v private_data	Private data, if available
+ * @v private_data_len	Length of private data
+ */
+static void ib_cmrc_changed ( struct ib_device *ibdev __unused,
+			      struct ib_queue_pair *qp,
+			      struct ib_connection *conn __unused, int rc_cm,
+			      void *private_data, size_t private_data_len ) {
+	struct ib_cmrc_connection *cmrc = ib_qp_get_ownerdata ( qp );
+	int rc_xfer;
+
+	/* Record connection status */
+	if ( rc_cm == 0 ) {
+		DBGC ( cmrc, "CMRC %p connected\n", cmrc );
+		cmrc->connected = 1;
+	} else {
+		DBGC ( cmrc, "CMRC %p disconnected: %s\n",
+		       cmrc, strerror ( rc_cm ) );
+		cmrc->connected = 0;
+	}
+
+	/* Pass up any private data */
+	DBGC2 ( cmrc, "CMRC %p received private data:\n", cmrc );
+	DBGC2_HDA ( cmrc, 0, private_data, private_data_len );
+	if ( private_data &&
+	     ( rc_xfer = xfer_deliver_raw ( &cmrc->xfer, private_data,
+					    private_data_len ) ) != 0 ) {
+		DBGC ( cmrc, "CMRC %p could not deliver private data: %s\n",
+		       cmrc, strerror ( rc_xfer ) );
+		ib_cmrc_close ( cmrc, rc_xfer );
+		return;
+	}
+
+	/* If we are disconnected, close the upper connection */
+	if ( rc_cm != 0 ) {
+		ib_cmrc_close ( cmrc, rc_cm );
+		return;
+	}
+}
+
+/** CMRC connection operations */
+static struct ib_connection_operations ib_cmrc_conn_op = {
+	.changed = ib_cmrc_changed,
+};
+
+/**
+ * Handle CMRC send completion
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v iobuf		I/O buffer
+ * @v rc		Completion status code
+ */
+static void ib_cmrc_complete_send ( struct ib_device *ibdev __unused,
+				    struct ib_queue_pair *qp,
+				    struct io_buffer *iobuf, int rc ) {
+	struct ib_cmrc_connection *cmrc = ib_qp_get_ownerdata ( qp );
+
+	/* Free the completed I/O buffer */
+	free_iob ( iobuf );
+
+	/* Close the connection on any send errors */
+	if ( rc != 0 ) {
+		DBGC ( cmrc, "CMRC %p send error: %s\n",
+		       cmrc, strerror ( rc ) );
+		ib_cmrc_close ( cmrc, rc );
+		return;
+	}
+}
+
+/**
+ * Handle CMRC receive completion
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v av		Address vector, or NULL
+ * @v iobuf		I/O buffer
+ * @v rc		Completion status code
+ */
+static void ib_cmrc_complete_recv ( struct ib_device *ibdev __unused,
+				    struct ib_queue_pair *qp,
+				    struct ib_address_vector *av __unused,
+				    struct io_buffer *iobuf, int rc ) {
+	struct ib_cmrc_connection *cmrc = ib_qp_get_ownerdata ( qp );
+
+	/* Close the connection on any receive errors */
+	if ( rc != 0 ) {
+		DBGC ( cmrc, "CMRC %p receive error: %s\n",
+		       cmrc, strerror ( rc ) );
+		free_iob ( iobuf );
+		ib_cmrc_close ( cmrc, rc );
+		return;
+	}
+
+	DBGC2 ( cmrc, "CMRC %p received:\n", cmrc );
+	DBGC2_HDA ( cmrc, 0, iobuf->data, iob_len ( iobuf ) );
+
+	/* Pass up data */
+	if ( ( rc = xfer_deliver_iob ( &cmrc->xfer, iobuf ) ) != 0 ) {
+		DBGC ( cmrc, "CMRC %p could not deliver data: %s\n",
+		       cmrc, strerror ( rc ) );
+		ib_cmrc_close ( cmrc, rc );
+		return;
+	}
+}
+
+/** Infiniband CMRC completion operations */
+static struct ib_completion_queue_operations ib_cmrc_completion_ops = {
+	.complete_send = ib_cmrc_complete_send,
+	.complete_recv = ib_cmrc_complete_recv,
+};
+
+/**
+ * Send data via CMRC
+ *
+ * @v xfer		Data transfer interface
+ * @v iobuf		Datagram I/O buffer
+ * @v meta		Data transfer metadata
+ * @ret rc		Return status code
+ */
+static int ib_cmrc_xfer_deliver_iob ( struct xfer_interface *xfer,
+				      struct io_buffer *iobuf,
+				      struct xfer_metadata *meta __unused ) {
+	struct ib_cmrc_connection *cmrc =
+		container_of ( xfer, struct ib_cmrc_connection, xfer );
+	int rc;
+
+	/* If no connection has yet been attempted, send this datagram
+	 * as the CM REQ private data.  Otherwise, send it via the QP.
+	 */
+	if ( ! cmrc->connected ) {
+
+		/* Abort if we have already sent a CM connection request */
+		if ( cmrc->conn ) {
+			DBGC ( cmrc, "CMRC %p attempt to send before "
+			       "connection is complete\n", cmrc );
+			rc = -EIO;
+			goto out;
+		}
+
+		/* Send via CM connection request */
+		cmrc->conn = ib_create_conn ( cmrc->ibdev, cmrc->qp,
+					      &cmrc->dgid, &cmrc->service_id,
+					      iobuf->data, iob_len ( iobuf ),
+					      &ib_cmrc_conn_op );
+		if ( ! cmrc->conn ) {
+			DBGC ( cmrc, "CMRC %p could not connect\n", cmrc );
+			rc = -ENOMEM;
+			goto out;
+		}
+
+	} else {
+
+		/* Send via QP */
+		if ( ( rc = ib_post_send ( cmrc->ibdev, cmrc->qp, NULL,
+					   iob_disown ( iobuf ) ) ) != 0 ) {
+			DBGC ( cmrc, "CMRC %p could not send: %s\n",
+			       cmrc, strerror ( rc ) );
+			goto out;
+		}
+
+	}
+	return 0;
+
+ out:
+	/* Free the I/O buffer if necessary */
+	free_iob ( iobuf );
+
+	/* Close the connection on any errors */
+	if ( rc != 0 )
+		ib_cmrc_close ( cmrc, rc );
+
+	return rc;
+}
+
+/**
+ * Check CMRC flow control window
+ *
+ * @v xfer		Data transfer interface
+ * @ret len		Length of window
+ */
+static size_t ib_cmrc_xfer_window ( struct xfer_interface *xfer ) {
+	struct ib_cmrc_connection *cmrc =
+		container_of ( xfer, struct ib_cmrc_connection, xfer );
+
+	/* We indicate a window only when we are successfully
+	 * connected.
+	 */
+	return ( cmrc->connected ? IB_MAX_PAYLOAD_SIZE : 0 );
+}
+
+/**
+ * Close CMRC data-transfer interface
+ *
+ * @v xfer		Data transfer interface
+ * @v rc		Reason for close
+ */
+static void ib_cmrc_xfer_close ( struct xfer_interface *xfer, int rc ) {
+	struct ib_cmrc_connection *cmrc =
+		container_of ( xfer, struct ib_cmrc_connection, xfer );
+
+	DBGC ( cmrc, "CMRC %p closed: %s\n", cmrc, strerror ( rc ) );
+	ib_cmrc_close ( cmrc, rc );
+}
+
+/** CMRC data transfer interface operations */
+static struct xfer_interface_operations ib_cmrc_xfer_operations = {
+	.close		= ib_cmrc_xfer_close,
+	.vredirect	= ignore_xfer_vredirect,
+	.window		= ib_cmrc_xfer_window,
+	.alloc_iob	= default_xfer_alloc_iob,
+	.deliver_iob	= ib_cmrc_xfer_deliver_iob,
+	.deliver_raw	= xfer_deliver_as_iob,
+};
+
+/**
+ * Open CMRC connection
+ *
+ * @v xfer		Data transfer interface
+ * @v ibdev		Infiniband device
+ * @v dgid		Destination GID
+ * @v service_id	Service ID
+ * @ret rc		Returns status code
+ */
+int ib_cmrc_open ( struct xfer_interface *xfer, struct ib_device *ibdev,
+		   struct ib_gid *dgid, struct ib_gid_half *service_id ) {
+	struct ib_cmrc_connection *cmrc;
+	int rc;
+
+	/* Allocate and initialise structure */
+	cmrc = zalloc ( sizeof ( *cmrc ) );
+	if ( ! cmrc ) {
+		rc = -ENOMEM;
+		goto err_alloc;
+	}
+	xfer_init ( &cmrc->xfer, &ib_cmrc_xfer_operations, &cmrc->refcnt );
+	cmrc->ibdev = ibdev;
+	memcpy ( &cmrc->dgid, dgid, sizeof ( cmrc->dgid ) );
+	memcpy ( &cmrc->service_id, service_id, sizeof ( cmrc->service_id ) );
+	process_init_stopped ( &cmrc->shutdown, ib_cmrc_shutdown,
+			       &cmrc->refcnt );
+
+	/* Open Infiniband device */
+	if ( ( rc = ib_open ( ibdev ) ) != 0 ) {
+		DBGC ( cmrc, "CMRC %p could not open device: %s\n",
+		       cmrc, strerror ( rc ) );
+		goto err_open;
+	}
+
+	/* Create completion queue */
+	cmrc->cq = ib_create_cq ( ibdev, IB_CMRC_NUM_CQES,
+				  &ib_cmrc_completion_ops );
+	if ( ! cmrc->cq ) {
+		DBGC ( cmrc, "CMRC %p could not create completion queue\n",
+		       cmrc );
+		rc = -ENOMEM;
+		goto err_create_cq;
+	}
+
+	/* Create queue pair */
+	cmrc->qp = ib_create_qp ( ibdev, IB_QPT_RC, IB_CMRC_NUM_SEND_WQES,
+				  cmrc->cq, IB_CMRC_NUM_RECV_WQES, cmrc->cq );
+	if ( ! cmrc->qp ) {
+		DBGC ( cmrc, "CMRC %p could not create queue pair\n", cmrc );
+		rc = -ENOMEM;
+		goto err_create_qp;
+	}
+	ib_qp_set_ownerdata ( cmrc->qp, cmrc );
+	DBGC ( cmrc, "CMRC %p using QPN %lx\n", cmrc, cmrc->qp->qpn );
+
+	/* Attach to parent interface, transfer reference (implicitly)
+	 * to our shutdown process, and return.
+	 */
+	xfer_plug_plug ( &cmrc->xfer, xfer );
+	return 0;
+
+	ib_destroy_qp ( ibdev, cmrc->qp );
+ err_create_qp:
+	ib_destroy_cq ( ibdev, cmrc->cq );
+ err_create_cq:
+	ib_close ( ibdev );
+ err_open:
+	ref_put ( &cmrc->refcnt );
+ err_alloc:
+	return rc;
+}
diff --git a/gpxe/src/net/infiniband/ib_mcast.c b/gpxe/src/net/infiniband/ib_mcast.c
new file mode 100644
index 0000000..5cb395d
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_mcast.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/list.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ib_mi.h>
+#include <gpxe/ib_mcast.h>
+
+/** @file
+ *
+ * Infiniband multicast groups
+ *
+ */
+
+/**
+ * Generate multicast membership MAD
+ *
+ * @v ibdev		Infiniband device
+ * @v gid		Multicast GID
+ * @v join		Join (rather than leave) group
+ * @v mad		MAD to fill in
+ */
+static void ib_mcast_mad ( struct ib_device *ibdev, struct ib_gid *gid,
+			   int join, union ib_mad *mad ) {
+	struct ib_mad_sa *sa = &mad->sa;
+
+	/* Construct multicast membership record request */
+	memset ( sa, 0, sizeof ( *sa ) );
+	sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
+	sa->mad_hdr.class_version = IB_SA_CLASS_VERSION;
+	sa->mad_hdr.method =
+		( join ? IB_MGMT_METHOD_SET : IB_MGMT_METHOD_DELETE );
+	sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_MC_MEMBER_REC );
+	sa->sa_hdr.comp_mask[1] =
+		htonl ( IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID |
+			IB_SA_MCMEMBER_REC_JOIN_STATE );
+	sa->sa_data.mc_member_record.scope__join_state = 1;
+	memcpy ( &sa->sa_data.mc_member_record.mgid, gid,
+		 sizeof ( sa->sa_data.mc_member_record.mgid ) );
+	memcpy ( &sa->sa_data.mc_member_record.port_gid, &ibdev->gid,
+		 sizeof ( sa->sa_data.mc_member_record.port_gid ) );
+}
+
+/**
+ * Handle multicast membership record join response
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v madx		Management transaction
+ * @v rc		Status code
+ * @v mad		Received MAD (or NULL on error)
+ * @v av		Source address vector (or NULL on error)
+ */
+static void ib_mcast_complete ( struct ib_device *ibdev,
+				struct ib_mad_interface *mi __unused,
+				struct ib_mad_transaction *madx,
+				int rc, union ib_mad *mad,
+				struct ib_address_vector *av __unused ) {
+	struct ib_mc_membership *membership = ib_madx_get_ownerdata ( madx );
+	struct ib_queue_pair *qp = membership->qp;
+	struct ib_gid *gid = &membership->gid;
+	struct ib_mc_member_record *mc_member_record =
+		&mad->sa.sa_data.mc_member_record;
+	int joined;
+	unsigned long qkey;
+
+	/* Report failures */
+	if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ))
+		rc = -ENOTCONN;
+	if ( rc != 0 ) {
+		DBGC ( ibdev, "IBDEV %p QPN %lx join failed: %s\n",
+		       ibdev, qp->qpn, strerror ( rc ) );
+		goto out;
+	}
+
+	/* Extract values from MAD */
+	joined = ( mad->hdr.method == IB_MGMT_METHOD_GET_RESP );
+	qkey = ntohl ( mc_member_record->qkey );
+	DBGC ( ibdev, "IBDEV %p QPN %lx %s %08x:%08x:%08x:%08x qkey %lx\n",
+	       ibdev, qp->qpn, ( joined ? "joined" : "left" ),
+	       ntohl ( gid->u.dwords[0] ), ntohl ( gid->u.dwords[1] ),
+	       ntohl ( gid->u.dwords[2] ), ntohl ( gid->u.dwords[3] ),
+	       qkey );
+
+	/* Set queue key */
+	qp->qkey = qkey;
+	if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p QPN %lx could not modify qkey: %s\n",
+		       ibdev, qp->qpn, strerror ( rc ) );
+		goto out;
+	}
+
+ out:
+	/* Destroy the completed transaction */
+	ib_destroy_madx ( ibdev, mi, madx );
+	membership->madx = NULL;
+
+	/* Hand off to upper completion handler */
+	membership->complete ( ibdev, qp, membership, rc, mad );
+}
+
+/** Multicast membership management transaction completion operations */
+static struct ib_mad_transaction_operations ib_mcast_op = {
+	.complete = ib_mcast_complete,
+};
+
+/**
+ * Join multicast group
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v membership	Multicast group membership
+ * @v gid		Multicast GID to join
+ * @v joined		Join completion handler
+ * @ret rc		Return status code
+ */
+int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+		    struct ib_mc_membership *membership, struct ib_gid *gid,
+		    void ( * complete ) ( struct ib_device *ibdev,
+					  struct ib_queue_pair *qp,
+					  struct ib_mc_membership *membership,
+					  int rc, union ib_mad *mad ) ) {
+	union ib_mad mad;
+	int rc;
+
+	DBGC ( ibdev, "IBDEV %p QPN %lx joining %08x:%08x:%08x:%08x\n",
+	       ibdev, qp->qpn, ntohl ( gid->u.dwords[0] ),
+	       ntohl ( gid->u.dwords[1] ), ntohl ( gid->u.dwords[2] ),
+	       ntohl ( gid->u.dwords[3] ) );
+
+	/* Initialise structure */
+	membership->qp = qp;
+	memcpy ( &membership->gid, gid, sizeof ( membership->gid ) );
+	membership->complete = complete;
+
+	/* Attach queue pair to multicast GID */
+	if ( ( rc = ib_mcast_attach ( ibdev, qp, gid ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p QPN %lx could not attach: %s\n",
+		       ibdev, qp->qpn, strerror ( rc ) );
+		goto err_mcast_attach;
+	}
+
+	/* Initiate multicast membership join */
+	ib_mcast_mad ( ibdev, gid, 1, &mad );
+	membership->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL,
+					    &ib_mcast_op );
+	if ( ! membership->madx ) {
+		DBGC ( ibdev, "IBDEV %p QPN %lx could not create join "
+		       "transaction\n", ibdev, qp->qpn );
+		rc = -ENOMEM;
+		goto err_create_madx;
+	}
+	ib_madx_set_ownerdata ( membership->madx, membership );
+
+	return 0;
+
+	ib_destroy_madx ( ibdev, ibdev->gsi, membership->madx );
+ err_create_madx:
+	ib_mcast_detach ( ibdev, qp, gid );
+ err_mcast_attach:
+	return rc;
+}
+
+/**
+ * Leave multicast group
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v membership	Multicast group membership
+ */
+void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+		      struct ib_mc_membership *membership ) {
+	struct ib_gid *gid = &membership->gid;
+	union ib_mad mad;
+	int rc;
+
+	DBGC ( ibdev, "IBDEV %p QPN %lx leaving %08x:%08x:%08x:%08x\n",
+	       ibdev, qp->qpn, ntohl ( gid->u.dwords[0] ),
+	       ntohl ( gid->u.dwords[1] ), ntohl ( gid->u.dwords[2] ),
+	       ntohl ( gid->u.dwords[3] ) );
+
+	/* Detach from multicast GID */
+	ib_mcast_detach ( ibdev, qp, &membership->gid );
+
+	/* Cancel multicast membership join, if applicable */
+	if ( membership->madx ) {
+		ib_destroy_madx ( ibdev, ibdev->gsi, membership->madx );
+		membership->madx = NULL;
+	}
+
+	/* Send a single group leave MAD */
+	ib_mcast_mad ( ibdev, &membership->gid, 0, &mad );
+	if ( ( rc = ib_mi_send ( ibdev, ibdev->gsi, &mad, NULL ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p QPN %lx could not send leave request: "
+		       "%s\n", ibdev, qp->qpn, strerror ( rc ) );
+	}
+}
diff --git a/gpxe/src/net/infiniband/ib_mi.c b/gpxe/src/net/infiniband/ib_mi.c
new file mode 100644
index 0000000..7511fd8
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_mi.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2009 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/ib_mi.h>
+
+/**
+ * @file
+ *
+ * Infiniband management interfaces
+ *
+ */
+
+/** Management interface number of send WQEs
+ *
+ * This is a policy decision.
+ */
+#define IB_MI_NUM_SEND_WQES 4
+
+/** Management interface number of receive WQEs
+ *
+ * This is a policy decision.
+ */
+#define IB_MI_NUM_RECV_WQES 2
+
+/** Management interface number of completion queue entries
+ *
+ * This is a policy decision
+ */
+#define IB_MI_NUM_CQES 8
+
+/** TID magic signature */
+#define IB_MI_TID_MAGIC ( ( 'g' << 24 ) | ( 'P' << 16 ) | ( 'X' << 8 ) | 'E' )
+
+/** TID to use for next MAD */
+static unsigned int next_tid;
+
+/**
+ * Handle received MAD
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @v av		Source address vector
+ * @ret rc		Return status code
+ */
+static int ib_mi_handle ( struct ib_device *ibdev,
+			  struct ib_mad_interface *mi,
+			  union ib_mad *mad,
+			  struct ib_address_vector *av ) {
+	struct ib_mad_hdr *hdr = &mad->hdr;
+	struct ib_mad_transaction *madx;
+	struct ib_mad_agent *agent;
+
+	/* Look for a matching transaction by TID */
+	list_for_each_entry ( madx, &mi->madx, list ) {
+		if ( memcmp ( &hdr->tid, &madx->mad.hdr.tid,
+			      sizeof ( hdr->tid ) ) != 0 )
+			continue;
+		/* Found a matching transaction */
+		madx->op->complete ( ibdev, mi, madx, 0, mad, av );
+		return 0;
+	}
+
+	/* If there is no matching transaction, look for a listening agent */
+	for_each_table_entry ( agent, IB_MAD_AGENTS ) {
+		if ( ( ( agent->mgmt_class & IB_MGMT_CLASS_MASK ) !=
+		       ( hdr->mgmt_class & IB_MGMT_CLASS_MASK ) ) ||
+		     ( agent->class_version != hdr->class_version ) ||
+		     ( agent->attr_id != hdr->attr_id ) )
+			continue;
+		/* Found a matching agent */
+		agent->handle ( ibdev, mi, mad, av );
+		return 0;
+	}
+
+	/* Otherwise, ignore it */
+	DBGC ( mi, "MI %p RX TID %08x%08x ignored\n",
+	       mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) );
+	return -ENOTSUP;
+}
+
+/**
+ * Complete receive via management interface
+ *
+ *
+ * @v ibdev		Infiniband device
+ * @v qp		Queue pair
+ * @v av		Address vector
+ * @v iobuf		I/O buffer
+ * @v rc		Completion status code
+ */
+static void ib_mi_complete_recv ( struct ib_device *ibdev,
+				  struct ib_queue_pair *qp,
+				  struct ib_address_vector *av,
+				  struct io_buffer *iobuf, int rc ) {
+	struct ib_mad_interface *mi = ib_qp_get_ownerdata ( qp );
+	union ib_mad *mad;
+	struct ib_mad_hdr *hdr;
+
+	/* Ignore errors */
+	if ( rc != 0 ) {
+		DBGC ( mi, "MI %p RX error: %s\n", mi, strerror ( rc ) );
+		goto out;
+	}
+
+	/* Sanity checks */
+	if ( iob_len ( iobuf ) != sizeof ( *mad ) ) {
+		DBGC ( mi, "MI %p RX bad size (%zd bytes)\n",
+		       mi, iob_len ( iobuf ) );
+		DBGC_HDA ( mi, 0, iobuf->data, iob_len ( iobuf ) );
+		goto out;
+	}
+	mad = iobuf->data;
+	hdr = &mad->hdr;
+	if ( hdr->base_version != IB_MGMT_BASE_VERSION ) {
+		DBGC ( mi, "MI %p RX unsupported base version %x\n",
+		       mi, hdr->base_version );
+		DBGC_HDA ( mi, 0, mad, sizeof ( *mad ) );
+		goto out;
+	}
+	DBGC ( mi, "MI %p RX TID %08x%08x (%02x,%02x,%02x,%04x) status "
+	       "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ),
+	       hdr->mgmt_class, hdr->class_version, hdr->method,
+	       ntohs ( hdr->attr_id ), ntohs ( hdr->status ) );
+	DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) );
+
+	/* Handle MAD */
+	if ( ( rc = ib_mi_handle ( ibdev, mi, mad, av ) ) != 0 )
+		goto out;
+
+ out:
+	free_iob ( iobuf );
+}
+
+/** Management interface completion operations */
+static struct ib_completion_queue_operations ib_mi_completion_ops = {
+	.complete_recv = ib_mi_complete_recv,
+};
+
+/**
+ * Transmit MAD
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		MAD
+ * @v av		Destination address vector
+ * @ret rc		Return status code
+ */
+int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi,
+		 union ib_mad *mad, struct ib_address_vector *av ) {
+	struct ib_mad_hdr *hdr = &mad->hdr;
+	struct io_buffer *iobuf;
+	int rc;
+
+	/* Set common fields */
+	hdr->base_version = IB_MGMT_BASE_VERSION;
+	if ( ( hdr->tid[0] == 0 ) && ( hdr->tid[1] == 0 ) ) {
+		hdr->tid[0] = htonl ( IB_MI_TID_MAGIC );
+		hdr->tid[1] = htonl ( ++next_tid );
+	}
+	DBGC ( mi, "MI %p TX TID %08x%08x (%02x,%02x,%02x,%04x) status "
+	       "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ),
+	       hdr->mgmt_class, hdr->class_version, hdr->method,
+	       ntohs ( hdr->attr_id ), ntohs ( hdr->status ) );
+	DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) );
+
+	/* Construct directed route portion of response, if necessary */
+	if ( hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) {
+		struct ib_mad_smp *smp = &mad->smp;
+		unsigned int hop_pointer;
+		unsigned int hop_count;
+
+		smp->mad_hdr.status |= htons ( IB_SMP_STATUS_D_INBOUND );
+		hop_pointer = smp->mad_hdr.class_specific.smp.hop_pointer;
+		hop_count = smp->mad_hdr.class_specific.smp.hop_count;
+		assert ( hop_count == hop_pointer );
+		if ( hop_pointer < ( sizeof ( smp->return_path.hops ) /
+				     sizeof ( smp->return_path.hops[0] ) ) ) {
+			smp->return_path.hops[hop_pointer] = ibdev->port;
+		} else {
+			DBGC ( mi, "MI %p TX TID %08x%08x invalid hop pointer "
+			       "%d\n", mi, ntohl ( hdr->tid[0] ),
+			       ntohl ( hdr->tid[1] ), hop_pointer );
+			return -EINVAL;
+		}
+	}
+
+	/* Construct I/O buffer */
+	iobuf = alloc_iob ( sizeof ( *mad ) );
+	if ( ! iobuf ) {
+		DBGC ( mi, "MI %p could not allocate buffer for TID "
+		       "%08x%08x\n",
+		       mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) );
+		return -ENOMEM;
+	}
+	memcpy ( iob_put ( iobuf, sizeof ( *mad ) ), mad, sizeof ( *mad ) );
+
+	/* Send I/O buffer */
+	if ( ( rc = ib_post_send ( ibdev, mi->qp, av, iobuf ) ) != 0 ) {
+		DBGC ( mi, "MI %p TX TID %08x%08x failed: %s\n",
+		       mi,  ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ),
+		       strerror ( rc ) );
+		free_iob ( iobuf );
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * Handle management transaction timer expiry
+ *
+ * @v timer		Retry timer
+ * @v expired		Failure indicator
+ */
+static void ib_mi_timer_expired ( struct retry_timer *timer, int expired ) {
+	struct ib_mad_transaction *madx =
+		container_of ( timer, struct ib_mad_transaction, timer );
+	struct ib_mad_interface *mi = madx->mi;
+	struct ib_device *ibdev = mi->ibdev;
+	struct ib_mad_hdr *hdr = &madx->mad.hdr;
+
+	/* Abandon transaction if we have tried too many times */
+	if ( expired ) {
+		DBGC ( mi, "MI %p abandoning TID %08x%08x\n",
+		       mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) );
+		madx->op->complete ( ibdev, mi, madx, -ETIMEDOUT, NULL, NULL );
+		return;
+	}
+
+	/* Restart retransmission timer */
+	start_timer ( timer );
+
+	/* Resend MAD */
+	ib_mi_send ( ibdev, mi, &madx->mad, &madx->av );
+}
+
+/**
+ * Create management transaction
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		MAD to send
+ * @v av		Destination address, or NULL to use SM's GSI
+ * @v op		Management transaction operations
+ * @ret madx		Management transaction, or NULL
+ */
+struct ib_mad_transaction *
+ib_create_madx ( struct ib_device *ibdev, struct ib_mad_interface *mi,
+		 union ib_mad *mad, struct ib_address_vector *av,
+		 struct ib_mad_transaction_operations *op ) {
+	struct ib_mad_transaction *madx;
+
+	/* Allocate and initialise structure */
+	madx = zalloc ( sizeof ( *madx ) );
+	if ( ! madx )
+		return NULL;
+	madx->mi = mi;
+	madx->timer.expired = ib_mi_timer_expired;
+	madx->op = op;
+
+	/* Determine address vector */
+	if ( av ) {
+		memcpy ( &madx->av, av, sizeof ( madx->av ) );
+	} else {
+		madx->av.lid = ibdev->sm_lid;
+		madx->av.sl = ibdev->sm_sl;
+		madx->av.qpn = IB_QPN_GSI;
+		madx->av.qkey = IB_QKEY_GSI;
+	}
+
+	/* Copy MAD */
+	memcpy ( &madx->mad, mad, sizeof ( madx->mad ) );
+
+	/* Add to list and start timer to send initial MAD */
+	list_add ( &madx->list, &mi->madx );
+	start_timer_nodelay ( &madx->timer );
+
+	return madx;
+}
+
+/**
+ * Destroy management transaction
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v madx		Management transaction
+ */
+void ib_destroy_madx ( struct ib_device *ibdev __unused,
+		       struct ib_mad_interface *mi __unused,
+		       struct ib_mad_transaction *madx ) {
+
+	/* Stop timer and remove from list */
+	stop_timer ( &madx->timer );
+	list_del ( &madx->list );
+
+	/* Free transaction */
+	free ( madx );
+}
+
+/**
+ * Create management interface
+ *
+ * @v ibdev		Infiniband device
+ * @v type		Queue pair type
+ * @ret mi		Management agent, or NULL
+ */
+struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev,
+					 enum ib_queue_pair_type type ) {
+	struct ib_mad_interface *mi;
+	int rc;
+
+	/* Allocate and initialise fields */
+	mi = zalloc ( sizeof ( *mi ) );
+	if ( ! mi )
+		goto err_alloc;
+	mi->ibdev = ibdev;
+	INIT_LIST_HEAD ( &mi->madx );
+
+	/* Create completion queue */
+	mi->cq = ib_create_cq ( ibdev, IB_MI_NUM_CQES, &ib_mi_completion_ops );
+	if ( ! mi->cq ) {
+		DBGC ( mi, "MI %p could not allocate completion queue\n", mi );
+		goto err_create_cq;
+	}
+
+	/* Create queue pair */
+	mi->qp = ib_create_qp ( ibdev, type, IB_MI_NUM_SEND_WQES, mi->cq,
+				IB_MI_NUM_RECV_WQES, mi->cq );
+	if ( ! mi->qp ) {
+		DBGC ( mi, "MI %p could not allocate queue pair\n", mi );
+		goto err_create_qp;
+	}
+	ib_qp_set_ownerdata ( mi->qp, mi );
+	DBGC ( mi, "MI %p (%s) running on QPN %#lx\n",
+	       mi, ( ( type == IB_QPT_SMI ) ? "SMI" : "GSI" ), mi->qp->qpn );
+
+	/* Set queue key */
+	mi->qp->qkey = ( ( type == IB_QPT_SMI ) ? IB_QKEY_SMI : IB_QKEY_GSI );
+	if ( ( rc = ib_modify_qp ( ibdev, mi->qp ) ) != 0 ) {
+		DBGC ( mi, "MI %p could not set queue key: %s\n",
+		       mi, strerror ( rc ) );
+		goto err_modify_qp;
+	}
+
+	/* Fill receive ring */
+	ib_refill_recv ( ibdev, mi->qp );
+	return mi;
+
+ err_modify_qp:
+	ib_destroy_qp ( ibdev, mi->qp );
+ err_create_qp:
+	ib_destroy_cq ( ibdev, mi->cq );
+ err_create_cq:
+	free ( mi );
+ err_alloc:
+	return NULL;
+}
+
+/**
+ * Destroy management interface
+ *
+ * @v mi		Management interface
+ */
+void ib_destroy_mi ( struct ib_device *ibdev, struct ib_mad_interface *mi ) {
+	struct ib_mad_transaction *madx;
+	struct ib_mad_transaction *tmp;
+
+	/* Flush any outstanding requests */
+	list_for_each_entry_safe ( madx, tmp, &mi->madx, list ) {
+		DBGC ( mi, "MI %p destroyed while TID %08x%08x in progress\n",
+		       mi, ntohl ( madx->mad.hdr.tid[0] ),
+		       ntohl ( madx->mad.hdr.tid[1] ) );
+		madx->op->complete ( ibdev, mi, madx, -ECANCELED, NULL, NULL );
+	}
+
+	ib_destroy_qp ( ibdev, mi->qp );
+	ib_destroy_cq ( ibdev, mi->cq );
+	free ( mi );
+}
diff --git a/gpxe/src/net/infiniband/ib_packet.c b/gpxe/src/net/infiniband/ib_packet.c
new file mode 100644
index 0000000..08820ef
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_packet.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ib_packet.h>
+
+/**
+ * @file
+ *
+ * Infiniband Packet Formats
+ *
+ */
+
+/**
+ * Add IB headers
+ *
+ * @v ibdev		Infiniband device
+ * @v iobuf		I/O buffer to contain headers
+ * @v qp		Queue pair
+ * @v payload_len	Payload length
+ * @v av		Address vector
+ */
+int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf,
+	      struct ib_queue_pair *qp, size_t payload_len,
+	      const struct ib_address_vector *av ) {
+	struct ib_local_route_header *lrh;
+	struct ib_global_route_header *grh;
+	struct ib_base_transport_header *bth;
+	struct ib_datagram_extended_transport_header *deth;
+	size_t orig_iob_len = iob_len ( iobuf );
+	size_t pad_len;
+	size_t lrh_len;
+	size_t grh_len;
+	unsigned int vl;
+	unsigned int lnh;
+
+	DBGC2 ( ibdev, "IBDEV %p TX %04x:%08lx => %04x:%08lx (key %08lx)\n",
+		ibdev, ibdev->lid, qp->ext_qpn, av->lid, av->qpn, av->qkey );
+
+	/* Calculate packet length */
+	pad_len = ( (-payload_len) & 0x3 );
+	payload_len += pad_len;
+	payload_len += 4; /* ICRC */
+
+	/* Reserve space for headers */
+	orig_iob_len = iob_len ( iobuf );
+	deth = iob_push ( iobuf, sizeof ( *deth ) );
+	bth = iob_push ( iobuf, sizeof ( *bth ) );
+	grh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len );
+	grh = ( av->gid_present ?
+		iob_push ( iobuf, sizeof ( *grh ) ) : NULL );
+	lrh = iob_push ( iobuf, sizeof ( *lrh ) );
+	lrh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len );
+
+	/* Construct LRH */
+	vl = ( ( qp->ext_qpn == IB_QPN_SMI ) ? IB_VL_SMP : IB_VL_DEFAULT );
+	lrh->vl__lver = ( vl << 4 );
+	lnh = ( grh ? IB_LNH_GRH : IB_LNH_BTH );
+	lrh->sl__lnh = ( ( av->sl << 4 ) | lnh );
+	lrh->dlid = htons ( av->lid );
+	lrh->length = htons ( lrh_len >> 2 );
+	lrh->slid = htons ( ibdev->lid );
+
+	/* Construct GRH, if required */
+	if ( grh ) {
+		grh->ipver__tclass__flowlabel =
+			htonl ( IB_GRH_IPVER_IPv6 << 28 );
+		grh->paylen = htons ( grh_len );
+		grh->nxthdr = IB_GRH_NXTHDR_IBA;
+		grh->hoplmt = 0;
+		memcpy ( &grh->sgid, &ibdev->gid, sizeof ( grh->sgid ) );
+		memcpy ( &grh->dgid, &av->gid, sizeof ( grh->dgid ) );
+	}
+
+	/* Construct BTH */
+	bth->opcode = BTH_OPCODE_UD_SEND;
+	bth->se__m__padcnt__tver = ( pad_len << 4 );
+	bth->pkey = htons ( ibdev->pkey );
+	bth->dest_qp = htonl ( av->qpn );
+	bth->ack__psn = htonl ( ( qp->send.psn++ ) & 0xffffffUL );
+
+	/* Construct DETH */
+	deth->qkey = htonl ( av->qkey );
+	deth->src_qp = htonl ( qp->ext_qpn );
+
+	DBGCP_HDA ( ibdev, 0, iobuf->data,
+		    ( iob_len ( iobuf ) - orig_iob_len ) );
+
+	return 0;
+}
+
+/**
+ * Remove IB headers
+ *
+ * @v ibdev		Infiniband device
+ * @v iobuf		I/O buffer containing headers
+ * @v qp		Queue pair to fill in, or NULL
+ * @v payload_len	Payload length to fill in, or NULL
+ * @v av		Address vector to fill in
+ */
+int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf,
+	      struct ib_queue_pair **qp, size_t *payload_len,
+	      struct ib_address_vector *av ) {
+	struct ib_local_route_header *lrh;
+	struct ib_global_route_header *grh;
+	struct ib_base_transport_header *bth;
+	struct ib_datagram_extended_transport_header *deth;
+	size_t orig_iob_len = iob_len ( iobuf );
+	unsigned int lnh;
+	size_t pad_len;
+	unsigned long qpn;
+	unsigned int lid;
+
+	/* Clear return values */
+	if ( qp )
+		*qp = NULL;
+	if ( payload_len )
+		*payload_len = 0;
+	memset ( av, 0, sizeof ( *av ) );
+
+	/* Extract LRH */
+	if ( iob_len ( iobuf ) < sizeof ( *lrh ) ) {
+		DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for LRH\n",
+		       ibdev, iob_len ( iobuf ) );
+		return -EINVAL;
+	}
+	lrh = iobuf->data;
+	iob_pull ( iobuf, sizeof ( *lrh ) );
+	av->lid = ntohs ( lrh->slid );
+	av->sl = ( lrh->sl__lnh >> 4 );
+	lnh = ( lrh->sl__lnh & 0x3 );
+	lid = ntohs ( lrh->dlid );
+
+	/* Reject unsupported packets */
+	if ( ! ( ( lnh == IB_LNH_BTH ) || ( lnh == IB_LNH_GRH ) ) ) {
+		DBGC ( ibdev, "IBDEV %p RX unsupported LNH %x\n",
+		       ibdev, lnh );
+		return -ENOTSUP;
+	}
+
+	/* Extract GRH, if present */
+	if ( lnh == IB_LNH_GRH ) {
+		if ( iob_len ( iobuf ) < sizeof ( *grh ) ) {
+			DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) "
+			       "for GRH\n", ibdev, iob_len ( iobuf ) );
+			return -EINVAL;
+		}
+		grh = iobuf->data;
+		iob_pull ( iobuf, sizeof ( *grh ) );
+		av->gid_present = 1;
+		memcpy ( &av->gid, &grh->sgid, sizeof ( av->gid ) );
+	} else {
+		grh = NULL;
+	}
+
+	/* Extract BTH */
+	if ( iob_len ( iobuf ) < sizeof ( *bth ) ) {
+		DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for BTH\n",
+		       ibdev, iob_len ( iobuf ) );
+		return -EINVAL;
+	}
+	bth = iobuf->data;
+	iob_pull ( iobuf, sizeof ( *bth ) );
+	if ( bth->opcode != BTH_OPCODE_UD_SEND ) {
+		DBGC ( ibdev, "IBDEV %p unsupported BTH opcode %x\n",
+		       ibdev, bth->opcode );
+		return -ENOTSUP;
+	}
+	qpn = ntohl ( bth->dest_qp );
+
+	/* Extract DETH */
+	if ( iob_len ( iobuf ) < sizeof ( *deth ) ) {
+		DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for DETH\n",
+		       ibdev, iob_len ( iobuf ) );
+		return -EINVAL;
+	}
+	deth = iobuf->data;
+	iob_pull ( iobuf, sizeof ( *deth ) );
+	av->qpn = ntohl ( deth->src_qp );
+	av->qkey = ntohl ( deth->qkey );
+
+	/* Calculate payload length, if applicable */
+	if ( payload_len ) {
+		pad_len = ( ( bth->se__m__padcnt__tver >> 4 ) & 0x3 );
+		*payload_len = ( ( ntohs ( lrh->length ) << 2 )
+				 - ( orig_iob_len - iob_len ( iobuf ) )
+				 - pad_len - 4 /* ICRC */ );
+	}
+
+	/* Determine destination QP, if applicable */
+	if ( qp ) {
+		if ( IB_LID_MULTICAST ( lid ) && grh ) {
+			if ( ! ( *qp = ib_find_qp_mgid ( ibdev, &grh->dgid ))){
+				DBGC ( ibdev, "IBDEV %p RX for unknown MGID "
+				       "%08x:%08x:%08x:%08x\n", ibdev,
+				       ntohl ( grh->dgid.u.dwords[0] ),
+				       ntohl ( grh->dgid.u.dwords[1] ),
+				       ntohl ( grh->dgid.u.dwords[2] ),
+				       ntohl ( grh->dgid.u.dwords[3] ) );
+				return -ENODEV;
+			}
+		} else {
+			if ( ! ( *qp = ib_find_qp_qpn ( ibdev, qpn ) ) ) {
+				DBGC ( ibdev, "IBDEV %p RX for nonexistent "
+				       "QPN %lx\n", ibdev, qpn );
+				return -ENODEV;
+			}
+		}
+		assert ( *qp );
+	}
+
+	DBGC2 ( ibdev, "IBDEV %p RX %04x:%08lx <= %04x:%08lx (key %08x)\n",
+		ibdev, lid, ( IB_LID_MULTICAST( lid ) ?
+			      ( qp ? (*qp)->ext_qpn : -1UL ) : qpn ),
+		av->lid, av->qpn, ntohl ( deth->qkey ) );
+	DBGCP_HDA ( ibdev, 0,
+		    ( iobuf->data - ( orig_iob_len - iob_len ( iobuf ) ) ),
+		    ( orig_iob_len - iob_len ( iobuf ) ) );
+
+	return 0;
+}
diff --git a/gpxe/src/net/infiniband/ib_pathrec.c b/gpxe/src/net/infiniband/ib_pathrec.c
new file mode 100644
index 0000000..136e628
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_pathrec.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ib_mi.h>
+#include <gpxe/ib_pathrec.h>
+
+/** @file
+ *
+ * Infiniband path lookups
+ *
+ */
+
+/**
+ * Handle path transaction completion
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v madx		Management transaction
+ * @v rc		Status code
+ * @v mad		Received MAD (or NULL on error)
+ * @v av		Source address vector (or NULL on error)
+ */
+static void ib_path_complete ( struct ib_device *ibdev,
+			       struct ib_mad_interface *mi,
+			       struct ib_mad_transaction *madx,
+			       int rc, union ib_mad *mad,
+			       struct ib_address_vector *av __unused ) {
+	struct ib_path *path = ib_madx_get_ownerdata ( madx );
+	struct ib_gid *dgid = &path->av.gid;
+	struct ib_path_record *pathrec = &mad->sa.sa_data.path_record;
+
+	/* Report failures */
+	if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ))
+		rc = -ENETUNREACH;
+	if ( rc != 0 ) {
+		DBGC ( ibdev, "IBDEV %p path lookup for %08x:%08x:%08x:%08x "
+		       "failed: %s\n", ibdev, htonl ( dgid->u.dwords[0] ),
+		       htonl ( dgid->u.dwords[1] ),
+		       htonl ( dgid->u.dwords[2] ),
+		       htonl ( dgid->u.dwords[3] ), strerror ( rc ) );
+		goto out;
+	}
+
+	/* Extract values from MAD */
+	path->av.lid = ntohs ( pathrec->dlid );
+	path->av.sl = ( pathrec->reserved__sl & 0x0f );
+	path->av.rate = ( pathrec->rate_selector__rate & 0x3f );
+	DBGC ( ibdev, "IBDEV %p path to %08x:%08x:%08x:%08x is %04x sl %d "
+	       "rate %d\n", ibdev, htonl ( dgid->u.dwords[0] ),
+	       htonl ( dgid->u.dwords[1] ), htonl ( dgid->u.dwords[2] ),
+	       htonl ( dgid->u.dwords[3] ), path->av.lid, path->av.sl,
+	       path->av.rate );
+
+ out:
+	/* Destroy the completed transaction */
+	ib_destroy_madx ( ibdev, mi, madx );
+	path->madx = NULL;
+
+	/* Hand off to upper completion handler */
+	path->op->complete ( ibdev, path, rc, &path->av );
+}
+
+/** Path transaction completion operations */
+static struct ib_mad_transaction_operations ib_path_op = {
+	.complete = ib_path_complete,
+};
+
+/**
+ * Create path
+ *
+ * @v ibdev		Infiniband device
+ * @v av		Address vector to complete
+ * @v op		Path operations
+ * @ret path		Path
+ */
+struct ib_path *
+ib_create_path ( struct ib_device *ibdev, struct ib_address_vector *av,
+		 struct ib_path_operations *op ) {
+	struct ib_path *path;
+	union ib_mad mad;
+	struct ib_mad_sa *sa = &mad.sa;
+
+	/* Allocate and initialise structure */
+	path = zalloc ( sizeof ( *path ) );
+	if ( ! path )
+		goto err_alloc_path;
+	path->ibdev = ibdev;
+	memcpy ( &path->av, av, sizeof ( path->av ) );
+	path->op = op;
+
+	/* Construct path request */
+	memset ( sa, 0, sizeof ( *sa ) );
+	sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
+	sa->mad_hdr.class_version = IB_SA_CLASS_VERSION;
+	sa->mad_hdr.method = IB_MGMT_METHOD_GET;
+	sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_PATH_REC );
+	sa->sa_hdr.comp_mask[1] =
+		htonl ( IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID );
+	memcpy ( &sa->sa_data.path_record.dgid, &path->av.gid,
+		 sizeof ( sa->sa_data.path_record.dgid ) );
+	memcpy ( &sa->sa_data.path_record.sgid, &ibdev->gid,
+		 sizeof ( sa->sa_data.path_record.sgid ) );
+
+	/* Create management transaction */
+	path->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL,
+				      &ib_path_op );
+	if ( ! path->madx )
+		goto err_create_madx;
+	ib_madx_set_ownerdata ( path->madx, path );
+
+	return path;
+
+	ib_destroy_madx ( ibdev, ibdev->gsi, path->madx );
+ err_create_madx:
+	free ( path );
+ err_alloc_path:
+	return NULL;
+}
+
+/**
+ * Destroy path
+ *
+ * @v ibdev		Infiniband device
+ * @v path		Path
+ */
+void ib_destroy_path ( struct ib_device *ibdev, struct ib_path *path ) {
+
+	if ( path->madx )
+		ib_destroy_madx ( ibdev, ibdev->gsi, path->madx );
+	free ( path );
+}
+
+/** Number of path cache entries
+ *
+ * Must be a power of two.
+ */
+#define IB_NUM_CACHED_PATHS 4
+
+/** A cached path */
+struct ib_cached_path {
+	/** Path */
+	struct ib_path *path;
+};
+
+/** Path cache */
+static struct ib_cached_path ib_path_cache[IB_NUM_CACHED_PATHS];
+
+/** Oldest path cache entry index */
+static unsigned int ib_path_cache_idx;
+
+/**
+ * Find path cache entry
+ *
+ * @v ibdev		Infiniband device
+ * @v dgid		Destination GID
+ * @ret path		Path cache entry, or NULL
+ */
+static struct ib_cached_path *
+ib_find_path_cache_entry ( struct ib_device *ibdev, struct ib_gid *dgid ) {
+	struct ib_cached_path *cached;
+	unsigned int i;
+
+	for ( i = 0 ; i < IB_NUM_CACHED_PATHS ; i++ ) {
+		cached = &ib_path_cache[i];
+		if ( ! cached->path )
+			continue;
+		if ( cached->path->ibdev != ibdev )
+			continue;
+		if ( memcmp ( &cached->path->av.gid, dgid,
+			      sizeof ( cached->path->av.gid ) ) != 0 )
+			continue;
+		return cached;
+	}
+
+	return NULL;
+}
+
+/**
+ * Handle cached path transaction completion
+ *
+ * @v ibdev		Infiniband device
+ * @v path		Path
+ * @v rc		Status code
+ * @v av		Address vector, or NULL on error
+ */
+static void ib_cached_path_complete ( struct ib_device *ibdev,
+				      struct ib_path *path, int rc,
+				      struct ib_address_vector *av __unused ) {
+	struct ib_cached_path *cached = ib_path_get_ownerdata ( path );
+
+	/* If the transaction failed, erase the cache entry */
+	if ( rc != 0 ) {
+		/* Destroy the old cache entry */
+		ib_destroy_path ( ibdev, path );
+		memset ( cached, 0, sizeof ( *cached ) );
+		return;
+	}
+
+	/* Do not destroy the completed transaction; we still need to
+	 * refer to the resolved path.
+	 */
+}
+
+/** Cached path transaction completion operations */
+static struct ib_path_operations ib_cached_path_op = {
+	.complete = ib_cached_path_complete,
+};
+
+/**
+ * Resolve path
+ *
+ * @v ibdev		Infiniband device
+ * @v av		Address vector to complete
+ * @ret rc		Return status code
+ *
+ * This provides a non-transactional way to resolve a path, via a
+ * cache similar to ARP.
+ */
+int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) {
+	struct ib_gid *gid = &av->gid;
+	struct ib_cached_path *cached;
+	unsigned int cache_idx;
+
+	/* Sanity check */
+	if ( ! av->gid_present ) {
+		DBGC ( ibdev, "IBDEV %p attempt to look up path "
+		       "without GID\n", ibdev );
+		return -EINVAL;
+	}
+
+	/* Look in cache for a matching entry */
+	cached = ib_find_path_cache_entry ( ibdev, gid );
+	if ( cached && cached->path->av.lid ) {
+		/* Populated entry found */
+		av->lid = cached->path->av.lid;
+		av->rate = cached->path->av.rate;
+		av->sl = cached->path->av.sl;
+		DBGC2 ( ibdev, "IBDEV %p cache hit for %08x:%08x:%08x:%08x\n",
+			ibdev, htonl ( gid->u.dwords[0] ),
+			htonl ( gid->u.dwords[1] ), htonl ( gid->u.dwords[2] ),
+			htonl ( gid->u.dwords[3] ) );
+		return 0;
+	}
+	DBGC ( ibdev, "IBDEV %p cache miss for %08x:%08x:%08x:%08x%s\n",
+	       ibdev, htonl ( gid->u.dwords[0] ), htonl ( gid->u.dwords[1] ),
+	       htonl ( gid->u.dwords[2] ), htonl ( gid->u.dwords[3] ),
+	       ( cached ? " (in progress)" : "" ) );
+
+	/* If lookup is already in progress, do nothing */
+	if ( cached )
+		return -ENOENT;
+
+	/* Locate a new cache entry to use */
+	cache_idx = ( (ib_path_cache_idx++) % IB_NUM_CACHED_PATHS );
+	cached = &ib_path_cache[cache_idx];
+
+	/* Destroy the old cache entry */
+	if ( cached->path )
+		ib_destroy_path ( ibdev, cached->path );
+	memset ( cached, 0, sizeof ( *cached ) );
+
+	/* Create new path */
+	cached->path = ib_create_path ( ibdev, av, &ib_cached_path_op );
+	if ( ! cached->path ) {
+		DBGC ( ibdev, "IBDEV %p could not create path\n",
+		       ibdev );
+		return -ENOMEM;
+	}
+	ib_path_set_ownerdata ( cached->path, cached );
+
+	/* Not found yet */
+	return -ENOENT;
+}
diff --git a/gpxe/src/net/infiniband/ib_sma.c b/gpxe/src/net/infiniband/ib_sma.c
new file mode 100644
index 0000000..ff4cbbf
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_sma.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2009 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <gpxe/settings.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/ib_mi.h>
+#include <gpxe/ib_sma.h>
+
+/**
+ * @file
+ *
+ * Infiniband Subnet Management Agent
+ *
+ */
+
+/**
+ * Node information
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @v av		Source address vector
+ */
+static void ib_sma_node_info ( struct ib_device *ibdev,
+			       struct ib_mad_interface *mi,
+			       union ib_mad *mad,
+			       struct ib_address_vector *av ) {
+	struct ib_node_info *node_info = &mad->smp.smp_data.node_info;
+	int rc;
+
+	/* Fill in information */
+	memset ( node_info, 0, sizeof ( *node_info ) );
+	node_info->base_version = IB_MGMT_BASE_VERSION;
+	node_info->class_version = IB_SMP_CLASS_VERSION;
+	node_info->node_type = IB_NODE_TYPE_HCA;
+	node_info->num_ports = ib_get_hca_info ( ibdev, &node_info->sys_guid );
+	memcpy ( &node_info->node_guid, &node_info->sys_guid,
+		 sizeof ( node_info->node_guid ) );
+	memcpy ( &node_info->port_guid, &ibdev->gid.u.half[1],
+		 sizeof ( node_info->port_guid ) );
+	node_info->partition_cap = htons ( 1 );
+	node_info->local_port_num = ibdev->port;
+
+	/* Send GetResponse */
+	mad->hdr.method = IB_MGMT_METHOD_GET_RESP;
+	if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) {
+		DBGC ( mi, "SMA %p could not send NodeInfo GetResponse: %s\n",
+		       mi, strerror ( rc ) );
+		return;
+	}
+}
+
+/**
+ * Node description
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @v av		Source address vector
+ */
+static void ib_sma_node_desc ( struct ib_device *ibdev,
+			       struct ib_mad_interface *mi,
+			       union ib_mad *mad,
+			       struct ib_address_vector *av ) {
+	struct ib_node_desc *node_desc = &mad->smp.smp_data.node_desc;
+	struct ib_gid_half guid;
+	char hostname[ sizeof ( node_desc->node_string ) ];
+	int hostname_len;
+	int rc;
+
+	/* Fill in information */
+	memset ( node_desc, 0, sizeof ( *node_desc ) );
+	ib_get_hca_info ( ibdev, &guid );
+	hostname_len = fetch_string_setting ( NULL, &hostname_setting,
+					      hostname, sizeof ( hostname ) );
+	snprintf ( node_desc->node_string, sizeof ( node_desc->node_string ),
+		   "gPXE %s%s%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)",
+		   hostname, ( ( hostname_len >= 0 ) ? " " : "" ),
+		   guid.u.bytes[0], guid.u.bytes[1], guid.u.bytes[2],
+		   guid.u.bytes[3], guid.u.bytes[4], guid.u.bytes[5],
+		   guid.u.bytes[6], guid.u.bytes[7], ibdev->dev->name );
+
+	/* Send GetResponse */
+	mad->hdr.method = IB_MGMT_METHOD_GET_RESP;
+	if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) {
+		DBGC ( mi, "SMA %p could not send NodeDesc GetResponse: %s\n",
+		       mi, strerror ( rc ) );
+		return;
+	}
+}
+
+/**
+ * GUID information
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @v av		Source address vector
+ */
+static void ib_sma_guid_info ( struct ib_device *ibdev,
+			       struct ib_mad_interface *mi,
+			       union ib_mad *mad,
+			       struct ib_address_vector *av ) {
+	struct ib_guid_info *guid_info = &mad->smp.smp_data.guid_info;
+	int rc;
+
+	/* Fill in information */
+	memset ( guid_info, 0, sizeof ( *guid_info ) );
+	memcpy ( guid_info->guid[0], &ibdev->gid.u.half[1],
+		 sizeof ( guid_info->guid[0] ) );
+
+	/* Send GetResponse */
+	mad->hdr.method = IB_MGMT_METHOD_GET_RESP;
+	if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) {
+		DBGC ( mi, "SMA %p could not send GuidInfo GetResponse: %s\n",
+		       mi, strerror ( rc ) );
+		return;
+	}
+}
+
+/**
+ * Set port information
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @ret rc		Return status code
+ */
+static int ib_sma_set_port_info ( struct ib_device *ibdev,
+				  struct ib_mad_interface *mi,
+				  union ib_mad *mad ) {
+	const struct ib_port_info *port_info = &mad->smp.smp_data.port_info;
+	unsigned int link_width_enabled;
+	unsigned int link_speed_enabled;
+	int rc;
+
+	/* Set parameters */
+	memcpy ( &ibdev->gid.u.half[0], port_info->gid_prefix,
+		 sizeof ( ibdev->gid.u.half[0] ) );
+	ibdev->lid = ntohs ( port_info->lid );
+	ibdev->sm_lid = ntohs ( port_info->mastersm_lid );
+	if ( ( link_width_enabled = port_info->link_width_enabled ) )
+		ibdev->link_width_enabled = link_width_enabled;
+	if ( ( link_speed_enabled =
+	       ( port_info->link_speed_active__link_speed_enabled & 0xf ) ) )
+		ibdev->link_speed_enabled = link_speed_enabled;
+	ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf );
+	DBGC ( mi, "SMA %p set LID %04x SMLID %04x link width %02x speed "
+	       "%02x\n", mi, ibdev->lid, ibdev->sm_lid,
+	       ibdev->link_width_enabled, ibdev->link_speed_enabled );
+
+	/* Update parameters on device */
+	if ( ( rc = ib_set_port_info ( ibdev, mad ) ) != 0 ) {
+		DBGC ( mi, "SMA %p could not set port information: %s\n",
+		       mi, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * Port information
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @v av		Source address vector
+ */
+static void ib_sma_port_info ( struct ib_device *ibdev,
+			       struct ib_mad_interface *mi,
+			       union ib_mad *mad,
+			       struct ib_address_vector *av ) {
+	struct ib_port_info *port_info = &mad->smp.smp_data.port_info;
+	int rc;
+
+	/* Set parameters if applicable */
+	if ( mad->hdr.method == IB_MGMT_METHOD_SET ) {
+		if ( ( rc = ib_sma_set_port_info ( ibdev, mi, mad ) ) != 0 ) {
+			mad->hdr.status =
+			      htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR );
+			/* Fall through to generate GetResponse */
+		}
+	}
+
+	/* Fill in information */
+	memset ( port_info, 0, sizeof ( *port_info ) );
+	memcpy ( port_info->gid_prefix, &ibdev->gid.u.half[0],
+		 sizeof ( port_info->gid_prefix ) );
+	port_info->lid = ntohs ( ibdev->lid );
+	port_info->mastersm_lid = ntohs ( ibdev->sm_lid );
+	port_info->local_port_num = ibdev->port;
+	port_info->link_width_enabled = ibdev->link_width_enabled;
+	port_info->link_width_supported = ibdev->link_width_supported;
+	port_info->link_width_active = ibdev->link_width_active;
+	port_info->link_speed_supported__port_state =
+		( ( ibdev->link_speed_supported << 4 ) | ibdev->port_state );
+	port_info->port_phys_state__link_down_def_state =
+		( ( IB_PORT_PHYS_STATE_POLLING << 4 ) |
+		  IB_PORT_PHYS_STATE_POLLING );
+	port_info->link_speed_active__link_speed_enabled =
+		( ( ibdev->link_speed_active << 4 ) |
+		  ibdev->link_speed_enabled );
+	port_info->neighbour_mtu__mastersm_sl =
+		( ( IB_MTU_2048 << 4 ) | ibdev->sm_sl );
+	port_info->vl_cap__init_type = ( IB_VL_0 << 4 );
+	port_info->init_type_reply__mtu_cap = IB_MTU_2048;
+	port_info->operational_vls__enforcement = ( IB_VL_0 << 4 );
+	port_info->guid_cap = 1;
+
+	/* Send GetResponse */
+	mad->hdr.method = IB_MGMT_METHOD_GET_RESP;
+	if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) {
+		DBGC ( mi, "SMA %p could not send PortInfo GetResponse: %s\n",
+		       mi, strerror ( rc ) );
+		return;
+	}
+}
+
+/**
+ * Set partition key table
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @ret rc		Return status code
+ */
+static int ib_sma_set_pkey_table ( struct ib_device *ibdev,
+				   struct ib_mad_interface *mi,
+				   union ib_mad *mad ) {
+	struct ib_pkey_table *pkey_table = &mad->smp.smp_data.pkey_table;
+	int rc;
+
+	/* Set parameters */
+	ibdev->pkey = ntohs ( pkey_table->pkey[0] );
+	DBGC ( mi, "SMA %p set pkey %04x\n", mi, ibdev->pkey );
+
+	/* Update parameters on device */
+	if ( ( rc = ib_set_pkey_table ( ibdev, mad ) ) != 0 ) {
+		DBGC ( mi, "SMA %p could not set pkey table: %s\n",
+		       mi, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * Partition key table
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @v mad		Received MAD
+ * @v av		Source address vector
+ */
+static void ib_sma_pkey_table ( struct ib_device *ibdev,
+				struct ib_mad_interface *mi,
+				union ib_mad *mad,
+				struct ib_address_vector *av ) {
+	struct ib_pkey_table *pkey_table = &mad->smp.smp_data.pkey_table;
+	int rc;
+
+	/* Set parameters, if applicable */
+	if ( mad->hdr.method == IB_MGMT_METHOD_SET ) {
+		if ( ( rc = ib_sma_set_pkey_table ( ibdev, mi, mad ) ) != 0 ) {
+			mad->hdr.status =
+			      htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR );
+			/* Fall through to generate GetResponse */
+		}
+	}
+
+	/* Fill in information */
+	mad->hdr.method = IB_MGMT_METHOD_GET_RESP;
+	memset ( pkey_table, 0, sizeof ( *pkey_table ) );
+	pkey_table->pkey[0] = htons ( ibdev->pkey );
+
+	/* Send GetResponse */
+	mad->hdr.method = IB_MGMT_METHOD_GET_RESP;
+	if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) {
+		DBGC ( mi, "SMA %p could not send PKeyTable GetResponse: %s\n",
+		       mi, strerror ( rc ) );
+		return;
+	}
+}
+
+/** Subnet management agent */
+struct ib_mad_agent ib_sma_agent[] __ib_mad_agent = {
+	{
+		.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED,
+		.class_version = IB_SMP_CLASS_VERSION,
+		.attr_id = htons ( IB_SMP_ATTR_NODE_INFO ),
+		.handle = ib_sma_node_info,
+	},
+	{
+		.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED,
+		.class_version = IB_SMP_CLASS_VERSION,
+		.attr_id = htons ( IB_SMP_ATTR_NODE_DESC ),
+		.handle = ib_sma_node_desc,
+	},
+	{
+		.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED,
+		.class_version = IB_SMP_CLASS_VERSION,
+		.attr_id = htons ( IB_SMP_ATTR_GUID_INFO ),
+		.handle = ib_sma_guid_info,
+	},
+	{
+		.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED,
+		.class_version = IB_SMP_CLASS_VERSION,
+		.attr_id = htons ( IB_SMP_ATTR_PORT_INFO ),
+		.handle = ib_sma_port_info,
+	},
+	{
+		.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED,
+		.class_version = IB_SMP_CLASS_VERSION,
+		.attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE ),
+		.handle = ib_sma_pkey_table,
+	},
+};
+
+/**
+ * Create subnet management agent and interface
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ * @ret rc		Return status code
+ */
+int ib_create_sma ( struct ib_device *ibdev, struct ib_mad_interface *mi ) {
+
+	/* Nothing to do */
+	DBGC ( ibdev, "IBDEV %p SMA using SMI %p\n", ibdev, mi );
+
+	return 0;
+}
+
+/**
+ * Destroy subnet management agent and interface
+ *
+ * @v ibdev		Infiniband device
+ * @v mi		Management interface
+ */
+void ib_destroy_sma ( struct ib_device *ibdev __unused,
+		      struct ib_mad_interface *mi __unused ) {
+	/* Nothing to do */
+}
diff --git a/gpxe/src/net/infiniband/ib_smc.c b/gpxe/src/net/infiniband/ib_smc.c
new file mode 100644
index 0000000..d308dd9
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_smc.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown at fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ib_smc.h>
+
+/**
+ * @file
+ *
+ * Infiniband Subnet Management Client
+ *
+ */
+
+/**
+ * Get port information
+ *
+ * @v ibdev		Infiniband device
+ * @v local_mad		Method for issuing local MADs
+ * @v mad		Management datagram to fill in
+ * @ret rc		Return status code
+ */
+static int ib_smc_get_port_info ( struct ib_device *ibdev,
+				  ib_local_mad_t local_mad,
+				  union ib_mad *mad ) {
+	int rc;
+
+	/* Construct MAD */
+	memset ( mad, 0, sizeof ( *mad ) );
+	mad->hdr.base_version = IB_MGMT_BASE_VERSION;
+	mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+	mad->hdr.class_version = 1;
+	mad->hdr.method = IB_MGMT_METHOD_GET;
+	mad->hdr.attr_id = htons ( IB_SMP_ATTR_PORT_INFO );
+	mad->hdr.attr_mod = htonl ( ibdev->port );
+
+	if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p could not get port info: %s\n",
+		       ibdev, strerror ( rc ) );
+		return rc;
+	}
+	return 0;
+}
+
+/**
+ * Get GUID information
+ *
+ * @v ibdev		Infiniband device
+ * @v local_mad		Method for issuing local MADs
+ * @v mad		Management datagram to fill in
+ * @ret rc		Return status code
+ */
+static int ib_smc_get_guid_info ( struct ib_device *ibdev,
+				  ib_local_mad_t local_mad,
+				  union ib_mad *mad ) {
+	int rc;
+
+	/* Construct MAD */
+	memset ( mad, 0, sizeof ( *mad ) );
+	mad->hdr.base_version = IB_MGMT_BASE_VERSION;
+	mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+	mad->hdr.class_version = 1;
+	mad->hdr.method = IB_MGMT_METHOD_GET;
+	mad->hdr.attr_id = htons ( IB_SMP_ATTR_GUID_INFO );
+
+	if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n",
+		       ibdev, strerror ( rc ) );
+		return rc;
+	}
+	return 0;
+}
+
+/**
+ * Get partition key table
+ *
+ * @v ibdev		Infiniband device
+ * @v local_mad		Method for issuing local MADs
+ * @v mad		Management datagram to fill in
+ * @ret rc		Return status code
+ */
+static int ib_smc_get_pkey_table ( struct ib_device *ibdev,
+				   ib_local_mad_t local_mad,
+				   union ib_mad *mad ) {
+	int rc;
+
+	/* Construct MAD */
+	memset ( mad, 0, sizeof ( *mad ) );
+	mad->hdr.base_version = IB_MGMT_BASE_VERSION;
+	mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+	mad->hdr.class_version = 1;
+	mad->hdr.method = IB_MGMT_METHOD_GET;
+	mad->hdr.attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE );
+
+	if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
+		DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n",
+		       ibdev, strerror ( rc ) );
+		return rc;
+	}
+	return 0;
+}
+
+/**
+ * Get MAD parameters
+ *
+ * @v ibdev		Infiniband device
+ * @v local_mad		Method for issuing local MADs
+ * @ret rc		Return status code
+ */
+int ib_smc_update ( struct ib_device *ibdev, ib_local_mad_t local_mad ) {
+	union ib_mad mad;
+	struct ib_port_info *port_info = &mad.smp.smp_data.port_info;
+	struct ib_guid_info *guid_info = &mad.smp.smp_data.guid_info;
+	struct ib_pkey_table *pkey_table = &mad.smp.smp_data.pkey_table;
+	int rc;
+
+	/* Port info gives us the link state, the first half of the
+	 * port GID and the SM LID.
+	 */
+	if ( ( rc = ib_smc_get_port_info ( ibdev, local_mad, &mad ) ) != 0 )
+		return rc;
+	memcpy ( &ibdev->gid.u.half[0], port_info->gid_prefix,
+		 sizeof ( ibdev->gid.u.half[0] ) );
+	ibdev->lid = ntohs ( port_info->lid );
+	ibdev->sm_lid = ntohs ( port_info->mastersm_lid );
+	ibdev->link_width_enabled = port_info->link_width_enabled;
+	ibdev->link_width_supported = port_info->link_width_supported;
+	ibdev->link_width_active = port_info->link_width_active;
+	ibdev->link_speed_supported =
+		( port_info->link_speed_supported__port_state >> 4 );
+	ibdev->port_state =
+		( port_info->link_speed_supported__port_state & 0xf );
+	ibdev->link_speed_active =
+		( port_info->link_speed_active__link_speed_enabled >> 4 );
+	ibdev->link_speed_enabled =
+		( port_info->link_speed_active__link_speed_enabled & 0xf );
+	ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf );
+
+	/* GUID info gives us the second half of the port GID */
+	if ( ( rc = ib_smc_get_guid_info ( ibdev, local_mad, &mad ) ) != 0 )
+		return rc;
+	memcpy ( &ibdev->gid.u.half[1], guid_info->guid[0],
+		 sizeof ( ibdev->gid.u.half[1] ) );
+
+	/* Get partition key */
+	if ( ( rc = ib_smc_get_pkey_table ( ibdev, local_mad, &mad ) ) != 0 )
+		return rc;
+	ibdev->pkey = ntohs ( pkey_table->pkey[0] );
+
+	DBGC ( ibdev, "IBDEV %p port GID is %08x:%08x:%08x:%08x\n", ibdev,
+	       htonl ( ibdev->gid.u.dwords[0] ),
+	       htonl ( ibdev->gid.u.dwords[1] ),
+	       htonl ( ibdev->gid.u.dwords[2] ),
+	       htonl ( ibdev->gid.u.dwords[3] ) );
+
+	return 0;
+}
diff --git a/gpxe/src/net/infiniband/ib_srp.c b/gpxe/src/net/infiniband/ib_srp.c
new file mode 100644
index 0000000..c156d3a
--- /dev/null
+++ b/gpxe/src/net/infiniband/ib_srp.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2009 Fen Systems Ltd <mbrown at fensystems.co.uk>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdlib.h>
+#include <errno.h>
+#include <gpxe/srp.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ib_cmrc.h>
+#include <gpxe/ib_srp.h>
+
+/**
+ * @file
+ *
+ * SCSI RDMA Protocol over Infiniband
+ *
+ */
+
+/* Disambiguate the various possible EINVALs */
+#define EINVAL_BYTE_STRING_LEN ( EINVAL | EUNIQ_01 )
+#define EINVAL_BYTE_STRING ( EINVAL | EUNIQ_02 )
+#define EINVAL_INTEGER ( EINVAL | EUNIQ_03 )
+#define EINVAL_RP_TOO_SHORT ( EINVAL | EUNIQ_04 )
+
+/** IB SRP parse flags */
+enum ib_srp_parse_flags {
+	IB_SRP_PARSE_REQUIRED = 0x0000,
+	IB_SRP_PARSE_OPTIONAL = 0x8000,
+	IB_SRP_PARSE_FLAG_MASK = 0xf000,
+};
+
+/** IB SRP root path parameters */
+struct ib_srp_root_path {
+	/** SCSI LUN */
+	struct scsi_lun *lun;
+	/** SRP port IDs */
+	struct srp_port_ids *port_ids;
+	/** IB SRP parameters */
+	struct ib_srp_parameters *ib;
+};
+
+/**
+ * Parse IB SRP root path byte-string value
+ *
+ * @v rp_comp		Root path component string
+ * @v default_value	Default value to use if component string is empty
+ * @ret value		Value
+ */
+static int ib_srp_parse_byte_string ( const char *rp_comp, uint8_t *bytes,
+				      unsigned int size_flags ) {
+	size_t size = ( size_flags & ~IB_SRP_PARSE_FLAG_MASK );
+	size_t rp_comp_len = strlen ( rp_comp );
+	char buf[3];
+	char *buf_end;
+
+	/* Allow optional components to be empty */
+	if ( ( rp_comp_len == 0 ) &&
+	     ( size_flags & IB_SRP_PARSE_OPTIONAL ) )
+		return 0;
+
+	/* Check string length */
+	if ( rp_comp_len != ( 2 * size ) )
+		return -EINVAL_BYTE_STRING_LEN;
+
+	/* Parse byte string */
+	for ( ; size ; size--, rp_comp += 2, bytes++ ) {
+		memcpy ( buf, rp_comp, 2 );
+		buf[2] = '\0';
+		*bytes = strtoul ( buf, &buf_end, 16 );
+		if ( buf_end != &buf[2] )
+			return -EINVAL_BYTE_STRING;
+	}
+	return 0;
+}
+
+/**
+ * Parse IB SRP root path integer value
+ *
+ * @v rp_comp		Root path component string
+ * @v default_value	Default value to use if component string is empty
+ * @ret value		Value
+ */
+static int ib_srp_parse_integer ( const char *rp_comp, int default_value ) {
+	int value;
+	char *end;
+
+	value = strtoul ( rp_comp, &end, 16 );
+	if ( *end )
+		return -EINVAL_INTEGER;
+
+	if ( end == rp_comp )
+		return default_value;
+
+	return value;
+}
+
+/**
+ * Parse IB SRP root path literal component
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_literal ( const char *rp_comp __unused,
+				  struct ib_srp_root_path *rp __unused ) {
+	/* Ignore */
+	return 0;
+}
+
+/**
+ * Parse IB SRP root path source GID
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_sgid ( const char *rp_comp,
+			       struct ib_srp_root_path *rp ) {
+	struct ib_device *ibdev;
+
+	/* Default to the GID of the last opened Infiniband device */
+	if ( ( ibdev = last_opened_ibdev() ) != NULL )
+		memcpy ( &rp->ib->sgid, &ibdev->gid, sizeof ( rp->ib->sgid ) );
+
+	return ib_srp_parse_byte_string ( rp_comp, rp->ib->sgid.u.bytes,
+					  ( sizeof ( rp->ib->sgid ) |
+					    IB_SRP_PARSE_OPTIONAL ) );
+}
+
+/**
+ * Parse IB SRP root path initiator identifier extension
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_initiator_id_ext ( const char *rp_comp,
+					   struct ib_srp_root_path *rp ) {
+	struct ib_srp_initiator_port_id *port_id =
+		ib_srp_initiator_port_id ( rp->port_ids );
+
+	return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes,
+					  ( sizeof ( port_id->id_ext ) |
+					    IB_SRP_PARSE_OPTIONAL ) );
+}
+
+/**
+ * Parse IB SRP root path initiator HCA GUID
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_initiator_hca_guid ( const char *rp_comp,
+					     struct ib_srp_root_path *rp ) {
+	struct ib_srp_initiator_port_id *port_id =
+		ib_srp_initiator_port_id ( rp->port_ids );
+
+	/* Default to the GUID portion of the source GID */
+	memcpy ( &port_id->hca_guid, &rp->ib->sgid.u.half[1],
+		 sizeof ( port_id->hca_guid ) );
+
+	return ib_srp_parse_byte_string ( rp_comp, port_id->hca_guid.u.bytes,
+					  ( sizeof ( port_id->hca_guid ) |
+					    IB_SRP_PARSE_OPTIONAL ) );
+}
+
+/**
+ * Parse IB SRP root path destination GID
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_dgid ( const char *rp_comp,
+			       struct ib_srp_root_path *rp ) {
+	return ib_srp_parse_byte_string ( rp_comp, rp->ib->dgid.u.bytes,
+					  ( sizeof ( rp->ib->dgid ) |
+					    IB_SRP_PARSE_REQUIRED ) );
+}
+
+/**
+ * Parse IB SRP root path partition key
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_pkey ( const char *rp_comp,
+			       struct ib_srp_root_path *rp ) {
+	int pkey;
+
+	if ( ( pkey = ib_srp_parse_integer ( rp_comp, IB_PKEY_DEFAULT ) ) < 0 )
+		return pkey;
+	rp->ib->pkey = pkey;
+	return 0;
+}
+
+/**
+ * Parse IB SRP root path service ID
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_service_id ( const char *rp_comp,
+				     struct ib_srp_root_path *rp ) {
+	return ib_srp_parse_byte_string ( rp_comp, rp->ib->service_id.u.bytes,
+					  ( sizeof ( rp->ib->service_id ) |
+					    IB_SRP_PARSE_REQUIRED ) );
+}
+
+/**
+ * Parse IB SRP root path LUN
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_lun ( const char *rp_comp,
+			      struct ib_srp_root_path *rp ) {
+	return scsi_parse_lun ( rp_comp, rp->lun );
+}
+
+/**
+ * Parse IB SRP root path target identifier extension
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_target_id_ext ( const char *rp_comp,
+					struct ib_srp_root_path *rp ) {
+	struct ib_srp_target_port_id *port_id =
+		ib_srp_target_port_id ( rp->port_ids );
+
+	return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes,
+					  ( sizeof ( port_id->id_ext ) |
+					    IB_SRP_PARSE_REQUIRED ) );
+}
+
+/**
+ * Parse IB SRP root path target I/O controller GUID
+ *
+ * @v rp_comp		Root path component string
+ * @v rp		IB SRP root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_target_ioc_guid ( const char *rp_comp,
+					  struct ib_srp_root_path *rp ) {
+	struct ib_srp_target_port_id *port_id =
+		ib_srp_target_port_id ( rp->port_ids );
+
+	return ib_srp_parse_byte_string ( rp_comp, port_id->ioc_guid.u.bytes,
+					  ( sizeof ( port_id->ioc_guid ) |
+					    IB_SRP_PARSE_REQUIRED ) );
+}
+
+/** IB SRP root path component parser */
+struct ib_srp_root_path_parser {
+	/**
+	 * Parse IB SRP root path component
+	 *
+	 * @v rp_comp		Root path component string
+	 * @v rp		IB SRP root path
+	 * @ret rc		Return status code
+	 */
+	int ( * parse ) ( const char *rp_comp, struct ib_srp_root_path *rp );
+};
+
+/** IB SRP root path components */
+static struct ib_srp_root_path_parser ib_srp_rp_parser[] = {
+	{ ib_srp_parse_literal },
+	{ ib_srp_parse_sgid },
+	{ ib_srp_parse_initiator_id_ext },
+	{ ib_srp_parse_initiator_hca_guid },
+	{ ib_srp_parse_dgid },
+	{ ib_srp_parse_pkey },
+	{ ib_srp_parse_service_id },
+	{ ib_srp_parse_lun },
+	{ ib_srp_parse_target_id_ext },
+	{ ib_srp_parse_target_ioc_guid },
+};
+
+/** Number of IB SRP root path components */
+#define IB_SRP_NUM_RP_COMPONENTS \
+	( sizeof ( ib_srp_rp_parser ) / sizeof ( ib_srp_rp_parser[0] ) )
+
+/**
+ * Parse IB SRP root path
+ *
+ * @v srp		SRP device
+ * @v rp_string		Root path
+ * @ret rc		Return status code
+ */
+static int ib_srp_parse_root_path ( struct srp_device *srp,
+				    const char *rp_string ) {
+	struct ib_srp_parameters *ib_params = ib_srp_params ( srp );
+	struct ib_srp_root_path rp = {
+		.lun = &srp->lun,
+		.port_ids = &srp->port_ids,
+		.ib = ib_params,
+	};
+	char rp_string_copy[ strlen ( rp_string ) + 1 ];
+	char *rp_comp[IB_SRP_NUM_RP_COMPONENTS];
+	char *rp_string_tmp = rp_string_copy;
+	unsigned int i = 0;
+	int rc;
+
+	/* Split root path into component parts */
+	strcpy ( rp_string_copy, rp_string );
+	while ( 1 ) {
+		rp_comp[i++] = rp_string_tmp;
+		if ( i == IB_SRP_NUM_RP_COMPONENTS )
+			break;
+		for ( ; *rp_string_tmp != ':' ; rp_string_tmp++ ) {
+			if ( ! *rp_string_tmp ) {
+				DBGC ( srp, "SRP %p root path \"%s\" too "
+				       "short\n", srp, rp_string );
+				return -EINVAL_RP_TOO_SHORT;
+			}
+		}
+		*(rp_string_tmp++) = '\0';
+	}
+
+	/* Parse root path components */
+	for ( i = 0 ; i < IB_SRP_NUM_RP_COMPONENTS ; i++ ) {
+		if ( ( rc = ib_srp_rp_parser[i].parse ( rp_comp[i],
+							&rp ) ) != 0 ) {
+			DBGC ( srp, "SRP %p could not parse \"%s\" in root "
+			       "path \"%s\": %s\n", srp, rp_comp[i],
+			       rp_string, strerror ( rc ) );
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Connect IB SRP session
+ *
+ * @v srp		SRP device
+ * @ret rc		Return status code
+ */
+static int ib_srp_connect ( struct srp_device *srp ) {
+	struct ib_srp_parameters *ib_params = ib_srp_params ( srp );
+	struct ib_device *ibdev;
+	int rc;
+
+	/* Identify Infiniband device */
+	ibdev = find_ibdev ( &ib_params->sgid );
+	if ( ! ibdev ) {
+		DBGC ( srp, "SRP %p could not identify Infiniband device\n",
+		       srp );
+		return -ENODEV;
+	}
+
+	/* Configure remaining SRP parameters */
+	srp->memory_handle = ibdev->rdma_key;
+
+	/* Open CMRC socket */
+	if ( ( rc = ib_cmrc_open ( &srp->socket, ibdev, &ib_params->dgid,
+				   &ib_params->service_id ) ) != 0 ) {
+		DBGC ( srp, "SRP %p could not open CMRC socket: %s\n",
+		       srp, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
+}
+
+/** IB SRP transport type */
+struct srp_transport_type ib_srp_transport = {
+	.priv_len = sizeof ( struct ib_srp_parameters ),
+	.parse_root_path = ib_srp_parse_root_path,
+	.connect = ib_srp_connect,
+};
diff --git a/gpxe/src/net/iobpad.c b/gpxe/src/net/iobpad.c
index 9961edc..cbae221 100644
--- a/gpxe/src/net/iobpad.c
+++ b/gpxe/src/net/iobpad.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/net/ipv4.c b/gpxe/src/net/ipv4.c
index 8668d44..4c1393f 100644
--- a/gpxe/src/net/ipv4.c
+++ b/gpxe/src/net/ipv4.c
@@ -21,6 +21,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /* Unique IP datagram identification number */
 static uint16_t next_ident = 0;
 
@@ -38,7 +40,7 @@ static LIST_HEAD ( frag_buffers );
  * @v netdev		Network device
  * @v address		IPv4 address
  * @v netmask		Subnet mask
- * @v gateway		Gateway address (or @c INADDR_NONE for no gateway)
+ * @v gateway		Gateway address (if any)
  * @ret miniroute	Routing table entry, or NULL
  */
 static struct ipv4_miniroute * __malloc
@@ -48,7 +50,7 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address,
 
 	DBG ( "IPv4 add %s", inet_ntoa ( address ) );
 	DBG ( "/%s ", inet_ntoa ( netmask ) );
-	if ( gateway.s_addr != INADDR_NONE )
+	if ( gateway.s_addr )
 		DBG ( "gw %s ", inet_ntoa ( gateway ) );
 	DBG ( "via %s\n", netdev->name );
 
@@ -68,7 +70,7 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address,
 	/* Add to end of list if we have a gateway, otherwise
 	 * to start of list.
 	 */
-	if ( gateway.s_addr != INADDR_NONE ) {
+	if ( gateway.s_addr ) {
 		list_add_tail ( &miniroute->list, &ipv4_miniroutes );
 	} else {
 		list_add ( &miniroute->list, &ipv4_miniroutes );
@@ -86,7 +88,7 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
 
 	DBG ( "IPv4 del %s", inet_ntoa ( miniroute->address ) );
 	DBG ( "/%s ", inet_ntoa ( miniroute->netmask ) );
-	if ( miniroute->gateway.s_addr != INADDR_NONE )
+	if ( miniroute->gateway.s_addr )
 		DBG ( "gw %s ", inet_ntoa ( miniroute->gateway ) );
 	DBG ( "via %s\n", miniroute->netdev->name );
 
@@ -116,9 +118,11 @@ static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
 
 	/* Find first usable route in routing table */
 	list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
+		if ( ! ( miniroute->netdev->state & NETDEV_OPEN ) )
+			continue;
 		local = ( ( ( dest->s_addr ^ miniroute->address.s_addr )
 			    & miniroute->netmask.s_addr ) == 0 );
-		has_gw = ( miniroute->gateway.s_addr != INADDR_NONE );
+		has_gw = ( miniroute->gateway.s_addr );
 		if ( local || has_gw ) {
 			if ( ! local )
 				*dest = miniroute->gateway;
@@ -269,7 +273,7 @@ static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
 
 	if ( dest.s_addr == INADDR_BROADCAST ) {
 		/* Broadcast address */
-		memcpy ( ll_dest, ll_protocol->ll_broadcast,
+		memcpy ( ll_dest, netdev->ll_broadcast,
 			 ll_protocol->ll_addr_len );
 		return 0;
 	} else if ( IN_MULTICAST ( ntohl ( dest.s_addr ) ) ) {
@@ -584,7 +588,7 @@ static int ipv4_create_routes ( void ) {
 	struct settings *settings;
 	struct in_addr address = { 0 };
 	struct in_addr netmask = { 0 };
-	struct in_addr gateway = { INADDR_NONE };
+	struct in_addr gateway = { 0 };
 
 	/* Delete all existing routes */
 	list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
@@ -598,20 +602,19 @@ static int ipv4_create_routes ( void ) {
 		fetch_ipv4_setting ( settings, &ip_setting, &address );
 		if ( ! address.s_addr )
 			continue;
-		/* Calculate default netmask */
-		if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) {
-			netmask.s_addr = htonl ( IN_CLASSA_NET );
-		} else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) {
-			netmask.s_addr = htonl ( IN_CLASSB_NET );
-		} else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) {
-			netmask.s_addr = htonl ( IN_CLASSC_NET );
-		} else {
-			netmask.s_addr = 0;
-		}
-		/* Override with subnet mask, if present */
+		/* Get subnet mask */
 		fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
+		/* Calculate default netmask, if necessary */
+		if ( ! netmask.s_addr ) {
+			if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) {
+				netmask.s_addr = htonl ( IN_CLASSA_NET );
+			} else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) {
+				netmask.s_addr = htonl ( IN_CLASSB_NET );
+			} else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) {
+				netmask.s_addr = htonl ( IN_CLASSC_NET );
+			}
+		}
 		/* Get default gateway, if present */
-		gateway.s_addr = INADDR_NONE;
 		fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
 		/* Configure route */
 		miniroute = add_ipv4_miniroute ( netdev, address,
diff --git a/gpxe/src/net/mii.c b/gpxe/src/net/mii.c
new file mode 100644
index 0000000..0de6442
--- /dev/null
+++ b/gpxe/src/net/mii.c
@@ -0,0 +1,147 @@
+/*
+
+	mii.c: MII interface library
+
+	Ported to gPXE by Daniel Verkamp <daniel at drv.nu>
+	from Linux drivers/net/mii.c
+
+	Maintained by Jeff Garzik <jgarzik at pobox.com>
+	Copyright 2001,2002 Jeff Garzik
+
+	Various code came from myson803.c and other files by
+	Donald Becker.  Copyright:
+
+		Written 1998-2002 by Donald Becker.
+
+		This software may be used and distributed according
+		to the terms of the GNU General Public License (GPL),
+		incorporated herein by reference.  Drivers based on
+		or derived from this code fall under the GPL and must
+		retain the authorship, copyright and license notice.
+		This file is not a complete program and may only be
+		used when the entire operating system is licensed
+		under the GPL.
+
+		The author may be reached as becker at scyld.com, or C/O
+		Scyld Computing Corporation
+		410 Severn Ave., Suite 210
+		Annapolis MD 21403
+
+*/
+
+#include <mii.h>
+
+/**
+ * mii_link_ok - is link status up/ok
+ * @mii: the MII interface
+ *
+ * Returns 1 if the MII reports link status up/ok, 0 otherwise.
+ */
+int
+mii_link_ok ( struct mii_if_info *mii )
+{
+	/* first, a dummy read, needed to latch some MII phys */
+	mii->mdio_read ( mii->dev, mii->phy_id, MII_BMSR );
+	if ( mii->mdio_read ( mii->dev, mii->phy_id, MII_BMSR ) & BMSR_LSTATUS )
+		return 1;
+	return 0;
+}
+
+/**
+ * mii_check_link - check MII link status
+ * @mii: MII interface
+ *
+ * If the link status changed (previous != current), call
+ * netif_carrier_on() if current link status is Up or call
+ * netif_carrier_off() if current link status is Down.
+ */
+void
+mii_check_link ( struct mii_if_info *mii )
+{
+	int cur_link = mii_link_ok ( mii );
+	int prev_link = netdev_link_ok ( mii->dev );
+
+	if ( cur_link && !prev_link )
+		netdev_link_up ( mii->dev );
+	else if (prev_link && !cur_link)
+		netdev_link_down ( mii->dev );
+}
+
+
+/**
+ * mii_check_media - check the MII interface for a duplex change
+ * @mii: the MII interface
+ * @ok_to_print: OK to print link up/down messages
+ * @init_media: OK to save duplex mode in @mii
+ *
+ * Returns 1 if the duplex mode changed, 0 if not.
+ * If the media type is forced, always returns 0.
+ */
+unsigned int
+mii_check_media ( struct mii_if_info *mii,
+                  unsigned int ok_to_print,
+                  unsigned int init_media )
+{
+	unsigned int old_carrier, new_carrier;
+	int advertise, lpa, media, duplex;
+	int lpa2 = 0;
+
+	/* if forced media, go no further */
+	if (mii->force_media)
+		return 0; /* duplex did not change */
+
+	/* check current and old link status */
+	old_carrier = netdev_link_ok ( mii->dev ) ? 1 : 0;
+	new_carrier = (unsigned int) mii_link_ok ( mii );
+
+	/* if carrier state did not change, this is a "bounce",
+	 * just exit as everything is already set correctly
+	 */
+	if ( ( ! init_media ) && ( old_carrier == new_carrier ) )
+		return 0; /* duplex did not change */
+
+	/* no carrier, nothing much to do */
+	if ( ! new_carrier ) {
+		netdev_link_down ( mii->dev );
+		if ( ok_to_print )
+			DBG ( "%s: link down\n", mii->dev->name);
+		return 0; /* duplex did not change */
+	}
+
+	/*
+	 * we have carrier, see who's on the other end
+	 */
+	netdev_link_up ( mii->dev );
+
+	/* get MII advertise and LPA values */
+	if ( ( ! init_media ) && ( mii->advertising ) ) {
+		advertise = mii->advertising;
+	} else {
+		advertise = mii->mdio_read ( mii->dev, mii->phy_id, MII_ADVERTISE );
+		mii->advertising = advertise;
+	}
+	lpa = mii->mdio_read ( mii->dev, mii->phy_id, MII_LPA );
+	if ( mii->supports_gmii )
+		lpa2 = mii->mdio_read ( mii->dev, mii->phy_id, MII_STAT1000 );
+
+	/* figure out media and duplex from advertise and LPA values */
+	media = mii_nway_result ( lpa & advertise );
+	duplex = ( media & ADVERTISE_FULL ) ? 1 : 0;
+	if ( lpa2 & LPA_1000FULL )
+		duplex = 1;
+
+	if ( ok_to_print )
+		DBG ( "%s: link up, %sMbps, %s-duplex, lpa 0x%04X\n",
+		       mii->dev->name,
+		       lpa2 & ( LPA_1000FULL | LPA_1000HALF ) ? "1000" :
+		       media & ( ADVERTISE_100FULL | ADVERTISE_100HALF ) ? "100" : "10",
+		       duplex ? "full" : "half",
+		       lpa);
+
+	if ( ( init_media ) || ( mii->full_duplex != duplex ) ) {
+		mii->full_duplex = duplex;
+		return 1; /* duplex changed */
+	}
+
+	return 0; /* duplex did not change */
+}
diff --git a/gpxe/src/net/netdev_settings.c b/gpxe/src/net/netdev_settings.c
index 44aca7d..d814193 100644
--- a/gpxe/src/net/netdev_settings.c
+++ b/gpxe/src/net/netdev_settings.c
@@ -16,10 +16,14 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <errno.h>
+#include <byteswap.h>
 #include <gpxe/dhcp.h>
 #include <gpxe/settings.h>
+#include <gpxe/device.h>
 #include <gpxe/netdevice.h>
 
 /** @file
@@ -34,6 +38,11 @@ struct setting mac_setting __setting = {
 	.description = "MAC address",
 	.type = &setting_type_hex,
 };
+struct setting busid_setting __setting = {
+	.name = "busid",
+	.description = "Bus ID",
+	.type = &setting_type_hex,
+};
 
 /**
  * Store value of network device setting
@@ -54,9 +63,9 @@ static int netdev_store ( struct settings *settings, struct setting *setting,
 			return -EINVAL;
 		memcpy ( netdev->ll_addr, data, len );
 		return 0;
-	} else {
-		return simple_settings_store ( settings, setting, data, len );
 	}
+
+	return generic_settings_store ( settings, setting, data, len );
 }
 
 /**
@@ -72,19 +81,40 @@ static int netdev_fetch ( struct settings *settings, struct setting *setting,
 			  void *data, size_t len ) {
 	struct net_device *netdev = container_of ( settings, struct net_device,
 						   settings.settings );
+	struct device_description *desc = &netdev->dev->desc;
+	struct dhcp_netdev_desc dhcp_desc;
 
 	if ( setting_cmp ( setting, &mac_setting ) == 0 ) {
 		if ( len > netdev->ll_protocol->ll_addr_len )
 			len = netdev->ll_protocol->ll_addr_len;
 		memcpy ( data, netdev->ll_addr, len );
 		return netdev->ll_protocol->ll_addr_len;
-	} else {
-		return simple_settings_fetch ( settings, setting, data, len );
 	}
+	if ( setting_cmp ( setting, &busid_setting ) == 0 ) {
+		dhcp_desc.type = desc->bus_type;
+		dhcp_desc.vendor = htons ( desc->vendor );
+		dhcp_desc.device = htons ( desc->device );
+		if ( len > sizeof ( dhcp_desc ) )
+			len = sizeof ( dhcp_desc );
+		memcpy ( data, &dhcp_desc, len );
+		return sizeof ( dhcp_desc );
+	}
+
+	return generic_settings_fetch ( settings, setting, data, len );
+}
+
+/**
+ * Clear network device settings
+ *
+ * @v settings		Settings block
+ */
+static void netdev_clear ( struct settings *settings ) {
+	generic_settings_clear ( settings );
 }
 
 /** Network device configuration settings operations */
 struct settings_operations netdev_settings_operations = {
 	.store = netdev_store,
 	.fetch = netdev_fetch,
+	.clear = netdev_clear,
 };
diff --git a/gpxe/src/net/netdevice.c b/gpxe/src/net/netdevice.c
index 9e142d2..ee0d0b7 100644
--- a/gpxe/src/net/netdevice.c
+++ b/gpxe/src/net/netdevice.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -28,6 +30,7 @@
 #include <gpxe/process.h>
 #include <gpxe/init.h>
 #include <gpxe/device.h>
+#include <gpxe/errortab.h>
 #include <gpxe/netdevice.h>
 
 /** @file
@@ -36,17 +39,39 @@
  *
  */
 
-/** Registered network-layer protocols */
-static struct net_protocol net_protocols[0]
-	__table_start ( struct net_protocol, net_protocols );
-static struct net_protocol net_protocols_end[0]
-	__table_end ( struct net_protocol, net_protocols );
-
 /** List of network devices */
 struct list_head net_devices = LIST_HEAD_INIT ( net_devices );
 
 /** List of open network devices, in reverse order of opening */
-struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
+static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
+
+/** Default link status code */
+#define EUNKNOWN_LINK_STATUS EINPROGRESS
+
+/** Human-readable message for the default link status */
+struct errortab netdev_errors[] __errortab = {
+	{ EUNKNOWN_LINK_STATUS, "Unknown" },
+};
+
+/**
+ * Mark network device as having link down
+ *
+ * @v netdev		Network device
+ */
+void netdev_link_down ( struct net_device *netdev ) {
+
+	switch ( netdev->link_rc ) {
+	case 0:
+	case -EUNKNOWN_LINK_STATUS:
+		netdev->link_rc = -ENOTCONN;
+		break;
+	default:
+		/* Avoid clobbering a more detailed link status code,
+		 * if one is already set.
+		 */
+		break;
+	}
+}
 
 /**
  * Record network device statistic
@@ -286,6 +311,7 @@ static void free_netdev ( struct refcnt *refcnt ) {
 	
 	netdev_tx_flush ( netdev );
 	netdev_rx_flush ( netdev );
+	clear_settings ( netdev_settings ( netdev ) );
 	free ( netdev );
 }
 
@@ -305,11 +331,10 @@ struct net_device * alloc_netdev ( size_t priv_size ) {
 	netdev = zalloc ( total_len );
 	if ( netdev ) {
 		netdev->refcnt.free = free_netdev;
+		netdev->link_rc = -EUNKNOWN_LINK_STATUS;
 		INIT_LIST_HEAD ( &netdev->tx_queue );
 		INIT_LIST_HEAD ( &netdev->rx_queue );
-		settings_init ( netdev_settings ( netdev ),
-				&netdev_settings_operations, &netdev->refcnt,
-				netdev->name, 0 );
+		netdev_settings_init ( netdev );
 		netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) );
 	}
 	return netdev;
@@ -332,6 +357,9 @@ int register_netdev ( struct net_device *netdev ) {
 	snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
 		   ifindex++ );
 
+	/* Set initial link-layer address */
+	netdev->ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
+
 	/* Register per-netdev configuration settings */
 	if ( ( rc = register_settings ( netdev_settings ( netdev ),
 					NULL ) ) != 0 ) {
@@ -345,7 +373,7 @@ int register_netdev ( struct net_device *netdev ) {
 	list_add_tail ( &netdev->list, &net_devices );
 	DBGC ( netdev, "NETDEV %p registered as %s (phys %s hwaddr %s)\n",
 	       netdev, netdev->name, netdev->dev->name,
-	       netdev_hwaddr ( netdev ) );
+	       netdev_addr ( netdev ) );
 
 	return 0;
 }
@@ -514,7 +542,7 @@ int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
 	netdev_poll ( netdev );
 
 	/* Add link-layer header */
-	if ( ( rc = ll_protocol->push ( iobuf, ll_dest, netdev->ll_addr,
+	if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest, netdev->ll_addr,
 					net_protocol->net_proto ) ) != 0 ) {
 		free_iob ( iobuf );
 		return rc;
@@ -538,12 +566,13 @@ int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
 	struct net_protocol *net_protocol;
 
 	/* Hand off to network-layer protocol, if any */
-	for ( net_protocol = net_protocols ; net_protocol < net_protocols_end ;
-	      net_protocol++ ) {
-		if ( net_protocol->net_proto == net_proto ) {
+	for_each_table_entry ( net_protocol, NET_PROTOCOLS ) {
+		if ( net_protocol->net_proto == net_proto )
 			return net_protocol->rx ( iobuf, netdev, ll_source );
-		}
 	}
+
+	DBGC ( netdev, "NETDEV %p unknown network protocol %04x\n",
+	       netdev, ntohs ( net_proto ) );
 	free_iob ( iobuf );
 	return 0;
 }
@@ -585,8 +614,8 @@ static void net_step ( struct process *process __unused ) {
 
 			/* Remove link-layer header */
 			ll_protocol = netdev->ll_protocol;
-			if ( ( rc = ll_protocol->pull ( iobuf, &ll_dest,
-							&ll_source,
+			if ( ( rc = ll_protocol->pull ( netdev, iobuf,
+							&ll_dest, &ll_source,
 							&net_proto ) ) != 0 ) {
 				free_iob ( iobuf );
 				continue;
@@ -599,5 +628,6 @@ static void net_step ( struct process *process __unused ) {
 
 /** Networking stack process */
 struct process net_process __permanent_process = {
+	.list = LIST_HEAD_INIT ( net_process.list ),
 	.step = net_step,
 };
diff --git a/gpxe/src/net/nullnet.c b/gpxe/src/net/nullnet.c
index 7e199ce..381f02a 100644
--- a/gpxe/src/net/nullnet.c
+++ b/gpxe/src/net/nullnet.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <errno.h>
 #include <gpxe/iobuf.h>
diff --git a/gpxe/src/net/rarp.c b/gpxe/src/net/rarp.c
index bb5e6ad..1d0dd96 100644
--- a/gpxe/src/net/rarp.c
+++ b/gpxe/src/net/rarp.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <byteswap.h>
 #include <gpxe/netdevice.h>
diff --git a/gpxe/src/net/retry.c b/gpxe/src/net/retry.c
index cd793a7..40f656f 100644
--- a/gpxe/src/net/retry.c
+++ b/gpxe/src/net/retry.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <gpxe/timer.h>
 #include <gpxe/list.h>
@@ -185,5 +187,6 @@ static void retry_step ( struct process *process __unused ) {
 
 /** Retry timer process */
 struct process retry_process __permanent_process = {
+	.list = LIST_HEAD_INIT ( retry_process.list ),
 	.step = retry_step,
 };
diff --git a/gpxe/src/net/tcp.c b/gpxe/src/net/tcp.c
index 6bcd193..a061962 100644
--- a/gpxe/src/net/tcp.c
+++ b/gpxe/src/net/tcp.c
@@ -21,6 +21,8 @@
  *
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /** A TCP connection */
 struct tcp_connection {
 	/** Reference counter */
@@ -142,15 +144,15 @@ tcp_dump_state ( struct tcp_connection *tcp ) {
 static inline __attribute__ (( always_inline )) void
 tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) {
 	if ( flags & TCP_RST )
-		DBGC ( tcp, " RST" );
+		DBGC2 ( tcp, " RST" );
 	if ( flags & TCP_SYN )
-		DBGC ( tcp, " SYN" );
+		DBGC2 ( tcp, " SYN" );
 	if ( flags & TCP_PSH )
-		DBGC ( tcp, " PSH" );
+		DBGC2 ( tcp, " PSH" );
 	if ( flags & TCP_FIN )
-		DBGC ( tcp, " FIN" );
+		DBGC2 ( tcp, " FIN" );
 	if ( flags & TCP_ACK )
-		DBGC ( tcp, " ACK" );
+		DBGC2 ( tcp, " ACK" );
 }
 
 /***************************************************************************
@@ -397,9 +399,10 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
 	void *payload;
 	unsigned int flags;
 	size_t len = 0;
-	size_t seq_len;
-	size_t app_win;
-	size_t max_rcv_win;
+	uint32_t seq_len;
+	uint32_t app_win;
+	uint32_t max_rcv_win;
+	int rc;
 
 	/* If retransmission timer is already running, do nothing */
 	if ( timer_running ( &tcp->timer ) )
@@ -436,7 +439,9 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
 	/* Allocate I/O buffer */
 	iobuf = alloc_iob ( len + MAX_HDR_LEN );
 	if ( ! iobuf ) {
-		DBGC ( tcp, "TCP %p could not allocate data buffer\n", tcp );
+		DBGC ( tcp, "TCP %p could not allocate iobuf for %08x..%08x "
+		       "%08x\n", tcp, tcp->snd_seq, ( tcp->snd_seq + seq_len ),
+		       tcp->rcv_ack );
 		return -ENOMEM;
 	}
 	iob_reserve ( iobuf, MAX_HDR_LEN );
@@ -471,6 +476,8 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
 		tsopt->tsopt.tsval = ntohl ( currticks() );
 		tsopt->tsopt.tsecr = ntohl ( tcp->ts_recent );
 	}
+	if ( ! ( flags & TCP_SYN ) )
+		flags |= TCP_PSH;
 	tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) );
 	memset ( tcphdr, 0, sizeof ( *tcphdr ) );
 	tcphdr->src = tcp->local_port;
@@ -478,21 +485,28 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
 	tcphdr->seq = htonl ( tcp->snd_seq );
 	tcphdr->ack = htonl ( tcp->rcv_ack );
 	tcphdr->hlen = ( ( payload - iobuf->data ) << 2 );
-	tcphdr->flags = ( flags | TCP_PSH );
+	tcphdr->flags = flags;
 	tcphdr->win = htons ( tcp->rcv_win );
 	tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );
 
 	/* Dump header */
-	DBGC ( tcp, "TCP %p TX %d->%d %08x..%08zx           %08x %4zd",
-	       tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
-	       ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ),
-	       ntohl ( tcphdr->ack ), len );
+	DBGC2 ( tcp, "TCP %p TX %d->%d %08x..%08x           %08x %4zd",
+		tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
+		ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ),
+		ntohl ( tcphdr->ack ), len );
 	tcp_dump_flags ( tcp, tcphdr->flags );
-	DBGC ( tcp, "\n" );
+	DBGC2 ( tcp, "\n" );
 
 	/* Transmit packet */
-	return tcpip_tx ( iobuf, &tcp_protocol, NULL, &tcp->peer, NULL,
-			  &tcphdr->csum );
+	if ( ( rc = tcpip_tx ( iobuf, &tcp_protocol, NULL, &tcp->peer, NULL,
+			       &tcphdr->csum ) ) != 0 ) {
+		DBGC ( tcp, "TCP %p could not transmit %08x..%08x %08x: %s\n",
+		       tcp, tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ),
+		       tcp->rcv_ack, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
 }
 
 /**
@@ -506,8 +520,9 @@ static void tcp_expired ( struct retry_timer *timer, int over ) {
 		container_of ( timer, struct tcp_connection, timer );
 	int graceful_close = TCP_CLOSED_GRACEFULLY ( tcp->tcp_state );
 
-	DBGC ( tcp, "TCP %p timer %s in %s\n", tcp,
-	       ( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ) );
+	DBGC ( tcp, "TCP %p timer %s in %s for %08x..%08x %08x\n", tcp,
+	       ( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ),
+	       tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ), tcp->rcv_ack );
 
 	assert ( ( tcp->tcp_state == TCP_SYN_SENT ) ||
 		 ( tcp->tcp_state == TCP_SYN_RCVD ) ||
@@ -542,11 +557,14 @@ static int tcp_xmit_reset ( struct tcp_connection *tcp,
 			    struct tcp_header *in_tcphdr ) {
 	struct io_buffer *iobuf;
 	struct tcp_header *tcphdr;
+	int rc;
 
 	/* Allocate space for dataless TX buffer */
 	iobuf = alloc_iob ( MAX_HDR_LEN );
 	if ( ! iobuf ) {
-		DBGC ( tcp, "TCP %p could not allocate data buffer\n", tcp );
+		DBGC ( tcp, "TCP %p could not allocate iobuf for RST "
+		       "%08x..%08x %08x\n", tcp, ntohl ( in_tcphdr->ack ),
+		       ntohl ( in_tcphdr->ack ), ntohl ( in_tcphdr->seq ) );
 		return -ENOMEM;
 	}
 	iob_reserve ( iobuf, MAX_HDR_LEN );
@@ -564,16 +582,24 @@ static int tcp_xmit_reset ( struct tcp_connection *tcp,
 	tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );
 
 	/* Dump header */
-	DBGC ( tcp, "TCP %p TX %d->%d %08x..%08x           %08x %4d",
-	       tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
-	       ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ),
-	       ntohl ( tcphdr->ack ), 0 );
+	DBGC2 ( tcp, "TCP %p TX %d->%d %08x..%08x           %08x %4d",
+		tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
+		ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ),
+		ntohl ( tcphdr->ack ), 0 );
 	tcp_dump_flags ( tcp, tcphdr->flags );
-	DBGC ( tcp, "\n" );
+	DBGC2 ( tcp, "\n" );
 
 	/* Transmit packet */
-	return tcpip_tx ( iobuf, &tcp_protocol, NULL, st_dest,
-			  NULL, &tcphdr->csum );
+	if ( ( rc = tcpip_tx ( iobuf, &tcp_protocol, NULL, st_dest,
+			       NULL, &tcphdr->csum ) ) != 0 ) {
+		DBGC ( tcp, "TCP %p could not transmit RST %08x..%08x %08x: "
+		       "%s\n", tcp, ntohl ( in_tcphdr->ack ),
+		       ntohl ( in_tcphdr->ack ), ntohl ( in_tcphdr->seq ),
+		       strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
 }
 
 /***************************************************************************
@@ -645,7 +671,7 @@ static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data,
  * @v tcp		TCP connection
  * @v seq_len		Sequence space length to consume
  */
-static void tcp_rx_seq ( struct tcp_connection *tcp, size_t seq_len ) {
+static void tcp_rx_seq ( struct tcp_connection *tcp, uint32_t seq_len ) {
 	tcp->rcv_ack += seq_len;
 	if ( tcp->rcv_win > seq_len ) {
 		tcp->rcv_win -= seq_len;
@@ -696,20 +722,42 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq,
  */
 static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
 			uint32_t win ) {
-	size_t ack_len = ( ack - tcp->snd_seq );
+	uint32_t ack_len = ( ack - tcp->snd_seq );
 	size_t len;
 	unsigned int acked_flags;
 
-	/* Ignore duplicate or out-of-range ACK */
+	/* Check for out-of-range or old duplicate ACKs */
 	if ( ack_len > tcp->snd_sent ) {
-		DBGC ( tcp, "TCP %p received ACK for [%08x,%08zx), "
-		       "sent only [%08x,%08x)\n", tcp, tcp->snd_seq,
+		DBGC ( tcp, "TCP %p received ACK for %08x..%08x, "
+		       "sent only %08x..%08x\n", tcp, tcp->snd_seq,
 		       ( tcp->snd_seq + ack_len ), tcp->snd_seq,
 		       ( tcp->snd_seq + tcp->snd_sent ) );
-		return -EINVAL;
+
+		if ( TCP_HAS_BEEN_ESTABLISHED ( tcp->tcp_state ) ) {
+			/* Just ignore what might be old duplicate ACKs */
+			return 0;
+		} else {
+			/* Send RST if an out-of-range ACK is received
+			 * on a not-yet-established connection, as per
+			 * RFC 793.
+			 */
+			return -EINVAL;
+		}
 	}
 
-	/* Acknowledge any flags being sent */
+	/* Ignore ACKs that don't actually acknowledge any new data.
+	 * (In particular, do not stop the retransmission timer; this
+	 * avoids creating a sorceror's apprentice syndrome when a
+	 * duplicate ACK is received and we still have data in our
+	 * transmit queue.)
+	 */
+	if ( ack_len == 0 )
+		return 0;
+
+	/* Stop the retransmission timer */
+	stop_timer ( &tcp->timer );
+
+	/* Determine acknowledged flags and data length */
 	len = ack_len;
 	acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) &
 			( TCP_SYN | TCP_FIN ) );
@@ -721,9 +769,6 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
 	tcp->snd_sent = 0;
 	tcp->snd_win = win;
 
-	/* Stop the retransmission timer */
-	stop_timer ( &tcp->timer );
-
 	/* Remove any acknowledged data from transmit queue */
 	tcp_process_queue ( tcp, len, NULL, 1 );
 		
@@ -750,8 +795,8 @@ static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
  */
 static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq,
 			 struct io_buffer *iobuf ) {
-	size_t already_rcvd;
-	size_t len;
+	uint32_t already_rcvd;
+	uint32_t len;
 	int rc;
 
 	/* Ignore duplicate or out-of-order data */
@@ -765,8 +810,11 @@ static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq,
 	len -= already_rcvd;
 
 	/* Deliver data to application */
-	if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 )
+	if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) {
+		DBGC ( tcp, "TCP %p could not deliver %08x..%08x: %s\n",
+		       tcp, seq, ( seq + len ), strerror ( rc ) );
 		return rc;
+	}
 
 	/* Acknowledge new data */
 	tcp_rx_seq ( tcp, len );
@@ -824,6 +872,7 @@ static int tcp_rx_rst ( struct tcp_connection *tcp, uint32_t seq ) {
 	tcp_dump_state ( tcp );
 	tcp_close ( tcp, -ECONNRESET );
 
+	DBGC ( tcp, "TCP %p connection reset by peer\n", tcp );
 	return -ECONNRESET;
 }
 
@@ -894,13 +943,13 @@ static int tcp_rx ( struct io_buffer *iobuf,
 	len = iob_len ( iobuf );
 
 	/* Dump header */
-	DBGC ( tcp, "TCP %p RX %d<-%d           %08x %08x..%08zx %4zd",
-	       tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
-	       ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ),
-	       ( ntohl ( tcphdr->seq ) + len +
-		 ( ( tcphdr->flags & ( TCP_SYN | TCP_FIN ) ) ? 1 : 0 ) ), len);
+	DBGC2 ( tcp, "TCP %p RX %d<-%d           %08x %08x..%08zx %4zd",
+		tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
+		ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ),
+		( ntohl ( tcphdr->seq ) + len +
+		  ( ( tcphdr->flags & ( TCP_SYN | TCP_FIN ) ) ? 1 : 0 )), len);
 	tcp_dump_flags ( tcp, tcphdr->flags );
-	DBGC ( tcp, "\n" );
+	DBGC2 ( tcp, "\n" );
 
 	/* If no connection was found, send RST */
 	if ( ! tcp ) {
diff --git a/gpxe/src/net/tcp/ftp.c b/gpxe/src/net/tcp/ftp.c
index 445e32b..920e537 100644
--- a/gpxe/src/net/tcp/ftp.c
+++ b/gpxe/src/net/tcp/ftp.c
@@ -131,11 +131,33 @@ static const char * ftp_uri_path ( struct ftp_request *ftp ) {
 	return ftp->uri->path;
 }
 
+/**
+ * Retrieve FTP user
+ *
+ * @v ftp		FTP request
+ * @ret user		FTP user
+ */
+static const char * ftp_user ( struct ftp_request *ftp ) {
+	static char *ftp_default_user = "anonymous";
+	return ftp->uri->user ? ftp->uri->user : ftp_default_user;
+}
+
+/**
+ * Retrieve FTP password
+ *
+ * @v ftp		FTP request
+ * @ret password	FTP password
+ */
+static const char * ftp_password ( struct ftp_request *ftp ) {
+	static char *ftp_default_password = "etherboot at etherboot.org";
+	return ftp->uri->password ? ftp->uri->password : ftp_default_password;
+}
+
 /** FTP control channel strings */
 static struct ftp_control_string ftp_strings[] = {
 	[FTP_CONNECT]	= { NULL, NULL },
-	[FTP_USER]	= { "USER anonymous", NULL },
-	[FTP_PASS]	= { "PASS etherboot at etherboot.org", NULL },
+	[FTP_USER]	= { "USER ", ftp_user },
+	[FTP_PASS]	= { "PASS ", ftp_password },
 	[FTP_TYPE]	= { "TYPE I", NULL },
 	[FTP_PASV]	= { "PASV", NULL },
 	[FTP_RETR]	= { "RETR ", ftp_uri_path },
@@ -335,7 +357,7 @@ static int ftp_control_deliver_raw ( struct xfer_interface *control,
 /** FTP control channel operations */
 static struct xfer_interface_operations ftp_control_operations = {
 	.close		= ftp_control_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= xfer_deliver_as_raw,
@@ -402,7 +424,7 @@ static int ftp_data_deliver_iob ( struct xfer_interface *data,
 /** FTP data channel operations */
 static struct xfer_interface_operations ftp_data_operations = {
 	.close		= ftp_data_closed,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= ftp_data_deliver_iob,
diff --git a/gpxe/src/net/tcp/http.c b/gpxe/src/net/tcp/http.c
index 93ccfd3..a365b2a 100644
--- a/gpxe/src/net/tcp/http.c
+++ b/gpxe/src/net/tcp/http.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -138,6 +140,8 @@ static void http_done ( struct http_request *http, int rc ) {
 static int http_response_to_rc ( unsigned int response ) {
 	switch ( response ) {
 	case 200:
+	case 301:
+	case 302:
 		return 0;
 	case 404:
 		return -ENOENT;
@@ -181,6 +185,28 @@ static int http_rx_response ( struct http_request *http, char *response ) {
 }
 
 /**
+ * Handle HTTP Location header
+ *
+ * @v http		HTTP request
+ * @v value		HTTP header value
+ * @ret rc		Return status code
+ */
+static int http_rx_location ( struct http_request *http, const char *value ) {
+	int rc;
+
+	/* Redirect to new location */
+	DBGC ( http, "HTTP %p redirecting to %s\n", http, value );
+	if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING,
+				    value ) ) != 0 ) {
+		DBGC ( http, "HTTP %p could not redirect: %s\n",
+		       http, strerror ( rc ) );
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
  * Handle HTTP Content-Length header
  *
  * @v http		HTTP request
@@ -223,6 +249,10 @@ struct http_header_handler {
 /** List of HTTP header handlers */
 static struct http_header_handler http_header_handlers[] = {
 	{
+		.header = "Location",
+		.rx = http_rx_location,
+	},
+	{
 		.header = "Content-Length",
 		.rx = http_rx_content_length,
 	},
@@ -387,9 +417,7 @@ static int http_socket_deliver_iob ( struct xfer_interface *socket,
 static void http_step ( struct process *process ) {
 	struct http_request *http =
 		container_of ( process, struct http_request, process );
-	const char *path = http->uri->path;
 	const char *host = http->uri->host;
-	const char *query = http->uri->query;
 	const char *user = http->uri->user;
 	const char *password =
 		( http->uri->password ? http->uri->password : "" );
@@ -399,27 +427,24 @@ static void http_step ( struct process *process ) {
 	char user_pw[ user_pw_len + 1 /* NUL */ ];
 	char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ];
 	int rc;
+	int request_len = unparse_uri ( NULL, 0, http->uri,
+					URI_PATH_BIT | URI_QUERY_BIT );
 
 	if ( xfer_window ( &http->socket ) ) {
+		char request[request_len + 1];
+
+		/* Construct path?query request */
+		unparse_uri ( request, sizeof ( request ), http->uri,
+			      URI_PATH_BIT | URI_QUERY_BIT );
 
 		/* We want to execute only once */
 		process_del ( &http->process );
 
 		/* Construct authorisation, if applicable */
 		if ( user ) {
-			char *buf = user_pw;
-			ssize_t remaining = sizeof ( user_pw );
-			size_t len;
-
-			/* URI-decode the username and password */
-			len = uri_decode ( user, buf, remaining );
-			buf += len;
-			remaining -= len;
-			*(remaining--, buf++) = ':';
-			len = uri_decode ( password, buf, remaining );
-			buf += len;
-			remaining -= len;
-			assert ( remaining >= 0 );
+			/* Make "user:password" string from decoded fields */
+			snprintf ( user_pw, sizeof ( user_pw ), "%s:%s",
+				   user, password );
 
 			/* Base64-encode the "user:password" string */
 			base64_encode ( user_pw, user_pw_base64 );
@@ -427,14 +452,13 @@ static void http_step ( struct process *process ) {
 
 		/* Send GET request */
 		if ( ( rc = xfer_printf ( &http->socket,
-					  "GET %s%s%s HTTP/1.0\r\n"
+					  "GET %s%s HTTP/1.0\r\n"
 					  "User-Agent: gPXE/" VERSION "\r\n"
 					  "%s%s%s"
 					  "Host: %s\r\n"
 					  "\r\n",
-					  ( path ? path : "/" ),
-					  ( query ? "?" : "" ),
-					  ( query ? query : "" ),
+					  http->uri->path ? "" : "/",
+					  request,
 					  ( user ?
 					    "Authorization: Basic " : "" ),
 					  ( user ? user_pw_base64 : "" ),
@@ -464,7 +488,7 @@ static void http_socket_close ( struct xfer_interface *socket, int rc ) {
 /** HTTP socket operations */
 static struct xfer_interface_operations http_socket_operations = {
 	.close		= http_socket_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= http_socket_deliver_iob,
diff --git a/gpxe/src/net/tcp/https.c b/gpxe/src/net/tcp/https.c
index 15ab32e..7a2961f 100644
--- a/gpxe/src/net/tcp/https.c
+++ b/gpxe/src/net/tcp/https.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
diff --git a/gpxe/src/net/tcp/iscsi.c b/gpxe/src/net/tcp/iscsi.c
index 45519e6..771384b 100644
--- a/gpxe/src/net/tcp/iscsi.c
+++ b/gpxe/src/net/tcp/iscsi.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stddef.h>
 #include <string.h>
 #include <stdlib.h>
@@ -180,9 +182,10 @@ static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) {
 static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc ) {
 
 	assert ( iscsi->tx_state == ISCSI_TX_IDLE );
+	assert ( iscsi->command != NULL );
 
+	iscsi->command->rc = rc;
 	iscsi->command = NULL;
-	iscsi->rc = rc;
 }
 
 /****************************************************************************
@@ -1514,7 +1517,7 @@ static int iscsi_vredirect ( struct xfer_interface *socket, int type,
 		va_end ( tmp );
 	}
 
-	return xfer_vopen ( socket, type, args );
+	return xfer_vreopen ( socket, type, args );
 }
 			     
 
@@ -1548,37 +1551,24 @@ static int iscsi_command ( struct scsi_device *scsi,
 		container_of ( scsi->backend, struct iscsi_session, refcnt );
 	int rc;
 
+	/* Abort immediately if we have a recorded permanent failure */
+	if ( iscsi->instant_rc )
+		return iscsi->instant_rc;
+
 	/* Record SCSI command */
 	iscsi->command = command;
 
-	/* Abort immediately if we have a recorded permanent failure */
-	if ( iscsi->instant_rc ) {
-		rc = iscsi->instant_rc;
-		goto done;
-	}
-
 	/* Issue command or open connection as appropriate */
 	if ( iscsi->status ) {
 		iscsi_start_command ( iscsi );
 	} else {
-		if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 )
-			goto done;
+		if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
+			iscsi->command = NULL;
+			return rc;
+		}
 	}
 
-	/* Wait for command to complete */
-	iscsi->rc = -EINPROGRESS;
-	while ( iscsi->rc == -EINPROGRESS )
-		step();
-	rc = iscsi->rc;
-
- done:
-	iscsi->command = NULL;
-	return rc;
-}
-
-static int iscsi_detached_command ( struct scsi_device *scsi __unused,
-				    struct scsi_command *command __unused ) {
-	return -ENODEV;
+	return 0;
 }
 
 /**
@@ -1593,7 +1583,7 @@ void iscsi_detach ( struct scsi_device *scsi ) {
 	xfer_nullify ( &iscsi->socket );
 	iscsi_close_connection ( iscsi, 0 );
 	process_del ( &iscsi->process );
-	scsi->command = iscsi_detached_command;
+	scsi->command = scsi_detached_command;
 	ref_put ( scsi->backend );
 	scsi->backend = NULL;
 }
@@ -1616,42 +1606,6 @@ enum iscsi_root_path_component {
 };
 
 /**
- * Parse iSCSI LUN
- *
- * @v iscsi		iSCSI session
- * @v lun_string	LUN string representation (as per RFC4173)
- * @ret rc		Return status code
- */
-static int iscsi_parse_lun ( struct iscsi_session *iscsi,
-			     const char *lun_string ) {
-	union {
-		uint64_t u64;
-		uint16_t u16[4];
-	} lun;
-	char *p;
-	int i;
-
-	memset ( &lun, 0, sizeof ( lun ) );
-	if ( lun_string ) {
-		p = ( char * ) lun_string;
-		
-		for ( i = 0 ; i < 4 ; i++ ) {
-			lun.u16[i] = htons ( strtoul ( p, &p, 16 ) );
-			if ( *p == '\0' )
-				break;
-			if ( *p != '-' )
-				return -EINVAL;
-			p++;
-		}
-		if ( *p )
-			return -EINVAL;
-	}
-
-	iscsi->lun = lun.u64;
-	return 0;
-}
-
-/**
  * Parse iSCSI root path
  *
  * @v iscsi		iSCSI session
@@ -1689,7 +1643,7 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
 	iscsi->target_port = strtoul ( rp_comp[RP_PORT], NULL, 10 );
 	if ( ! iscsi->target_port )
 		iscsi->target_port = ISCSI_PORT;
-	if ( ( rc = iscsi_parse_lun ( iscsi, rp_comp[RP_LUN] ) ) != 0 ) {
+	if ( ( rc = scsi_parse_lun ( rp_comp[RP_LUN], &iscsi->lun ) ) != 0 ) {
 		DBGC ( iscsi, "iSCSI %p invalid LUN \"%s\"\n",
 		       iscsi, rp_comp[RP_LUN] );
 		return rc;
@@ -1809,7 +1763,6 @@ int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) {
 	/* Attach parent interface, mortalise self, and return */
 	scsi->backend = ref_get ( &iscsi->refcnt );
 	scsi->command = iscsi_command;
-	scsi->lun = iscsi->lun;
 	ref_put ( &iscsi->refcnt );
 	return 0;
 	
diff --git a/gpxe/src/net/tcpip.c b/gpxe/src/net/tcpip.c
index d4542b0..932fd48 100644
--- a/gpxe/src/net/tcpip.c
+++ b/gpxe/src/net/tcpip.c
@@ -14,17 +14,7 @@
  * TCP/IP transport-network layer interface
  */
 
-/** Registered network-layer protocols that support TCP/IP */
-static struct tcpip_net_protocol tcpip_net_protocols[0]
-	__table_start ( struct tcpip_net_protocol, tcpip_net_protocols );
-static struct tcpip_net_protocol tcpip_net_protocols_end[0]
-	__table_end ( struct tcpip_net_protocol, tcpip_net_protocols );
-
-/** Registered transport-layer protocols that support TCP/IP */
-static struct tcpip_protocol tcpip_protocols[0]
-	__table_start ( struct tcpip_protocol, tcpip_protocols );
-static struct tcpip_protocol tcpip_protocols_end[0]
-	__table_end ( struct tcpip_protocol, tcpip_protocols );
+FILE_LICENCE ( GPL2_OR_LATER );
 
 /** Process a received TCP/IP packet
  *
@@ -48,7 +38,7 @@ int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
 	struct tcpip_protocol *tcpip;
 
 	/* Hand off packet to the appropriate transport-layer protocol */
-	for ( tcpip = tcpip_protocols; tcpip < tcpip_protocols_end; tcpip++ ) {
+	for_each_table_entry ( tcpip, TCPIP_PROTOCOLS ) {
 		if ( tcpip->tcpip_proto == tcpip_proto ) {
 			DBG ( "TCP/IP received %s packet\n", tcpip->name );
 			return tcpip->rx ( iobuf, st_src, st_dest, pshdr_csum );
@@ -76,8 +66,7 @@ int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
 	struct tcpip_net_protocol *tcpip_net;
 
 	/* Hand off packet to the appropriate network-layer protocol */
-	for ( tcpip_net = tcpip_net_protocols ;
-	      tcpip_net < tcpip_net_protocols_end ; tcpip_net++ ) {
+	for_each_table_entry ( tcpip_net, TCPIP_NET_PROTOCOLS ) {
 		if ( tcpip_net->sa_family == st_dest->st_family ) {
 			DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
 			return tcpip_net->tx ( iobuf, tcpip_protocol, st_src,
diff --git a/gpxe/src/net/tls.c b/gpxe/src/net/tls.c
index 73f9ad0..a5b126e 100644
--- a/gpxe/src/net/tls.c
+++ b/gpxe/src/net/tls.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * @file
  *
@@ -1625,7 +1627,7 @@ static int tls_cipherstream_deliver_raw ( struct xfer_interface *xfer,
 /** TLS ciphertext stream operations */
 static struct xfer_interface_operations tls_cipherstream_operations = {
 	.close		= tls_cipherstream_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= filter_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= xfer_deliver_as_raw,
diff --git a/gpxe/src/net/udp.c b/gpxe/src/net/udp.c
index c3a1eba..771655e 100644
--- a/gpxe/src/net/udp.c
+++ b/gpxe/src/net/udp.c
@@ -16,6 +16,8 @@
  * UDP protocol
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 /**
  * A UDP connection
  *
@@ -238,7 +240,7 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
  * @ret udp		UDP connection, or NULL
  */
 static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) {
-	static const struct sockaddr_tcpip empty_sockaddr;
+	static const struct sockaddr_tcpip empty_sockaddr = { .pad = { 0, } };
 	struct udp_connection *udp;
 
 	list_for_each_entry ( udp, &udp_conns, list ) {
diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c
index ab843ce..ce2c820 100644
--- a/gpxe/src/net/udp/dhcp.c
+++ b/gpxe/src/net/udp/dhcp.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -69,6 +71,7 @@ static const uint8_t dhcp_op[] = {
 
 /** Raw option data for options common to all DHCP requests */
 static uint8_t dhcp_request_options_data[] = {
+	DHCP_MESSAGE_TYPE, DHCP_BYTE ( 0 ),
 	DHCP_MAX_MESSAGE_SIZE,
 	DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ),
 	DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ),
@@ -88,10 +91,6 @@ static uint8_t dhcp_request_options_data[] = {
 	DHCP_END
 };
 
-/** DHCP feature codes */
-static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features );
-static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features );
-
 /** Version number feature */
 FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH );
 
@@ -111,6 +110,14 @@ struct setting user_class_setting __setting = {
 	.type = &setting_type_string,
 };
 
+/** Use cached network settings */
+struct setting use_cached_setting __setting = {
+	.name = "use-cached",
+	.description = "Use cached network settings",
+	.tag = DHCP_EB_USE_CACHED,
+	.type = &setting_type_uint8,
+};
+
 /**
  * Name a DHCP packet type
  *
@@ -199,6 +206,36 @@ static struct dhcp_session_state dhcp_state_request;
 static struct dhcp_session_state dhcp_state_proxy;
 static struct dhcp_session_state dhcp_state_pxebs;
 
+/** DHCP offer is valid for IP lease */
+#define DHCP_OFFER_IP	1
+
+/** DHCP offer is valid for PXE options */
+#define DHCP_OFFER_PXE	2
+
+/** A DHCP offer */
+struct dhcp_offer {
+	/** IP address of server granting offer */
+	struct in_addr server;
+
+	/** IP address being offered, or 0.0.0.0 for a pure proxy */
+	struct in_addr ip;
+
+	/** DHCP packet containing PXE options; NULL if missing or proxied */
+	struct dhcp_packet *pxe;
+
+	/** Valid uses for this offer, a combination of DHCP_OFFER bits */
+	uint8_t valid;
+
+	/** Priority of this offer */
+	int8_t priority;
+
+	/** Whether to ignore PXE DHCP extensions */
+	uint8_t no_pxedhcp;
+};
+
+/** Maximum number of DHCP offers to queue */
+#define DHCP_MAX_OFFERS   6
+
 /** A DHCP session */
 struct dhcp_session {
 	/** Reference counter */
@@ -215,20 +252,6 @@ struct dhcp_session {
 	/** State of the session */
 	struct dhcp_session_state *state;
 
-	/** Offered IP address */
-	struct in_addr offer;
-	/** DHCP server */
-	struct in_addr server;
-	/** DHCP offer priority */
-	int priority;
-
-	/** ProxyDHCP protocol extensions should be ignored */
-	int no_pxedhcp;
-	/** ProxyDHCP server */
-	struct in_addr proxy_server;
-	/** ProxyDHCP server priority */
-	int proxy_priority;
-
 	/** PXE Boot Server type */
 	uint16_t pxe_type;
 	/** List of PXE Boot Servers to attempt */
@@ -240,6 +263,11 @@ struct dhcp_session {
 	struct retry_timer timer;
 	/** Start time of the current state (in ticks) */
 	unsigned long start;
+
+	/** DHCP offer just requested */
+	struct dhcp_offer *current_offer;
+	/** List of DHCP offers received */
+	struct dhcp_offer offers[DHCP_MAX_OFFERS];
 };
 
 /**
@@ -250,6 +278,12 @@ struct dhcp_session {
 static void dhcp_free ( struct refcnt *refcnt ) {
 	struct dhcp_session *dhcp =
 		container_of ( refcnt, struct dhcp_session, refcnt );
+	int i;
+
+	for ( i = 0 ; i < DHCP_MAX_OFFERS ; i++ ) {
+		if ( dhcp->offers[i].pxe )
+			dhcppkt_put ( dhcp->offers[i].pxe );
+	}
 
 	netdev_put ( dhcp->netdev );
 	free ( dhcp );
@@ -294,6 +328,35 @@ static void dhcp_set_state ( struct dhcp_session *dhcp,
 	start_timer_nodelay ( &dhcp->timer );
 }
 
+/**
+ * Determine next DHCP offer to try
+ *
+ * @v dhcp		DHCP session
+ * @v type		DHCP offer type
+ * @ret offer		Next DHCP offer to try
+ *
+ * Offers are ranked by priority, then by completeness (combined
+ * IP+PXE are tried before @a type alone), then by order of receipt.
+ */
+static struct dhcp_offer * dhcp_next_offer ( struct dhcp_session *dhcp,
+					     uint8_t type ) {
+
+	struct dhcp_offer *offer;
+	struct dhcp_offer *best = NULL;
+
+	for ( offer = dhcp->offers ; offer < dhcp->offers + DHCP_MAX_OFFERS ;
+	      offer++ ) {
+		if ( ( offer->valid & type ) &&
+		     ( ( best == NULL ) ||
+		       ( offer->priority > best->priority ) ||
+		       ( ( offer->priority == best->priority ) &&
+			 ( offer->valid & ~best->valid ) ) ) )
+			best = offer;
+	}
+
+	return best;
+}
+
 /****************************************************************************
  *
  * DHCP state machine
@@ -321,7 +384,7 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
 }
 
 /**
- * Handle received packet during DHCP discovery
+ * Handle received DHCPOFFER during any state
  *
  * @v dhcp		DHCP session
  * @v dhcppkt		DHCP packet
@@ -329,17 +392,17 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
  * @v msgtype		DHCP message type
  * @v server_id		DHCP server ID
  */
-static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
-				struct dhcp_packet *dhcppkt,
-				struct sockaddr_in *peer, uint8_t msgtype,
-				struct in_addr server_id ) {
-	struct in_addr ip;
+static void dhcp_rx_offer ( struct dhcp_session *dhcp,
+			    struct dhcp_packet *dhcppkt,
+			    struct sockaddr_in *peer, uint8_t msgtype,
+			    struct in_addr server_id ) {
 	char vci[9]; /* "PXEClient" */
 	int vci_len;
 	int has_pxeclient;
-	int8_t priority = 0;
-	uint8_t no_pxedhcp = 0;
-	unsigned long elapsed;
+	int pxeopts_len;
+	int has_pxeopts;
+	struct dhcp_offer *offer;
+	int i;
 
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
@@ -348,47 +411,87 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
 		DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
 
 	/* Identify offered IP address */
-	ip = dhcppkt->dhcphdr->yiaddr;
-	if ( ip.s_addr )
-		DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
+	if ( dhcppkt->dhcphdr->yiaddr.s_addr )
+		DBGC ( dhcp, " for %s", inet_ntoa ( dhcppkt->dhcphdr->yiaddr ));
+
+	/* Enqueue an offer to be filled in */
+	for ( i = 0 ; i < DHCP_MAX_OFFERS ; i++ ) {
+		if ( dhcp->offers[i].server.s_addr == server_id.s_addr ) {
+			DBGC ( dhcp, " dup\n" );
+			return;
+		}
+
+		if ( ! dhcp->offers[i].valid )
+			break;
+	}
+	if ( i == DHCP_MAX_OFFERS ) {
+		DBGC ( dhcp, " dropped\n" );
+		return;
+	}
+
+	offer = &dhcp->offers[i];
+	offer->server = server_id;
+	offer->ip = dhcppkt->dhcphdr->yiaddr;
 
 	/* Identify "PXEClient" vendor class */
 	vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
 				  vci, sizeof ( vci ) );
 	has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
 			  ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 ));
+
+	/* Identify presence of PXE-specific options */
+	pxeopts_len = dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU, NULL, 0 );
+	has_pxeopts = ( pxeopts_len >= 0 );
 	if ( has_pxeclient )
-		DBGC ( dhcp, " pxe" );
+		DBGC ( dhcp, "%s", ( has_pxeopts ? " pxe" : " proxy" ) );
+
+	if ( has_pxeclient && has_pxeopts ) {
+		/* Save reference to packet for future use */
+		if ( offer->pxe )
+			dhcppkt_put ( offer->pxe );
+		offer->pxe = dhcppkt_get ( dhcppkt );
+	}
 
 	/* Identify priority */
-	dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
-			sizeof ( priority ) );
-	if ( priority )
-		DBGC ( dhcp, " pri %d", priority );
+	dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &offer->priority,
+			sizeof ( offer->priority ) );
+	if ( offer->priority )
+		DBGC ( dhcp, " pri %d", offer->priority );
 
 	/* Identify ignore-PXE flag */
-	dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp,
-			sizeof ( no_pxedhcp ) );
-	if ( no_pxedhcp )
+	dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &offer->no_pxedhcp,
+			sizeof ( offer->no_pxedhcp ) );
+	if ( offer->no_pxedhcp )
 		DBGC ( dhcp, " nopxe" );
 	DBGC ( dhcp, "\n" );
 
-	/* Select as DHCP offer, if applicable */
-	if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
-	     ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) &&
-	     ( priority >= dhcp->priority ) ) {
-		dhcp->offer = ip;
-		dhcp->server = server_id;
-		dhcp->priority = priority;
-		dhcp->no_pxedhcp = no_pxedhcp;
-	}
+	/* Determine roles this offer can fill */
+	if ( offer->ip.s_addr &&
+	     ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
+	     ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) )
+		offer->valid |= DHCP_OFFER_IP;
 
-	/* Select as ProxyDHCP offer, if applicable */
-	if ( has_pxeclient && ( msgtype == DHCPOFFER ) &&
-	     ( priority >= dhcp->proxy_priority ) ) {
-		dhcp->proxy_server = server_id;
-		dhcp->proxy_priority = priority;
-	}
+	if ( has_pxeclient && ( msgtype == DHCPOFFER ) )
+		offer->valid |= DHCP_OFFER_PXE;
+}
+
+/**
+ * Handle received packet during DHCP discovery
+ *
+ * @v dhcp		DHCP session
+ * @v dhcppkt		DHCP packet
+ * @v peer		DHCP server address
+ * @v msgtype		DHCP message type
+ * @v server_id		DHCP server ID
+ */
+static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
+				struct dhcp_packet *dhcppkt,
+				struct sockaddr_in *peer, uint8_t msgtype,
+				struct in_addr server_id ) {
+	unsigned long elapsed;
+	struct dhcp_offer *ip_offer;
+
+	dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
 
 	/* We can exit the discovery state when we have a valid
 	 * DHCPOFFER, and either:
@@ -399,12 +502,14 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
 	 */
 
 	/* If we don't yet have a DHCPOFFER, do nothing */
-	if ( ! dhcp->offer.s_addr )
+	ip_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_IP );
+	if ( ! ip_offer )
 		return;
 
 	/* If we can't yet transition to DHCPREQUEST, do nothing */
 	elapsed = ( currticks() - dhcp->start );
-	if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_server.s_addr ||
+	if ( ! ( ip_offer->no_pxedhcp ||
+		 dhcp_next_offer ( dhcp, DHCP_OFFER_PXE ) ||
 		 ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
 		return;
 
@@ -421,7 +526,8 @@ static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) {
 	unsigned long elapsed = ( currticks() - dhcp->start );
 
 	/* Give up waiting for ProxyDHCP before we reach the failure point */
-	if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) {
+	if ( dhcp_next_offer ( dhcp, DHCP_OFFER_IP ) &&
+	     ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) {
 		dhcp_set_state ( dhcp, &dhcp_state_request );
 		return;
 	}
@@ -451,21 +557,23 @@ static int dhcp_request_tx ( struct dhcp_session *dhcp,
 			     struct dhcp_packet *dhcppkt,
 			     struct sockaddr_in *peer ) {
 	int rc;
+	struct dhcp_offer *offer;
+
+	offer = dhcp->current_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_IP );
 
 	DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
-	       dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT );
-	DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) );
+	       dhcp, inet_ntoa ( offer->server ), BOOTPS_PORT );
+	DBGC ( dhcp, " for %s\n", inet_ntoa ( offer->ip ) );
 
 	/* Set server ID */
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
-				    &dhcp->server,
-				    sizeof ( dhcp->server ) ) ) != 0 )
+				    &offer->server,
+				    sizeof ( offer->server ) ) ) != 0 )
 		return rc;
 
 	/* Set requested IP address */
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
-				    &dhcp->offer,
-				    sizeof ( dhcp->offer ) ) ) != 0 )
+				    &offer->ip, sizeof ( offer->ip ) ) ) != 0 )
 		return rc;
 
 	/* Set server address */
@@ -491,6 +599,18 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
 	struct in_addr ip;
 	struct settings *parent;
 	int rc;
+	struct dhcp_offer *pxe_offer;
+
+	if ( msgtype == DHCPOFFER ) {
+		dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
+		if ( dhcp_next_offer ( dhcp, DHCP_OFFER_IP ) !=
+		     dhcp->current_offer ) {
+			/* Restart due to higher-priority offer received */
+			DBGC ( dhcp, "DHCP %p re-requesting\n", dhcp );
+			dhcp_set_state ( dhcp, &dhcp_state_request );
+		}
+		return;
+	}
 
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
@@ -509,7 +629,7 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
 		return;
 	if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
 		return;
-	if ( server_id.s_addr != dhcp->server.s_addr )
+	if ( server_id.s_addr != dhcp->current_offer->server.s_addr )
 		return;
 
 	/* Record assigned address */
@@ -524,14 +644,31 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
 		return;
 	}
 
-	/* Start ProxyDHCPREQUEST if applicable */
-	if ( dhcp->proxy_server.s_addr && ( ! dhcp->no_pxedhcp ) ) {
+	/* Locate best source of PXE settings */
+	pxe_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_PXE );
+
+	if ( ( ! pxe_offer ) || /* No PXE available */
+	     /* IP offer instructs us to ignore PXE */
+	     dhcp->current_offer->no_pxedhcp ||
+	     /* PXE settings already registered with IP offer */
+	     ( ( dhcp->current_offer == pxe_offer ) && ( pxe_offer->pxe ) ) ) {
+
+		/* Terminate DHCP */
+		dhcp_finished ( dhcp, 0 );
+
+	} else if ( pxe_offer->pxe ) {
+		/* Register PXE settings and terminate DHCP */
+		pxe_offer->pxe->settings.name = PROXYDHCP_SETTINGS_NAME;
+		if ( ( rc = register_settings ( &pxe_offer->pxe->settings,
+						NULL ) ) != 0 ) {
+			DBGC ( dhcp, "DHCP %p could not register settings: "
+			       "%s\n", dhcp, strerror ( rc ) );
+		}
+		dhcp_finished ( dhcp, rc );
+	} else {
+		/* Start ProxyDHCP */
 		dhcp_set_state ( dhcp, &dhcp_state_proxy );
-		return;
 	}
-
-	/* Terminate DHCP */
-	dhcp_finished ( dhcp, 0 );
 }
 
 /**
@@ -566,18 +703,21 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
 			   struct dhcp_packet *dhcppkt,
 			   struct sockaddr_in *peer ) {
 	int rc;
+	struct dhcp_offer *offer;
+
+	offer = dhcp->current_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_PXE );
 
-	DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s:%d\n",
-	       dhcp, inet_ntoa ( dhcp->proxy_server ), PXE_PORT );
+	DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s:%d\n", dhcp,
+	       inet_ntoa ( offer->server ), PXE_PORT );
 
 	/* Set server ID */
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
-				    &dhcp->proxy_server,
-				    sizeof ( dhcp->proxy_server ) ) ) != 0 )
+				    &offer->server,
+				    sizeof ( offer->server ) ) )  != 0 )
 		return rc;
 
 	/* Set server address */
-	peer->sin_addr = dhcp->proxy_server;
+	peer->sin_addr = offer->server;
 	peer->sin_port = htons ( PXE_PORT );
 
 	return 0;
@@ -598,6 +738,13 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
 			    struct in_addr server_id ) {
 	int rc;
 
+	/* Enqueue last-minute DHCPOFFERs for use in case of failure */
+	if ( peer->sin_port == htons ( BOOTPS_PORT ) &&
+	     msgtype == DHCPOFFER ) {
+		dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
+		return;
+	}
+
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
 	       ntohs ( peer->sin_port ) );
@@ -608,10 +755,10 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
 	/* Filter out unacceptable responses */
 	if ( peer->sin_port != htons ( PXE_PORT ) )
 		return;
-	if ( msgtype != DHCPACK )
+	if ( msgtype != DHCPACK && msgtype != DHCPOFFER )
 		return;
 	if ( server_id.s_addr /* Linux PXE server omits server ID */ &&
-	     ( server_id.s_addr != dhcp->proxy_server.s_addr ) )
+	     ( server_id.s_addr != dhcp->current_offer->server.s_addr ) )
 		return;
 
 	/* Register settings */
@@ -637,6 +784,28 @@ static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) {
 
 	/* Give up waiting for ProxyDHCP before we reach the failure point */
 	if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) {
+
+		/* Mark failed offer as unsuitable for ProxyDHCP */
+		dhcp->current_offer->valid &= ~DHCP_OFFER_PXE;
+
+		/* Prefer not to use only half of a PXE+IP offer if we
+		 * have other offers available
+		 */
+		dhcp->current_offer->priority = -1;
+
+		/* If we have any other PXE offers we can try, go back
+		 * to DHCPREQUEST (since they might not be proxied
+		 * offers, or might be coupled to a new IP address).
+		 * We should probably DHCPRELEASE our old IP, but the
+		 * standard does not require it.
+		 */
+		if ( dhcp_next_offer ( dhcp, DHCP_OFFER_PXE ) ) {
+			dhcp->local.sin_addr.s_addr = 0;
+			dhcp_set_state ( dhcp, &dhcp_state_request );
+			return;
+		}
+
+		/* No possibilities left; finish without PXE options */
 		dhcp_finished ( dhcp, 0 );
 		return;
 	}
@@ -668,9 +837,14 @@ static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
 	struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
 	int rc;
 
+	/* Set server address */
+	peer->sin_addr = *(dhcp->pxe_attempt);
+	peer->sin_port = ( ( peer->sin_addr.s_addr == INADDR_BROADCAST ) ?
+			   htons ( BOOTPS_PORT ) : htons ( PXE_PORT ) );
+
 	DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n",
-	       dhcp, inet_ntoa ( *(dhcp->pxe_attempt) ), PXE_PORT,
-	       ntohs ( dhcp->pxe_type ) );
+	       dhcp, inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ),
+	       le16_to_cpu ( dhcp->pxe_type ) );
 
 	/* Set boot menu item */
 	menu_item.type = dhcp->pxe_type;
@@ -678,10 +852,6 @@ static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
 				    &menu_item, sizeof ( menu_item ) ) ) != 0 )
 		return rc;
 
-	/* Set server address */
-	peer->sin_addr = *(dhcp->pxe_attempt);
-	peer->sin_port = htons ( PXE_PORT );
-
 	return 0;
 }
 
@@ -741,7 +911,8 @@ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
 	DBGC ( dhcp, "\n" );
 
 	/* Filter out unacceptable responses */
-	if ( peer->sin_port != htons ( PXE_PORT ) )
+	if ( ( peer->sin_port != htons ( BOOTPS_PORT ) ) &&
+	     ( peer->sin_port != htons ( PXE_PORT ) ) )
 		return;
 	if ( msgtype != DHCPACK )
 		return;
@@ -807,6 +978,46 @@ static struct dhcp_session_state dhcp_state_pxebs = {
  */
 
 /**
+ * Construct DHCP client hardware address field and broadcast flag
+ *
+ * @v netdev		Network device
+ * @v hlen		DHCP hardware address length to fill in
+ * @v flags		DHCP flags to fill in
+ * @ret chaddr		DHCP client hardware address
+ */
+void * dhcp_chaddr ( struct net_device *netdev, uint8_t *hlen,
+		     uint16_t *flags ) {
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
+	typeof ( ( ( struct dhcphdr * ) NULL )->chaddr ) chaddr;
+
+	/* If the link-layer address cannot fit into the chaddr field
+	 * (as is the case for IPoIB) then try using the hardware
+	 * address instead.  If we do this, set the broadcast flag,
+	 * since chaddr then does not represent a valid link-layer
+	 * address for the return path.
+	 *
+	 * If even the hardware address is too large, use an empty
+	 * chaddr field and set the broadcast flag.
+	 *
+	 * This goes against RFC4390, but RFC4390 mandates that we use
+	 * a DHCP client identifier that conforms with RFC4361, which
+	 * we cannot do without either persistent (NIC-independent)
+	 * storage, or by eliminating the hardware address completely
+	 * from the DHCP packet, which seems unfriendly to users.
+	 */
+	if ( ( *hlen = ll_protocol->ll_addr_len ) <= sizeof ( chaddr ) ) {
+		return netdev->ll_addr;
+	}
+	*flags = htons ( BOOTP_FL_BROADCAST );
+	if ( ( *hlen = ll_protocol->hw_addr_len ) <= sizeof ( chaddr ) ) {
+		return netdev->hw_addr;
+	} else {
+		*hlen = 0;
+		return NULL;
+	}
+}
+
+/**
  * Create a DHCP packet
  *
  * @v dhcppkt		DHCP packet structure to fill in
@@ -826,7 +1037,7 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
 			 const void *options, size_t options_len,
 			 void *data, size_t max_len ) {
 	struct dhcphdr *dhcphdr = data;
-	unsigned int hlen;
+	void *chaddr;
 	int rc;
 
 	/* Sanity check */
@@ -839,16 +1050,8 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
 	dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
 	dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
 	dhcphdr->op = dhcp_op[msgtype];
-	/* If hardware length exceeds the chaddr field length, don't
-	 * use the chaddr field.  This is as per RFC4390.
-	 */
-	hlen = netdev->ll_protocol->ll_addr_len;
-	if ( hlen > sizeof ( dhcphdr->chaddr ) ) {
-		hlen = 0;
-		dhcphdr->flags = htons ( BOOTP_FL_BROADCAST );
-	}
-	dhcphdr->hlen = hlen;
-	memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
+	chaddr = dhcp_chaddr ( netdev, &dhcphdr->hlen, &dhcphdr->flags );
+	memcpy ( dhcphdr->chaddr, chaddr, dhcphdr->hlen );
 	memcpy ( dhcphdr->options, options, options_len );
 
 	/* Initialise DHCP packet structure */
@@ -880,10 +1083,10 @@ int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
 int dhcp_create_request ( struct dhcp_packet *dhcppkt,
 			  struct net_device *netdev, unsigned int msgtype,
 			  struct in_addr ciaddr, void *data, size_t max_len ) {
-	struct device_description *desc = &netdev->dev->desc;
 	struct dhcp_netdev_desc dhcp_desc;
 	struct dhcp_client_id client_id;
 	struct dhcp_client_uuid client_uuid;
+	uint8_t *dhcp_features;
 	size_t dhcp_features_len;
 	size_t ll_addr_len;
 	ssize_t len;
@@ -903,7 +1106,8 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt,
 	dhcppkt->dhcphdr->ciaddr = ciaddr;
 
 	/* Add options to identify the feature list */
-	dhcp_features_len = ( dhcp_features_end - dhcp_features );
+	dhcp_features = table_start ( DHCP_FEATURES );
+	dhcp_features_len = table_num_entries ( DHCP_FEATURES );
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
 				    dhcp_features_len ) ) != 0 ) {
 		DBG ( "DHCP could not set features list option: %s\n",
@@ -912,9 +1116,8 @@ int dhcp_create_request ( struct dhcp_packet *dhcppkt,
 	}
 
 	/* Add options to identify the network device */
-	dhcp_desc.type = desc->bus_type;
-	dhcp_desc.vendor = htons ( desc->vendor );
-	dhcp_desc.device = htons ( desc->device );
+	fetch_setting ( &netdev->settings.settings, &busid_setting, &dhcp_desc,
+		sizeof ( dhcp_desc ) );
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc,
 				    sizeof ( dhcp_desc ) ) ) != 0 ) {
 		DBG ( "DHCP could not set bus ID option: %s\n",
@@ -1109,7 +1312,7 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
 /** DHCP data transfer interface operations */
 static struct xfer_interface_operations dhcp_xfer_operations = {
 	.close		= ignore_xfer_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= dhcp_deliver_iob,
@@ -1184,16 +1387,28 @@ static struct sockaddr dhcp_peer = {
  *
  * @v job		Job control interface
  * @v netdev		Network device
- * @ret rc		Return status code
+ * @ret rc		Return status code, or positive if cached
  *
  * Starts DHCP on the specified network device.  If successful, the
  * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
  * option sources.
+ *
+ * On a return of 0, a background job has been started to perform the
+ * DHCP request. Any nonzero return means the job has not been
+ * started; a positive return value indicates the success condition of
+ * having fetched the appropriate data from cached information.
  */
 int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
 	struct dhcp_session *dhcp;
 	int rc;
 
+	/* Check for cached DHCP information */
+	get_cached_dhcpack();
+	if ( fetch_uintz_setting ( NULL, &use_cached_setting ) ) {
+		DBG ( "DHCP using cached network settings\n" );
+		return 1;
+	}
+
 	/* Allocate and initialise structure */
 	dhcp = zalloc ( sizeof ( *dhcp ) );
 	if ( ! dhcp )
@@ -1309,7 +1524,7 @@ int start_pxebs ( struct job_interface *job, struct net_device *netdev,
 	fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting,
 			     &dhcp->local.sin_addr );
 	dhcp->local.sin_port = htons ( BOOTPC_PORT );
-	dhcp->pxe_type = htons ( pxe_type );
+	dhcp->pxe_type = cpu_to_le16 ( pxe_type );
 	dhcp->timer.expired = dhcp_timer_expired;
 
 	/* Construct PXE boot server IP address lists */
diff --git a/gpxe/src/net/udp/dns.c b/gpxe/src/net/udp/dns.c
index a498aef..f94094a 100644
--- a/gpxe/src/net/udp/dns.c
+++ b/gpxe/src/net/udp/dns.c
@@ -19,6 +19,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -459,7 +461,7 @@ static void dns_xfer_close ( struct xfer_interface *socket, int rc ) {
 /** DNS socket operations */
 static struct xfer_interface_operations dns_socket_operations = {
 	.close		= dns_xfer_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= xfer_deliver_as_raw,
diff --git a/gpxe/src/net/udp/slam.c b/gpxe/src/net/udp/slam.c
index 6add99b..396f69b 100644
--- a/gpxe/src/net/udp/slam.c
+++ b/gpxe/src/net/udp/slam.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -614,7 +616,7 @@ static void slam_socket_close ( struct xfer_interface *socket, int rc ) {
 /** SLAM unicast socket data transfer operations */
 static struct xfer_interface_operations slam_socket_operations = {
 	.close		= slam_socket_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= slam_socket_deliver,
@@ -640,7 +642,7 @@ static void slam_mc_socket_close ( struct xfer_interface *mc_socket, int rc ){
 /** SLAM multicast socket data transfer operations */
 static struct xfer_interface_operations slam_mc_socket_operations = {
 	.close		= slam_mc_socket_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= slam_mc_socket_deliver,
diff --git a/gpxe/src/net/udp/tftp.c b/gpxe/src/net/udp/tftp.c
index 19525f7..3de2fb9 100644
--- a/gpxe/src/net/udp/tftp.c
+++ b/gpxe/src/net/udp/tftp.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -131,6 +133,8 @@ enum {
 	TFTP_FL_RRQ_MULTICAST = 0x0004,
 	/** Perform MTFTP recovery on timeout */
 	TFTP_FL_MTFTP_RECOVERY = 0x0008,
+	/** Only get filesize and then abort the transfer */
+	TFTP_FL_SIZEONLY = 0x0010,
 };
 
 /** Maximum number of MTFTP open requests before falling back to TFTP */
@@ -409,6 +413,42 @@ static int tftp_send_ack ( struct tftp_request *tftp ) {
 }
 
 /**
+ * Transmit ERROR (Abort)
+ *
+ * @v tftp		TFTP connection
+ * @v errcode		TFTP error code
+ * @v errmsg		Error message string
+ * @ret rc		Return status code
+ */
+static int tftp_send_error ( struct tftp_request *tftp, int errcode,
+			     const char *errmsg ) {
+	struct tftp_error *err;
+	struct io_buffer *iobuf;
+	struct xfer_metadata meta = {
+		.dest = ( struct sockaddr * ) &tftp->peer,
+	};
+	size_t msglen;
+
+	DBGC2 ( tftp, "TFTP %p sending ERROR %d: %s\n", tftp, errcode,
+		errmsg );
+
+	/* Allocate buffer */
+	msglen = sizeof ( *err ) + strlen ( errmsg ) + 1 /* NUL */;
+	iobuf = xfer_alloc_iob ( &tftp->socket, msglen );
+	if ( ! iobuf )
+		return -ENOMEM;
+
+	/* Build ERROR */
+	err = iob_put ( iobuf, msglen );
+	err->opcode = htons ( TFTP_ERROR );
+	err->errcode = htons ( errcode );
+	strcpy ( err->errmsg, errmsg );
+
+	/* ERR always goes to the peer recorded from the RRQ response */
+	return xfer_deliver_iob_meta ( &tftp->socket, iobuf, &meta );
+}
+
+/**
  * Transmit next relevant packet
  *
  * @v tftp		TFTP connection
@@ -416,9 +456,16 @@ static int tftp_send_ack ( struct tftp_request *tftp ) {
  */
 static int tftp_send_packet ( struct tftp_request *tftp ) {
 
-	/* Update retransmission timer */
+	/* Update retransmission timer.  While name resolution takes place the
+	 * window is zero.  Avoid unnecessary delay after name resolution
+	 * completes by retrying immediately.
+	 */
 	stop_timer ( &tftp->timer );
-	start_timer ( &tftp->timer );
+	if ( xfer_window ( &tftp->socket ) ) {
+		start_timer ( &tftp->timer );
+	} else {
+		start_timer_nodelay ( &tftp->timer );
+	}
 
 	/* Send RRQ or ACK as appropriate */
 	if ( ! tftp->peer.st_family ) {
@@ -670,6 +717,7 @@ static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) {
 	char *end = buf + len;
 	char *name;
 	char *value;
+	char *next;
 	int rc = 0;
 
 	/* Sanity check */
@@ -679,26 +727,41 @@ static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) {
 		rc = -EINVAL;
 		goto done;
 	}
-	if ( end[-1] != '\0' ) {
-		DBGC ( tftp, "TFTP %p received OACK missing final NUL\n",
-		       tftp );
-		rc = -EINVAL;
-		goto done;
-	}
 
 	/* Process each option in turn */
-	name = oack->data;
-	while ( name < end ) {
-		value = ( name + strlen ( name ) + 1 );
+	for ( name = oack->data ; name < end ; name = next ) {
+
+		/* Parse option name and value
+		 *
+		 * We treat parsing errors as non-fatal, because there
+		 * exists at least one TFTP server (IBM Tivoli PXE
+		 * Server 5.1.0.3) that has been observed to send
+		 * malformed OACKs containing trailing garbage bytes.
+		 */
+		value = ( name + strnlen ( name, ( end - name ) ) + 1 );
+		if ( value > end ) {
+			DBGC ( tftp, "TFTP %p received OACK with malformed "
+			       "option name:\n", tftp );
+			DBGC_HD ( tftp, oack, len );
+			break;
+		}
 		if ( value == end ) {
 			DBGC ( tftp, "TFTP %p received OACK missing value "
 			       "for option \"%s\"\n", tftp, name );
-			rc = -EINVAL;
-			goto done;
+			DBGC_HD ( tftp, oack, len );
+			break;
 		}
+		next = ( value + strnlen ( value, ( end - value ) ) + 1 );
+		if ( next > end ) {
+			DBGC ( tftp, "TFTP %p received OACK with malformed "
+			       "value for option \"%s\":\n", tftp, name );
+			DBGC_HD ( tftp, oack, len );
+			break;
+		}
+
+		/* Process option */
 		if ( ( rc = tftp_process_option ( tftp, name, value ) ) != 0 )
 			goto done;
-		name = ( value + strlen ( value ) + 1 );
 	}
 
 	/* Process tsize information, if available */
@@ -707,6 +770,14 @@ static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) {
 			goto done;
 	}
 
+	/* Abort request if only trying to determine file size */
+	if ( tftp->flags & TFTP_FL_SIZEONLY ) {
+		rc = 0;
+		tftp_send_error ( tftp, 0, "TFTP Aborted" );
+		tftp_done ( tftp, rc );
+		return rc;
+	}
+
 	/* Request next data block */
 	tftp_send_packet ( tftp );
 
@@ -729,11 +800,18 @@ static int tftp_rx_data ( struct tftp_request *tftp,
 			  struct io_buffer *iobuf ) {
 	struct tftp_data *data = iobuf->data;
 	struct xfer_metadata meta;
-	int block;
+	unsigned int block;
 	off_t offset;
 	size_t data_len;
 	int rc;
 
+	if ( tftp->flags & TFTP_FL_SIZEONLY ) {
+		/* If we get here then server doesn't support SIZE option */
+		rc = -ENOTSUP;
+		tftp_send_error ( tftp, 0, "TFTP Aborted" );
+		goto done;
+	}
+
 	/* Sanity check */
 	if ( iob_len ( iobuf ) < sizeof ( *data ) ) {
 		DBGC ( tftp, "TFTP %p received underlength DATA packet "
@@ -741,14 +819,17 @@ static int tftp_rx_data ( struct tftp_request *tftp,
 		rc = -EINVAL;
 		goto done;
 	}
-	if ( data->block == 0 ) {
+
+	/* Calculate block number */
+	block = ( ( bitmap_first_gap ( &tftp->bitmap ) + 1 ) & ~0xffff );
+	if ( data->block == 0 && block == 0 ) {
 		DBGC ( tftp, "TFTP %p received data block 0\n", tftp );
 		rc = -EINVAL;
 		goto done;
 	}
+	block += ( ntohs ( data->block ) - 1 );
 
 	/* Extract data */
-	block = ( ntohs ( data->block ) - 1 );
 	offset = ( block * tftp->blksize );
 	iob_pull ( iobuf, sizeof ( *data ) );
 	data_len = iob_len ( iobuf );
@@ -934,7 +1015,7 @@ static int tftp_socket_deliver_iob ( struct xfer_interface *socket,
 /** TFTP socket operations */
 static struct xfer_interface_operations tftp_socket_operations = {
 	.close		= ignore_xfer_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= tftp_socket_deliver_iob,
@@ -961,7 +1042,7 @@ static int tftp_mc_socket_deliver_iob ( struct xfer_interface *mc_socket,
 /** TFTP multicast socket operations */
 static struct xfer_interface_operations tftp_mc_socket_operations = {
 	.close		= ignore_xfer_close,
-	.vredirect	= xfer_vopen,
+	.vredirect	= xfer_vreopen,
 	.window		= unlimited_xfer_window,
 	.alloc_iob	= default_xfer_alloc_iob,
 	.deliver_iob	= tftp_mc_socket_deliver_iob,
@@ -1093,6 +1174,26 @@ struct uri_opener tftp_uri_opener __uri_opener = {
 };
 
 /**
+ * Initiate TFTP-size request
+ *
+ * @v xfer		Data transfer interface
+ * @v uri		Uniform Resource Identifier
+ * @ret rc		Return status code
+ */
+static int tftpsize_open ( struct xfer_interface *xfer, struct uri *uri ) {
+	return tftp_core_open ( xfer, uri, TFTP_PORT, NULL,
+				( TFTP_FL_RRQ_SIZES |
+				  TFTP_FL_SIZEONLY ) );
+
+}
+
+/** TFTP URI opener */
+struct uri_opener tftpsize_uri_opener __uri_opener = {
+	.scheme	= "tftpsize",
+	.open	= tftpsize_open,
+};
+
+/**
  * Initiate TFTM download
  *
  * @v xfer		Data transfer interface
diff --git a/gpxe/src/proto/fsp.c b/gpxe/src/proto/fsp.c
deleted file mode 100644
index 9a10991..0000000
--- a/gpxe/src/proto/fsp.c
+++ /dev/null
@@ -1,243 +0,0 @@
-    /*********************************************************************\
-    * Copyright (c) 2005 by Radim Kolar (hsn-sendmail.cz)                 *
-    *                                                                     *
-    * You may copy or modify this file in any manner you wish, provided   *
-    * that this notice is always included, and that you hold the author   *
-    * harmless for any loss or damage resulting from the installation or  *
-    * use of this software.                                               *
-    *                                                                     *
-    * This file provides support for FSP v2 protocol written from scratch *
-    * by Radim Kolar,   FSP project leader.                               *
-    *                                                                     *
-    * ABOUT FSP                                                           *
-    * FSP is a lightweight file transfer protocol and is being used for   *
-    * booting, Internet firmware updates, embedded devices and in         *
-    * wireless applications. FSP is very easy to implement; contact Radim *
-    * Kolar if you need hand optimized assembler FSP stacks for various   *
-    * microcontrollers, CPUs or consultations.                            *
-    * http://fsp.sourceforge.net/                                         *
-    *                                                                     *
-    * REVISION HISTORY                                                    *
-    * 1.0 2005-03-17 rkolar   Initial coding                              *
-    * 1.1 2005-03-24 rkolar   We really need to send CC_BYE to the server *
-    *                         at end of transfer, because next stage boot *
-    *                         loader is unable to contact FSP server      *
-    *                         until session timeouts.                     *
-    * 1.2 2005-03-26 rkolar   We need to query filesize in advance,       *
-    *                         because NBI loader do not reads file until  *
-    *                         eof is reached.
-    * REMARKS                                                             *
-    * there is no support for selecting port number of fsp server, maybe  *
-    *   we should parse fsp:// URLs in boot image filename.               *
-    * this implementation has filename limit 255 chars.                   *
-    \*********************************************************************/
-
-#ifdef DOWNLOAD_PROTO_FSP
-
-#define FSP_PORT 21
-
-/* FSP commands */
-#define CC_GET_FILE	0x42
-#define CC_BYE		0x4A
-#define CC_ERR		0x40
-#define CC_STAT		0x4D
-
-/* etherboot limits */
-#define FSP_MAXFILENAME 255
-
-struct fsp_info {
-	in_addr server_ip;
-	uint16_t server_port;
-	uint16_t local_port;
-	const char *filename;
-	int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
-};
-
-struct fsp_header {
-    	uint8_t cmd;
-	uint8_t sum;
-	uint16_t key;
-	uint16_t seq;
-	uint16_t len;
-	uint32_t pos;
-} PACKED;
-
-#define FSP_MAXPAYLOAD (ETH_MAX_MTU - \
-  (sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct fsp_header)))
-
-static struct fsp_request {
-	struct iphdr ip;
-	struct udphdr udp;
-	struct fsp_header fsp;
-	unsigned char data[FSP_MAXFILENAME + 1 + 2];
-} request;
-
-struct fsp_reply {
-	struct iphdr ip;
-	struct udphdr udp;
-	struct fsp_header fsp;
-	unsigned char data[FSP_MAXPAYLOAD];
-} PACKED;
-
-
-static int await_fsp(int ival, void *ptr, unsigned short ptype __unused,
-                      struct iphdr *ip, struct udphdr *udp)
-{
-	if(!udp)
-	    return 0;
-	if (ip->dest.s_addr != arptable[ARP_CLIENT].ipaddr.s_addr) 
-	    return 0;
-        if (ntohs(udp->dest) != ival)
-            return 0;
-	if (ntohs(udp->len) < 12+sizeof(struct udphdr))
-	    return 0;
-	return 1;
-}
-
-static int proto_fsp(struct fsp_info *info)
-{
-    uint32_t filepos;
-    uint32_t filelength=0;
-    int i,retry;
-    uint16_t reqlen;
-    struct fsp_reply *reply;
-    int block=1;
-    
-    /* prepare FSP request packet */
-    filepos=0;
-    i=strlen(info->filename);
-    if(i>FSP_MAXFILENAME)
-    {
-	printf("Boot filename is too long.\n");
-	return 0;
-    }
-    strcpy(request.data,info->filename);
-    *(uint16_t *)(request.data+i+1)=htons(FSP_MAXPAYLOAD);
-    request.fsp.len=htons(i+1);
-    reqlen=i+3+12;
-
-    rx_qdrain();
-    retry=0;
-
-    /* main loop */
-    for(;;) {
-	int  sum;
-	long timeout;
-
-        /* query filelength if not known */
-	if(filelength == 0)
-	    request.fsp.cmd=CC_STAT;
-		
-	/* prepare request packet */
-	request.fsp.pos=htonl(filepos);
-	request.fsp.seq=random();
-	request.fsp.sum=0;
-	for(i=0,sum=reqlen;i<reqlen;i++)
-	{
-	    sum += ((uint8_t *)&request.fsp)[i];
-        }
-	request.fsp.sum= sum + (sum >> 8);
-	/* send request */
-        if (!udp_transmit(info->server_ip.s_addr, info->local_port,
-	                 info->server_port, sizeof(request.ip) +
-			 sizeof(request.udp) + reqlen, &request))
-	                    return (0);
-	/* wait for retry */		    
-#ifdef  CONGESTED
-        timeout =
-            rfc2131_sleep_interval(filepos ? TFTP_REXMT : TIMEOUT, retry);
-#else
-	timeout = rfc2131_sleep_interval(TIMEOUT, retry);
-#endif
-	retry++;
-        if (!await_reply(await_fsp, info->local_port, NULL, timeout))
-	    continue;
-	reply=(struct fsp_reply *) &nic.packet[ETH_HLEN];    
-	/* check received packet */
-	if (reply->fsp.seq != request.fsp.seq)
-	    continue;
-	reply->udp.len=ntohs(reply->udp.len)-sizeof(struct udphdr);
-	if(reply->udp.len < ntohs(reply->fsp.len) + 12 )
-	    continue;
-        sum=-reply->fsp.sum;
-	for(i=0;i<reply->udp.len;i++)
-	{
-	    sum += ((uint8_t *)&(reply->fsp))[i];
-        }
-        sum = (sum + (sum >> 8)) & 0xff;
-	if(sum != reply->fsp.sum)
-	{
-	    printf("FSP checksum failed. computed %d, but packet has %d.\n",sum,reply->fsp.sum);
-	    continue;
-	}
-	if(reply->fsp.cmd == CC_ERR)
-	{
-	    printf("\nFSP error: %s",info->filename);
-	    if(reply->fsp.len)
-	        printf(" [%s]",reply->data);
-	    printf("\n");
-	    return 0;
-	}
-	if(reply->fsp.cmd == CC_BYE && filelength == 1)
-	{
-	    info->fnc(request.data,block,1,1);
-	    return 1;
-	}
-	if(reply->fsp.cmd == CC_STAT)
-	{
-	    if(reply->data[8] == 0)
-	    {
-		/* file not found, etc. */
-		filelength=0xffffffff;
-	    } else
-	    {
-		filelength= ntohl(*((uint32_t *)&reply->data[4]));
-	    }
-	    request.fsp.cmd = CC_GET_FILE;
-	    request.fsp.key = reply->fsp.key;
-	    retry=0;
-	    continue;
-	}
-
-	if(reply->fsp.cmd == CC_GET_FILE)
-	{
-	    if(ntohl(reply->fsp.pos) != filepos)
-		continue;
-	    request.fsp.key = reply->fsp.key;
-	    retry=0;
-	    i=ntohs(reply->fsp.len);
-	    if(i == 1)
-	    {
-		request.fsp.cmd=CC_BYE;
-		request.data[0]=reply->data[0];
-		continue;
-	    }
-	    /* let last byte alone */
-            if(i >= filelength)
-		i = filelength - 1;
-	    if(!info->fnc(reply->data,block++,i,0))
-		return 0;
-	    filepos += i;
-	    filelength -= i;
-	}
-    }
-
-    return 0;
-}
-
-int url_fsp(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
-{
-	struct fsp_info info;
-	/* Set the defaults */
-	info.server_ip.s_addr    = arptable[ARP_SERVER].ipaddr.s_addr;
-	info.server_port         = FSP_PORT;
-	info.local_port		 = 1024 + random() & 0xfbff;
-	info.fnc                 = fnc;
-	
-	/* Now parse the url */
-	/* printf("fsp-URI: [%s]\n", name); */
-        /* quick hack for now */
-	info.filename=name;
-	return proto_fsp(&info);
-}
-#endif
diff --git a/gpxe/src/proto/igmp.c b/gpxe/src/proto/igmp.c
deleted file mode 100644
index d711421..0000000
--- a/gpxe/src/proto/igmp.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Eric Biederman wrote this code originally.
- *
- */
-
-#if 0
-
-#include <ip.h>
-#include <igmp.h>
-
-static unsigned long last_igmpv1 = 0;
-static struct igmptable_t igmptable[MAX_IGMP];
-
-static long rfc1112_sleep_interval ( long base, int exp ) {
-	unsigned long divisor, tmo;
-
-	if ( exp > BACKOFF_LIMIT )
-		exp = BACKOFF_LIMIT;
-	divisor = RAND_MAX / ( base << exp );
-	tmo = random() / divisor;
-	return tmo;
-}
-
-static void send_igmp_reports ( unsigned long now ) {
-	struct igmp_ip_t igmp;
-	int i;
-
-	for ( i = 0 ; i < MAX_IGMP ; i++ ) {
-		if ( ! igmptable[i].time )
-			continue;
-		if ( now < igmptable[i].time )
-			continue;
-
-		igmp.router_alert[0] = 0x94;
-		igmp.router_alert[1] = 0x04;
-		igmp.router_alert[2] = 0;
-		igmp.router_alert[3] = 0;
-		build_ip_hdr ( igmptable[i].group.s_addr, 1, IP_IGMP,
-			       sizeof ( igmp.router_alert ),
-			       sizeof ( igmp ), &igmp );
-		igmp.igmp.type = IGMPv2_REPORT;
-		if ( last_igmpv1 && 
-		     ( now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT ) ) {
-			igmp.igmp.type = IGMPv1_REPORT;
-		}
-		igmp.igmp.response_time = 0;
-		igmp.igmp.chksum = 0;
-		igmp.igmp.group.s_addr = igmptable[i].group.s_addr;
-		igmp.igmp.chksum = ipchksum ( &igmp.igmp,
-					      sizeof ( igmp.igmp ) );
-		ip_transmit ( sizeof ( igmp ), &igmp );
-		DBG ( "IGMP sent report to %s\n", inet_ntoa ( igmp.igmp.group ) );
-		/* Don't send another igmp report until asked */
-		igmptable[i].time = 0;
-	}
-}
-
-static void process_igmp ( unsigned long now, unsigned short ptype __unused,
-			   struct iphdr *ip ) {
-	struct igmp *igmp;
-	int i;
-	unsigned iplen;
-
-	if ( ( ! ip ) || ( ip->protocol != IP_IGMP ) ||
-	     ( nic.packetlen < ( sizeof ( struct iphdr ) +
-				 sizeof ( struct igmp ) ) ) ) {
-		return;
-	}
-
-	iplen = ( ip->verhdrlen & 0xf ) * 4;
-	igmp = ( struct igmp * ) &nic.packet[ sizeof( struct iphdr ) ];
-	if ( ipchksum ( igmp, ntohs ( ip->len ) - iplen ) != 0 )
-		return;
-
-	if ( ( igmp->type == IGMP_QUERY ) && 
-	     ( ip->dest.s_addr == htonl ( GROUP_ALL_HOSTS ) ) ) {
-		unsigned long interval = IGMP_INTERVAL;
-
-		if ( igmp->response_time == 0 ) {
-			last_igmpv1 = now;
-		} else {
-			interval = ( igmp->response_time * TICKS_PER_SEC ) /10;
-		}
-		
-		DBG ( "IGMP received query for %s\n", inet_ntoa ( igmp->group ) );
-		for ( i = 0 ; i < MAX_IGMP ; i++ ) {
-			uint32_t group = igmptable[i].group.s_addr;
-			if ( ( group == 0 ) ||
-			     ( group == igmp->group.s_addr ) ) {
-				unsigned long time;
-				time = currticks() +
-					rfc1112_sleep_interval ( interval, 0 );
-				if ( time < igmptable[i].time ) {
-					igmptable[i].time = time;
-				}
-			}
-		}
-	}
-	if ( ( ( igmp->type == IGMPv1_REPORT ) ||
-	       ( igmp->type == IGMPv2_REPORT ) ) &&
-	     ( ip->dest.s_addr == igmp->group.s_addr ) ) {
-	        DBG ( "IGMP received report for %s\n", 
-		      inet_ntoa ( igmp->group ) );
-		for ( i = 0 ; i < MAX_IGMP ; i++ ) {
-			if ( ( igmptable[i].group.s_addr ==
-			       igmp->group.s_addr ) &&
-			     ( igmptable[i].time != 0 ) ) {
-				igmptable[i].time = 0;
-			}
-		}
-	}
-}
-
-struct background igmp_background __background = {
-	.send = send_igmp_reports,
-	.process = process_igmp,
-};
-
-void leave_group ( int slot ) {
-	/* Be very stupid and always send a leave group message if 
-	 * I have subscribed.  Imperfect but it is standards
-	 * compliant, easy and reliable to implement.
-	 *
-	 * The optimal group leave method is to only send leave when,
-	 * we were the last host to respond to a query on this group,
-	 * and igmpv1 compatibility is not enabled.
-	 */
-	if ( igmptable[slot].group.s_addr ) {
-		struct igmp_ip_t igmp;
-
-		igmp.router_alert[0] = 0x94;
-		igmp.router_alert[1] = 0x04;
-		igmp.router_alert[2] = 0;
-		igmp.router_alert[3] = 0;
-		build_ip_hdr ( htonl ( GROUP_ALL_HOSTS ), 1, IP_IGMP,
-			       sizeof ( igmp.router_alert ), sizeof ( igmp ),
-			       &igmp);
-		igmp.igmp.type = IGMP_LEAVE;
-		igmp.igmp.response_time = 0;
-		igmp.igmp.chksum = 0;
-		igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
-		igmp.igmp.chksum = ipchksum ( &igmp.igmp, sizeof ( igmp ) );
-		ip_transmit ( sizeof ( igmp ), &igmp );
-		DBG ( "IGMP left group %s\n", inet_ntoa ( igmp.igmp.group ) );
-	}
-	memset ( &igmptable[slot], 0, sizeof ( igmptable[0] ) );
-}
-
-void join_group ( int slot, unsigned long group ) {
-	/* I have already joined */
-	if ( igmptable[slot].group.s_addr == group )
-		return;
-	if ( igmptable[slot].group.s_addr ) {
-		leave_group ( slot );
-	}
-	/* Only join a group if we are given a multicast ip, this way
-	 * code can be given a non-multicast (broadcast or unicast ip)
-	 * and still work... 
-	 */
-	if ( ( group & htonl ( MULTICAST_MASK ) ) ==
-	     htonl ( MULTICAST_NETWORK ) ) {
-		igmptable[slot].group.s_addr = group;
-		igmptable[slot].time = currticks();
-	}
-}
-
-#endif
diff --git a/gpxe/src/proto/nfs.c b/gpxe/src/proto/nfs.c
deleted file mode 100644
index 943081a..0000000
--- a/gpxe/src/proto/nfs.c
+++ /dev/null
@@ -1,616 +0,0 @@
-#if 0
-
-#include <gpxe/init.h>
-#include <gpxe/in.h>
-
-/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
- * large portions are copied verbatim) as distributed in OSKit 0.97.  A few
- * changes were necessary to adapt the code to Etherboot and to fix several
- * inconsistencies.  Also the RPC message preparation is done "by hand" to
- * avoid adding netsprintf() which I find hard to understand and use.  */
-
-/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
- * it loads the kernel image off the boot server (ARP_SERVER) and does not
- * access the client root disk (root-path in dhcpd.conf), which would use
- * ARP_ROOTSERVER.  The root disk is something the operating system we are
- * about to load needs to use.  This is different from the OSKit 0.97 logic.  */
-
-/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
- * If a symlink is encountered, it is followed as far as possible (recursion
- * possible, maximum 16 steps). There is no clearing of ".."'s inside the
- * path, so please DON'T DO THAT. thx. */
-
-#define START_OPORT 700		/* mountd usually insists on secure ports */
-#define OPORT_SWEEP 200		/* make sure we don't leave secure range */
-
-static int oport = START_OPORT;
-static struct sockaddr_in mount_server;
-static struct sockaddr_in nfs_server;
-static unsigned long rpc_id;
-
-/**************************************************************************
-RPC_INIT - set up the ID counter to something fairly random
-**************************************************************************/
-void rpc_init(void)
-{
-	unsigned long t;
-
-	t = currticks();
-	rpc_id = t ^ (t << 8) ^ (t << 16);
-}
-
-/**************************************************************************
-RPC_PRINTERROR - Print a low level RPC error message
-**************************************************************************/
-static void rpc_printerror(struct rpc_t *rpc)
-{
-	if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
-	    rpc->u.reply.astatus) {
-		/* rpc_printerror() is called for any RPC related error,
-		 * suppress output if no low level RPC error happened.  */
-		DBG("RPC error: (%ld,%ld,%ld)\n", ntohl(rpc->u.reply.rstatus),
-		    ntohl(rpc->u.reply.verifier),
-		    ntohl(rpc->u.reply.astatus));
-	}
-}
-
-/**************************************************************************
-AWAIT_RPC - Wait for an rpc packet
-**************************************************************************/
-static int await_rpc(int ival, void *ptr,
-		     unsigned short ptype __unused, struct iphdr *ip,
-		     struct udphdr *udp, struct tcphdr *tcp __unused)
-{
-	struct rpc_t *rpc;
-	if (!udp) 
-		return 0;
-	if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
-		return 0;
-	if (ntohs(udp->dest) != ival)
-		return 0;
-	if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
-		return 0;
-	rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-	if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
-		return 0;
-	if (MSG_REPLY != ntohl(rpc->u.reply.type))
-		return 0;
-	return 1;
-}
-
-/**************************************************************************
-RPC_LOOKUP - Lookup RPC Port numbers
-**************************************************************************/
-static int rpc_lookup(struct sockaddr_in *addr, int prog, int ver, int sport)
-{
-	struct rpc_t buf, *rpc;
-	unsigned long id;
-	int retries;
-	long *p;
-
-	id = rpc_id++;
-	buf.u.call.id = htonl(id);
-	buf.u.call.type = htonl(MSG_CALL);
-	buf.u.call.rpcvers = htonl(2);	/* use RPC version 2 */
-	buf.u.call.prog = htonl(PROG_PORTMAP);
-	buf.u.call.vers = htonl(2);	/* portmapper is version 2 */
-	buf.u.call.proc = htonl(PORTMAP_GETPORT);
-	p = (long *)buf.u.call.data;
-	*p++ = 0; *p++ = 0;				/* auth credential */
-	*p++ = 0; *p++ = 0;				/* auth verifier */
-	*p++ = htonl(prog);
-	*p++ = htonl(ver);
-	*p++ = htonl(IP_UDP);
-	*p++ = 0;
-	for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
-		long timeout;
-		udp_transmit(addr->sin_addr.s_addr, sport, addr->sin_port,
-			(char *)p - (char *)&buf, &buf);
-		timeout = rfc2131_sleep_interval(TIMEOUT, retries);
-		if (await_reply(await_rpc, sport, &id, timeout)) {
-			rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-			if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
-			    rpc->u.reply.astatus) {
-				rpc_printerror(rpc);
-				return 0;
-			} else {
-				return ntohl(rpc->u.reply.data[0]);
-			}
-		}
-	}
-	return 0;
-}
-
-/**************************************************************************
-RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
-**************************************************************************/
-static long *rpc_add_credentials(long *p)
-{
-	int hl;
-
-	/* Here's the executive summary on authentication requirements of the
-	 * various NFS server implementations:  Linux accepts both AUTH_NONE
-	 * and AUTH_UNIX authentication (also accepts an empty hostname field
-	 * in the AUTH_UNIX scheme).  *BSD refuses AUTH_NONE, but accepts
-	 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
-	 * scheme).  To be safe, use AUTH_UNIX and pass the hostname if we have
-	 * it (if the BOOTP/DHCP reply didn't give one, just use an empty
-	 * hostname).  */
-
-	hl = (hostnamelen + 3) & ~3;
-
-	/* Provide an AUTH_UNIX credential.  */
-	*p++ = htonl(1);		/* AUTH_UNIX */
-	*p++ = htonl(hl+20);		/* auth length */
-	*p++ = htonl(0);		/* stamp */
-	*p++ = htonl(hostnamelen);	/* hostname string */
-	if (hostnamelen & 3) {
-		*(p + hostnamelen / 4) = 0; /* add zero padding */
-	}
-	memcpy(p, hostname, hostnamelen);
-	p += hl / 4;
-	*p++ = 0;			/* uid */
-	*p++ = 0;			/* gid */
-	*p++ = 0;			/* auxiliary gid list */
-
-	/* Provide an AUTH_NONE verifier.  */
-	*p++ = 0;			/* AUTH_NONE */
-	*p++ = 0;			/* auth length */
-
-	return p;
-}
-
-/**************************************************************************
-NFS_PRINTERROR - Print a NFS error message
-**************************************************************************/
-static void nfs_printerror(int err)
-{
-	switch (-err) {
-	case NFSERR_PERM:
-		printf("Not owner\n");
-		break;
-	case NFSERR_NOENT:
-		printf("No such file or directory\n");
-		break;
-	case NFSERR_ACCES:
-		printf("Permission denied\n");
-		break;
-	case NFSERR_ISDIR:
-		printf("Directory given where filename expected\n");
-		break;
-	case NFSERR_INVAL:
-		printf("Invalid filehandle\n");
-		break; // INVAL is not defined in NFSv2, some NFS-servers
-		// seem to use it in answers to v2 nevertheless.
-	case 9998:
-		printf("low-level RPC failure (parameter decoding problem?)\n");
-		break;
-	case 9999:
-		printf("low-level RPC failure (authentication problem?)\n");
-		break;
-	default:
-		printf("Unknown NFS error %d\n", -err);
-	}
-}
-
-/**************************************************************************
-NFS_MOUNT - Mount an NFS Filesystem
-**************************************************************************/
-static int nfs_mount(struct sockaddr_in *server, char *path, char *fh, int sport)
-{
-	struct rpc_t buf, *rpc;
-	unsigned long id;
-	int retries;
-	long *p;
-	int pathlen = strlen(path);
-
-	id = rpc_id++;
-	buf.u.call.id = htonl(id);
-	buf.u.call.type = htonl(MSG_CALL);
-	buf.u.call.rpcvers = htonl(2);	/* use RPC version 2 */
-	buf.u.call.prog = htonl(PROG_MOUNT);
-	buf.u.call.vers = htonl(1);	/* mountd is version 1 */
-	buf.u.call.proc = htonl(MOUNT_ADDENTRY);
-	p = rpc_add_credentials((long *)buf.u.call.data);
-	*p++ = htonl(pathlen);
-	if (pathlen & 3) {
-		*(p + pathlen / 4) = 0;	/* add zero padding */
-	}
-	memcpy(p, path, pathlen);
-	p += (pathlen + 3) / 4;
-	for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
-		long timeout;
-		udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
-			(char *)p - (char *)&buf, &buf);
-		timeout = rfc2131_sleep_interval(TIMEOUT, retries);
-		if (await_reply(await_rpc, sport, &id, timeout)) {
-			rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-			if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
-			    rpc->u.reply.astatus || rpc->u.reply.data[0]) {
-				rpc_printerror(rpc);
-				if (rpc->u.reply.rstatus) {
-					/* RPC failed, no verifier, data[0] */
-					return -9999;
-				}
-				if (rpc->u.reply.astatus) {
-					/* RPC couldn't decode parameters */
-					return -9998;
-				}
-				return -ntohl(rpc->u.reply.data[0]);
-			} else {
-				memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
-				return 0;
-			}
-		}
-	}
-	return -1;
-}
-
-/**************************************************************************
-NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
-**************************************************************************/
-static void nfs_umountall(struct sockaddr_in *server)
-{
-	struct rpc_t buf, *rpc;
-	unsigned long id;
-	int retries;
-	long *p;
-
-	id = rpc_id++;
-	buf.u.call.id = htonl(id);
-	buf.u.call.type = htonl(MSG_CALL);
-	buf.u.call.rpcvers = htonl(2);	/* use RPC version 2 */
-	buf.u.call.prog = htonl(PROG_MOUNT);
-	buf.u.call.vers = htonl(1);	/* mountd is version 1 */
-	buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
-	p = rpc_add_credentials((long *)buf.u.call.data);
-	for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
-		long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
-		udp_transmit(server->sin_addr.s_addr, oport, server->sin_port,
-			(char *)p - (char *)&buf, &buf);
-		if (await_reply(await_rpc, oport, &id, timeout)) {
-			rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-			if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
-			    rpc->u.reply.astatus) {
-				rpc_printerror(rpc);
-			}
-			break;
-		}
-	}
-}
-
-/**************************************************************************
-NFS_RESET - Reset the NFS subsystem
-**************************************************************************/
-static void nfs_reset ( void ) {
-	/* If we have a mount server, call nfs_umountall() */
-	if ( mount_server.sin_addr.s_addr ) {
-		nfs_umountall ( &mount_server );
-	}
-	/* Zero the data structures */
-	memset ( &mount_server, 0, sizeof ( mount_server ) );
-	memset ( &nfs_server, 0, sizeof ( nfs_server ) );
-}
-
-/***************************************************************************
- * NFS_READLINK (AH 2003-07-14)
- * This procedure is called when read of the first block fails -
- * this probably happens when it's a directory or a symlink
- * In case of successful readlink(), the dirname is manipulated,
- * so that inside the nfs() function a recursion can be done.
- **************************************************************************/
-static int nfs_readlink(struct sockaddr_in *server, char *fh __unused,
-			char *path, char *nfh, int sport)
-{
-	struct rpc_t buf, *rpc;
-	unsigned long id;
-	long *p;
-	int retries;
-	int pathlen = strlen(path);
-
-	id = rpc_id++;
-	buf.u.call.id = htonl(id);
-	buf.u.call.type = htonl(MSG_CALL);
-	buf.u.call.rpcvers = htonl(2);	/* use RPC version 2 */
-	buf.u.call.prog = htonl(PROG_NFS);
-	buf.u.call.vers = htonl(2);	/* nfsd is version 2 */
-	buf.u.call.proc = htonl(NFS_READLINK);
-	p = rpc_add_credentials((long *)buf.u.call.data);
-	memcpy(p, nfh, NFS_FHSIZE);
-	p += (NFS_FHSIZE / 4);
-	for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
-		long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
-		udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
-			(char *)p - (char *)&buf, &buf);
-		if (await_reply(await_rpc, sport, &id, timeout)) {
-			rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-			if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
-			    rpc->u.reply.astatus || rpc->u.reply.data[0]) {
-				rpc_printerror(rpc);
-				if (rpc->u.reply.rstatus) {
-					/* RPC failed, no verifier, data[0] */
-					return -9999;
-				}
-				if (rpc->u.reply.astatus) {
-					/* RPC couldn't decode parameters */
-					return -9998;
-				}
-				return -ntohl(rpc->u.reply.data[0]);
-			} else {
-				// It *is* a link.
-				// If it's a relative link, append everything to dirname, filename TOO!
-				retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
-				if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
-					path[pathlen++] = '/';
-					while ( ( retries + pathlen ) > 298 ) {
-						retries--;
-					}
-					if ( retries > 0 ) {
-						memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
-					} else { retries = 0; }
-					path[pathlen + retries] = 0;
-				} else {
-					// Else make it the only path.
-					if ( retries > 298 ) { retries = 298; }
-					memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
-					path[retries] = 0;
-				}
-				return 0;
-			}
-		}
-	}
-	return -1;
-}
-/**************************************************************************
-NFS_LOOKUP - Lookup Pathname
-**************************************************************************/
-static int nfs_lookup(struct sockaddr_in *server, char *fh, char *path, char *nfh,
-	int sport)
-{
-	struct rpc_t buf, *rpc;
-	unsigned long id;
-	long *p;
-	int retries;
-	int pathlen = strlen(path);
-
-	id = rpc_id++;
-	buf.u.call.id = htonl(id);
-	buf.u.call.type = htonl(MSG_CALL);
-	buf.u.call.rpcvers = htonl(2);	/* use RPC version 2 */
-	buf.u.call.prog = htonl(PROG_NFS);
-	buf.u.call.vers = htonl(2);	/* nfsd is version 2 */
-	buf.u.call.proc = htonl(NFS_LOOKUP);
-	p = rpc_add_credentials((long *)buf.u.call.data);
-	memcpy(p, fh, NFS_FHSIZE);
-	p += (NFS_FHSIZE / 4);
-	*p++ = htonl(pathlen);
-	if (pathlen & 3) {
-		*(p + pathlen / 4) = 0;	/* add zero padding */
-	}
-	memcpy(p, path, pathlen);
-	p += (pathlen + 3) / 4;
-	for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
-		long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
-		udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
-			(char *)p - (char *)&buf, &buf);
-		if (await_reply(await_rpc, sport, &id, timeout)) {
-			rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-			if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
-			    rpc->u.reply.astatus || rpc->u.reply.data[0]) {
-				rpc_printerror(rpc);
-				if (rpc->u.reply.rstatus) {
-					/* RPC failed, no verifier, data[0] */
-					return -9999;
-				}
-				if (rpc->u.reply.astatus) {
-					/* RPC couldn't decode parameters */
-					return -9998;
-				}
-				return -ntohl(rpc->u.reply.data[0]);
-			} else {
-				memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
-				return 0;
-			}
-		}
-	}
-	return -1;
-}
-
-/**************************************************************************
-NFS_READ - Read File on NFS Server
-**************************************************************************/
-static int nfs_read(struct sockaddr_in *server, char *fh, int offset, int len,
-		    int sport)
-{
-	struct rpc_t buf, *rpc;
-	unsigned long id;
-	int retries;
-	long *p;
-
-	static int tokens=0;
-	/*
-	 * Try to implement something similar to a window protocol in
-	 * terms of response to losses. On successful receive, increment
-	 * the number of tokens by 1 (cap at 256). On failure, halve it.
-	 * When the number of tokens is >= 2, use a very short timeout.
-	 */
-
-	id = rpc_id++;
-	buf.u.call.id = htonl(id);
-	buf.u.call.type = htonl(MSG_CALL);
-	buf.u.call.rpcvers = htonl(2);	/* use RPC version 2 */
-	buf.u.call.prog = htonl(PROG_NFS);
-	buf.u.call.vers = htonl(2);	/* nfsd is version 2 */
-	buf.u.call.proc = htonl(NFS_READ);
-	p = rpc_add_credentials((long *)buf.u.call.data);
-	memcpy(p, fh, NFS_FHSIZE);
-	p += NFS_FHSIZE / 4;
-	*p++ = htonl(offset);
-	*p++ = htonl(len);
-	*p++ = 0;		/* unused parameter */
-	for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
-		long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
-		if (tokens >= 2)
-			timeout = TICKS_PER_SEC/2;
-
-		udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
-			(char *)p - (char *)&buf, &buf);
-		if (await_reply(await_rpc, sport, &id, timeout)) {
-			if (tokens < 256)
-				tokens++;
-			rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-			if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
-			    rpc->u.reply.astatus || rpc->u.reply.data[0]) {
-				rpc_printerror(rpc);
-				if (rpc->u.reply.rstatus) {
-					/* RPC failed, no verifier, data[0] */
-					return -9999;
-				}
-				if (rpc->u.reply.astatus) {
-					/* RPC couldn't decode parameters */
-					return -9998;
-				}
-				return -ntohl(rpc->u.reply.data[0]);
-			} else {
-				return 0;
-			}
-		} else
-			tokens >>= 1;
-	}
-	return -1;
-}
-
-/**************************************************************************
-NFS - Download extended BOOTP data, or kernel image from NFS server
-**************************************************************************/
-static int nfs ( char *url __unused, struct sockaddr_in *server,
-		 char *name, struct buffer *buffer ) {
-	static int recursion = 0;
-	int sport;
-	int err, namelen = strlen(name);
-	char dirname[300], *fname;
-	char dirfh[NFS_FHSIZE];		/* file handle of directory */
-	char filefh[NFS_FHSIZE];	/* file handle of kernel image */
-	int rlen, size, offs, len;
-	struct rpc_t *rpc;
-
-	sport = oport++;
-	if (oport > START_OPORT+OPORT_SWEEP) {
-		oport = START_OPORT;
-	}
-
-	mount_server.sin_addr = nfs_server.sin_addr = server->sin_addr;
-	mount_server.sin_port = rpc_lookup(server, PROG_MOUNT, 1, sport);
-	if ( ! mount_server.sin_port ) {
-		DBG ( "Cannot get mount port from %s:%d\n",
-		      inet_ntoa ( server->sin_addr ), server->sin_port );
-		return 0;
-	}
-	nfs_server.sin_port = rpc_lookup(server, PROG_NFS, 2, sport);
-	if ( ! mount_server.sin_port ) {
-		DBG ( "Cannot get nfs port from %s:%d\n",
-		      inet_ntoa ( server->sin_addr ), server->sin_port );
-		return 0;
-	}
-
-	if ( name != dirname ) {
-		memcpy(dirname, name, namelen + 1);
-	}
-	recursion = 0;
-nfssymlink:
-	if ( recursion > NFS_MAXLINKDEPTH ) {
-		DBG ( "\nRecursion: More than %d symlinks followed. Abort.\n",
-		      NFS_MAXLINKDEPTH );
-		return	0;
-	}
-	recursion++;
-	fname = dirname + (namelen - 1);
-	while (fname >= dirname) {
-		if (*fname == '/') {
-			*fname = '\0';
-			fname++;
-			break;
-		}
-		fname--;
-	}
-	if (fname < dirname) {
-		DBG("can't parse file name %s\n", name);
-		return 0;
-	}
-
-	err = nfs_mount(&mount_server, dirname, dirfh, sport);
-	if (err) {
-		DBG("mounting %s: ", dirname);
-		nfs_printerror(err);
-		/* just to be sure... */
-		nfs_reset();
-		return 0;
-	}
-
-	err = nfs_lookup(&nfs_server, dirfh, fname, filefh, sport);
-	if (err) {
-		DBG("looking up %s: ", fname);
-		nfs_printerror(err);
-		nfs_reset();
-		return 0;
-	}
-
-	offs = 0;
-	size = -1;	/* will be set properly with the first reply */
-	len = NFS_READ_SIZE;	/* first request is always full size */
-	do {
-		err = nfs_read(&nfs_server, filefh, offs, len, sport);
-                if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
-			// An error occured. NFS servers tend to sending
-			// errors 21 / 22 when symlink instead of real file
-			// is requested. So check if it's a symlink!
-			if ( nfs_readlink(&nfs_server, dirfh, dirname,
-					  filefh, sport) == 0 ) {
-				printf("\nLoading symlink:%s ..",dirname);
-				goto nfssymlink;
-			}
-			nfs_printerror(err);
-			nfs_reset();
-			return 0;
-		}
-		if (err) {
-			printf("\nError reading at offset %d: ", offs);
-			nfs_printerror(err);
-			nfs_reset();
-			return 0;
-		}
-
-		rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-
-		/* size must be found out early to allow EOF detection */
-		if (size == -1) {
-			size = ntohl(rpc->u.reply.data[6]);
-		}
-		rlen = ntohl(rpc->u.reply.data[18]);
-		if (rlen > len) {
-			rlen = len;	/* shouldn't happen...  */
-		}
-
-		if ( ! fill_buffer ( buffer, &rpc->u.reply.data[19],
-				     offs, rlen ) ) {
-			nfs_reset();
-			return 0;
-		}
-
-		offs += rlen;
-		/* last request is done with matching requested read size */
-		if (size-offs < NFS_READ_SIZE) {
-			len = size-offs;
-		}
-	} while (len != 0);
-	/* len == 0 means that all the file has been read */
-	return 1;
-}
-
-struct protocol nfs_protocol __protocol = {
-	.name = "nfs",
-	.default_port = SUNRPC_PORT,
-	.load = nfs,
-};
-
-#endif
diff --git a/gpxe/src/proto/nmb.c b/gpxe/src/proto/nmb.c
deleted file mode 100644
index e1fc911..0000000
--- a/gpxe/src/proto/nmb.c
+++ /dev/null
@@ -1,110 +0,0 @@
-#if 0
-
-#include "resolv.h"
-#include "string.h"
-#include <gpxe/dns.h>
-#include "nic.h"
-#include "nmb.h"
-
-/*
- * Convert a standard NUL-terminated string to an NBNS query name.
- *
- * Returns a pointer to the character following the constructed NBNS
- * query name.
- *
- */
-static inline char * nbns_make_name ( char *dest, const char *name ) {
-	char nb_name[16];
-	char c;
-	int i;
-	uint16_t *d;
-
-	*(dest++) = 32; /* Length is always 32 */
-
-	/* Name encoding is as follows: pad the name with spaces to
-	 * length 15, and add a NUL.  Take this 16-byte string, split
-	 * it into nibbles and add 0x41 to each nibble to form a byte
-	 * of the resulting name string.
-	 */
-	memset ( nb_name, ' ', 15 );
-	nb_name[15] = '\0';
-	memcpy ( nb_name, name, strlen ( name ) ); /* Do not copy NUL */
-
-	d = ( uint16_t * ) dest;
-	for ( i = 0 ; i < 16 ; i++ ) {
-		c = nb_name[i];
-		*( d++ )  = htons ( ( ( c | ( c << 4 ) ) & 0x0f0f ) + 0x4141 );
-	}
-	dest = ( char * ) d;
-
-	*(dest++) = 0; /* Terminating 0-length name component */
-	return dest;
-}
-
-/*
- * Resolve a name using NMB
- *
- */
-static int nmb_resolv ( struct in_addr *addr, const char *name ) {
-	struct dns_query query;
-	struct dns_query_info *query_info;
-	struct dns_header *reply;
-	struct dns_rr_info *rr_info;
-	struct dns_rr_info_nb *rr_info_nb;
-	struct sockaddr_in nameserver;
-
-	DBG ( "NMB resolving %s\n", name );
-
-	/* Set up the query data */
-	nameserver.sin_addr.s_addr = INADDR_BROADCAST;
-	nameserver.sin_port = NBNS_UDP_PORT;
-	memset ( &query, 0, sizeof ( query ) );
-	query.dns.id = htons ( 1 );
-	query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
-				  DNS_FLAG_RD | DNS_FLAG_BROADCAST );
-	query.dns.qdcount = htons ( 1 );
-	query_info = ( void * )	nbns_make_name ( query.payload, name );
-	query_info->qtype = htons ( DNS_TYPE_NB );
-	query_info->qclass = htons ( DNS_CLASS_IN );
-
-	/* Issue query, wait for reply */
-	reply = dns_query ( &query,
-			    ( ( ( char * ) query_info )
-			      + sizeof ( *query_info )
-			      - ( ( char * ) &query ) ),
-			    &nameserver );
-	if ( ! reply ) {
-		DBG ( "NMB got no response via %@ (port %d)\n",
-		      nameserver.sin_addr.s_addr,
-		      nameserver.sin_port );
-		return 0;
-	}
-
-	/* Search through response for useful answers. */
-	rr_info = dns_find_rr ( &query, reply );
-	if ( ! rr_info ) {
-		DBG ( "NMB got invalid response\n" );
-		return 0;
-	}
-
-	/* Check type of response */
-	if ( ntohs ( rr_info->type ) != DNS_TYPE_NB ) {
-		DBG ( "NMB got answer type %hx (wanted %hx)\n",
-		      ntohs ( rr_info->type ), DNS_TYPE_NB );
-		return 0;
-	}
-
-	/* Read response */
-	rr_info_nb = ( struct dns_rr_info_nb * ) rr_info;
-	*addr = rr_info_nb->nb_address;
-	DBG ( "NMB found address %@\n", addr->s_addr );
-
-	return 1;
-}
-
-struct resolver nmb_resolver __resolver = {
-	.name = "NMB",
-	.resolv = nmb_resolv,
-};
-
-#endif
diff --git a/gpxe/src/tests/uri_test.c b/gpxe/src/tests/uri_test.c
index 2548760..ca3aed9 100644
--- a/gpxe/src/tests/uri_test.c
+++ b/gpxe/src/tests/uri_test.c
@@ -22,6 +22,9 @@ static struct uri_test uri_tests[] = {
 	  "http://etherboot.org/page3" },
 	{ "tftp://192.168.0.1/", "/tftpboot/vmlinuz",
 	  "tftp://192.168.0.1/tftpboot/vmlinuz" },
+	{ "ftp://the%41nswer%3d:%34ty%32wo@ether%62oot.org:8080/p%41th/foo",
+	  "to?%41=b#%43d",
+	  "ftp://theAnswer%3d:4ty2wo@etherboot.org:8080/path/to?a=b#cd" },
 #if 0
 	"http://www.etherboot.org/wiki",
 	"mailto:bob at nowhere.com",
@@ -41,7 +44,7 @@ static int test_parse_unparse ( const char *uri_string ) {
 		rc = -ENOMEM;
 		goto done;
 	}
-	len = unparse_uri ( buf, sizeof ( buf ), uri );
+	len = unparse_uri ( buf, sizeof ( buf ), uri, URI_ALL );
 
 	/* Compare result */
 	if ( strcmp ( buf, uri_string ) != 0 ) {
@@ -92,7 +95,7 @@ static int test_resolve ( const char *base_uri_string,
 	}
 
 	/* Compare result */
-	len = unparse_uri ( buf, sizeof ( buf ), resolved_uri );
+	len = unparse_uri ( buf, sizeof ( buf ), resolved_uri, URI_ALL );
 	if ( strcmp ( buf, resolved_uri_string ) != 0 ) {
 		printf ( "Resolution of \"%s\"+\"%s\" produced \"%s\"\n",
 			 base_uri_string, relative_uri_string, buf );
diff --git a/gpxe/src/usr/autoboot.c b/gpxe/src/usr/autoboot.c
index 98e79a7..2fa10e6 100644
--- a/gpxe/src/usr/autoboot.c
+++ b/gpxe/src/usr/autoboot.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
@@ -37,18 +39,9 @@
  *
  */
 
-/** Time to wait for link-up */
-#define LINK_WAIT_MS 15000
-
 /** Shutdown flags for exit */
 int shutdown_exit_flags = 0;
 
-/* SAN boot protocols */
-static struct sanboot_protocol sanboot_protocols[0] \
-	__table_start ( struct sanboot_protocol, sanboot_protocols );
-static struct sanboot_protocol sanboot_protocols_end[0] \
-	__table_end ( struct sanboot_protocol, sanboot_protocols );
-
 /**
  * Identify the boot network device
  *
@@ -68,16 +61,16 @@ int boot_next_server_and_filename ( struct in_addr next_server,
 				    const char *filename ) {
 	struct uri *uri;
 	struct image *image;
-	char buf[ 23 /* tftp://xxx.xxx.xxx.xxx/ */ + strlen(filename) + 1 ];
+	char buf[ 23 /* tftp://xxx.xxx.xxx.xxx/ */ +
+		  ( 3 * strlen(filename) ) /* completely URI-encoded */
+		  + 1 /* NUL */ ];
 	int filename_is_absolute;
 	int rc;
 
 	/* Construct URI */
 	uri = parse_uri ( filename );
-	if ( ! uri ) {
-		printf ( "Out of memory\n" );
+	if ( ! uri )
 		return -ENOMEM;
-	}
 	filename_is_absolute = uri_is_absolute ( uri );
 	uri_put ( uri );
 	if ( ! filename_is_absolute ) {
@@ -87,27 +80,22 @@ int boot_next_server_and_filename ( struct in_addr next_server,
 		 * between filenames with and without initial slashes,
 		 * which is significant for TFTP.
 		 */
-		snprintf ( buf, sizeof ( buf ), "tftp://%s/%s",
-			   inet_ntoa ( next_server ), filename );
+		snprintf ( buf, sizeof ( buf ), "tftp://%s/",
+			   inet_ntoa ( next_server ) );
+		uri_encode ( filename, buf + strlen ( buf ),
+			     sizeof ( buf ) - strlen ( buf ), URI_PATH );
 		filename = buf;
 	}
 
 	image = alloc_image();
-	if ( ! image ) {
-		printf ( "Out of memory\n" );
+	if ( ! image )
 		return -ENOMEM;
-	}
 	if ( ( rc = imgfetch ( image, filename,
 			       register_and_autoload_image ) ) != 0 ) {
-		printf ( "Could not load %s: %s\n",
-			 filename, strerror ( rc ) );
 		goto done;
 	}
-	if ( ( rc = imgexec ( image ) ) != 0 ) {
-		printf ( "Could not boot %s: %s\n",
-			 filename, strerror ( rc ) );
+	if ( ( rc = imgexec ( image ) ) != 0 )
 		goto done;
-	}
 
  done:
 	image_put ( image );
@@ -124,8 +112,7 @@ int boot_root_path ( const char *root_path ) {
 	struct sanboot_protocol *sanboot;
 
 	/* Quick hack */
-	for ( sanboot = sanboot_protocols ;
-	      sanboot < sanboot_protocols_end ; sanboot++ ) {
+	for_each_table_entry ( sanboot, SANBOOT_PROTOCOLS ) {
 		if ( strncmp ( root_path, sanboot->prefix,
 			       strlen ( sanboot->prefix ) ) == 0 ) {
 			return sanboot->boot ( root_path );
@@ -158,14 +145,6 @@ static int netboot ( struct net_device *netdev ) {
 		return rc;
 	ifstat ( netdev );
 
-	/* Wait for link-up */
-	printf ( "Waiting for link-up on %s...", netdev->name );
-	if ( ( rc = iflinkwait ( netdev, LINK_WAIT_MS ) ) != 0 ) {
-		printf ( " no link detected\n" );
-		return rc;
-	}
-	printf ( " ok\n" );
-
 	/* Configure device via DHCP */
 	if ( ( rc = dhcp ( netdev ) ) != 0 )
 		return rc;
@@ -176,7 +155,7 @@ static int netboot ( struct net_device *netdev ) {
 			       buf, sizeof ( buf ) );
 	pxe_discovery_control =
 		fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
-	if ( ( strcmp ( buf, "PXEClient" ) == 0 ) &&
+	if ( ( strcmp ( buf, "PXEClient" ) == 0 ) && pxe_menu_boot != NULL &&
 	     setting_exists ( NULL, &pxe_boot_menu_setting ) &&
 	     ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) &&
 		   setting_exists ( NULL, &filename_setting ) ) ) ) {
@@ -189,14 +168,25 @@ static int netboot ( struct net_device *netdev ) {
 	fetch_string_setting ( NULL, &filename_setting, buf, sizeof ( buf ) );
 	if ( buf[0] ) {
 		printf ( "Booting from filename \"%s\"\n", buf );
-		return boot_next_server_and_filename ( next_server, buf );
+		if ( ( rc = boot_next_server_and_filename ( next_server,
+							    buf ) ) != 0 ) {
+			printf ( "Could not boot from filename \"%s\": %s\n",
+				 buf, strerror ( rc ) );
+			return rc;
+		}
+		return 0;
 	}
 	
 	/* No filename; try the root path */
 	fetch_string_setting ( NULL, &root_path_setting, buf, sizeof ( buf ) );
 	if ( buf[0] ) {
 		printf ( "Booting from root path \"%s\"\n", buf );
-		return boot_root_path ( buf );
+		if ( ( rc = boot_root_path ( buf ) ) != 0 ) {
+			printf ( "Could not boot from root path \"%s\": %s\n",
+				 buf, strerror ( rc ) );
+			return rc;
+		}
+		return 0;
 	}
 
 	printf ( "No filename or root path specified\n" );
diff --git a/gpxe/src/usr/dhcpmgmt.c b/gpxe/src/usr/dhcpmgmt.c
index 6acf7f6..f82a3bb 100644
--- a/gpxe/src/usr/dhcpmgmt.c
+++ b/gpxe/src/usr/dhcpmgmt.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
@@ -26,6 +28,8 @@
 #include <usr/ifmgmt.h>
 #include <usr/dhcpmgmt.h>
 
+#define LINK_WAIT_MS	15000
+
 /** @file
  *
  * DHCP management
@@ -33,16 +37,31 @@
  */
 
 int dhcp ( struct net_device *netdev ) {
+	uint8_t *chaddr;
+	uint8_t hlen;
+	uint16_t flags;
 	int rc;
 
 	/* Check we can open the interface first */
 	if ( ( rc = ifopen ( netdev ) ) != 0 )
 		return rc;
 
+	/* Wait for link-up */
+	if ( ( rc = iflinkwait ( netdev, LINK_WAIT_MS ) ) != 0 )
+		return rc;
+
 	/* Perform DHCP */
-	printf ( "DHCP (%s %s)", netdev->name, netdev_hwaddr ( netdev ) );
-	if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 )
+	chaddr = dhcp_chaddr ( netdev, &hlen, &flags );
+	printf ( "DHCP (%s ", netdev->name );
+	while ( hlen-- )
+		printf ( "%02x%c", *(chaddr++), ( hlen ? ':' : ')' ) );
+
+	if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 ) {
 		rc = monojob_wait ( "" );
+	} else if ( rc > 0 ) {
+		printf ( " using cached\n" );
+		rc = 0;
+	}
 
 	return rc;
 }
diff --git a/gpxe/src/usr/ifmgmt.c b/gpxe/src/usr/ifmgmt.c
index 0ebf561..d4cc5a5 100644
--- a/gpxe/src/usr/ifmgmt.c
+++ b/gpxe/src/usr/ifmgmt.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -87,35 +89,59 @@ static void ifstat_errors ( struct net_device_stats *stats,
 void ifstat ( struct net_device *netdev ) {
 	printf ( "%s: %s on %s (%s)\n"
 		 "  [Link:%s, TX:%d TXE:%d RX:%d RXE:%d]\n",
-		 netdev->name, netdev_hwaddr ( netdev ), netdev->dev->name,
+		 netdev->name, netdev_addr ( netdev ), netdev->dev->name,
 		 ( ( netdev->state & NETDEV_OPEN ) ? "open" : "closed" ),
 		 ( netdev_link_ok ( netdev ) ? "up" : "down" ),
 		 netdev->tx_stats.good, netdev->tx_stats.bad,
 		 netdev->rx_stats.good, netdev->rx_stats.bad );
+	if ( ! netdev_link_ok ( netdev ) ) {
+		printf ( "  [Link status: %s]\n",
+			 strerror ( netdev->link_rc ) );
+	}
 	ifstat_errors ( &netdev->tx_stats, "TXE" );
 	ifstat_errors ( &netdev->rx_stats, "RXE" );
 }
 
 /**
- * Wait for link-up
+ * Wait for link-up, with status indication
  *
  * @v netdev		Network device
  * @v max_wait_ms	Maximum time to wait, in ms
  */
 int iflinkwait ( struct net_device *netdev, unsigned int max_wait_ms ) {
 	int key;
+	int rc;
+
+	if ( netdev_link_ok ( netdev ) )
+		return 0;
+
+	printf ( "Waiting for link-up on %s...", netdev->name );
 
 	while ( 1 ) {
-		if ( netdev_link_ok ( netdev ) )
-			return 0;
-		if ( max_wait_ms-- == 0 )
-			return -ETIMEDOUT;
+		if ( netdev_link_ok ( netdev ) ) {
+			rc = 0;
+			break;
+		}
+		if ( max_wait_ms-- == 0 ) {
+			rc = netdev->link_rc;
+			break;
+		}
 		step();
 		if ( iskey() ) {
 			key = getchar();
-			if ( key == CTRL_C )
-				return -ECANCELED;
+			if ( key == CTRL_C ) {
+				rc = -ECANCELED;
+				break;
+			}
 		}
 		mdelay ( 1 );
 	}
+
+	if ( rc == 0 ) {
+		printf ( " ok\n" );
+	} else {
+		printf ( " failed: %s\n", strerror ( rc ) );
+	}
+
+	return rc;
 }
diff --git a/gpxe/src/usr/imgmgmt.c b/gpxe/src/usr/imgmgmt.c
index bd53d82..023e3f0 100644
--- a/gpxe/src/usr/imgmgmt.c
+++ b/gpxe/src/usr/imgmgmt.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -59,7 +61,7 @@ int imgfetch ( struct image *image, const char *uri_string,
 	if ( password )
 		uri->password = "***";
 	unparse_uri ( uri_string_redacted, sizeof ( uri_string_redacted ),
-		      uri );
+		      uri, URI_ALL );
 	uri->password = password;
 
 	if ( ( rc = create_downloader ( &monojob, image, image_register,
diff --git a/gpxe/src/usr/iwmgmt.c b/gpxe/src/usr/iwmgmt.c
new file mode 100644
index 0000000..59ba103
--- /dev/null
+++ b/gpxe/src/usr/iwmgmt.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2009 Joshua Oreman <oremanj at rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdio.h>
+#include <console.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/net80211.h>
+#include <gpxe/ethernet.h>
+#include <usr/ifmgmt.h>
+#include <usr/iwmgmt.h>
+#include <gpxe/errortab.h>
+
+/** @file
+ *
+ * Wireless network interface management
+ *
+ */
+
+/**
+ * Print status of 802.11 device
+ *
+ * @v dev	802.11 device
+ */
+void iwstat ( struct net80211_device *dev ) {
+
+	ifstat ( dev->netdev );
+
+	printf ( "  [802.11 ");
+	if ( dev->state & NET80211_ASSOCIATED ) {
+		printf ( "SSID '%s', ", dev->essid );
+	} else {
+		printf ( "not associated, " );
+	}
+	if ( dev->channel < dev->nr_channels && dev->rate < dev->nr_rates ) {
+		printf ( "Ch:%d Sig:%d", dev->channels[dev->channel].channel_nr,
+			 dev->last_signal );
+		switch ( dev->hw->signal_type ) {
+		case NET80211_SIGNAL_NONE:
+			printf ( "?" );
+			break;
+		case NET80211_SIGNAL_ARBITRARY:
+			printf ( "/%d", dev->hw->signal_max );
+			break;
+		case NET80211_SIGNAL_DB:
+			printf ( "/%d dB", dev->hw->signal_max );
+			break;
+		case NET80211_SIGNAL_DBM:
+			printf ( " dBm" );
+			break;
+		}
+		printf ( ", Qual:%d%% Rate:%d Mbps]\n",
+			 ( dev->rx_beacon_interval == 0 ? 0 :
+			   100 * dev->tx_beacon_interval /
+			   dev->rx_beacon_interval ),
+			 dev->rates[dev->rate] / 10 );
+	} else {
+		printf ( "antenna off]\n" );
+	}
+
+	if ( dev->state & NET80211_WORKING ) {
+		printf ( "  [associating" );
+		if ( dev->associating )
+			printf ( " to '%s'", dev->associating->essid );
+		printf ( "...]\n" );
+	}
+}
+
+/** Identifiers for 802.11 cryptography types, indexed by type number */
+static const char *crypto_types[] = {
+	[NET80211_CRYPT_NONE] = "Open",
+	[NET80211_CRYPT_WEP] = "WEP ",
+	[NET80211_CRYPT_TKIP] = "WPA ",
+	[NET80211_CRYPT_CCMP] = "WPA2",
+	[NET80211_CRYPT_UNKNOWN] = "UNK ",
+};
+
+/** Number of 802.11 cryptography types defined */
+#define NR_CRYPTO_TYPES ( sizeof ( crypto_types ) / sizeof ( crypto_types[0] ) )
+
+/** Identifiers for 802.11 authentication types, indexed by type number */
+static const char *auth_types[] = {
+	[NET80211_SECPROT_NONE] = "",
+	[NET80211_SECPROT_PSK] = "PSK",
+	[NET80211_SECPROT_EAP] = "802.1X",
+	[NET80211_SECPROT_UNKNOWN] = "UNK",
+};
+
+/** Number of 802.11 authentication types defined */
+#define NR_AUTH_TYPES ( sizeof ( auth_types ) / sizeof ( auth_types[0] ) )
+
+/**
+ * Scan for wireless networks using 802.11 device
+ *
+ * @v dev	802.11 device
+ * @v active	Whether to use active scanning
+ *
+ * The list of networks found will be printed in tabular format.
+ *
+ * This function is safe to call at all times, whether the 802.11
+ * device is open or not, but if called while the auto-association
+ * task is running it will return an error indication.
+ */
+int iwlist ( struct net80211_device *dev ) {
+	struct net80211_probe_ctx *ctx;
+	struct list_head *networks;
+	struct net80211_wlan *wlan;
+	char ssid_buf[22];
+	int rc;
+	unsigned i;
+	int was_opened = dev->netdev->state & NETDEV_OPEN;
+	int was_channel = dev->channels[dev->channel].channel_nr;
+
+	if ( ! was_opened ) {
+		dev->state |= NET80211_NO_ASSOC;
+		rc = netdev_open ( dev->netdev );
+		if ( rc < 0 )
+			goto err;
+	}
+
+	if ( dev->state & NET80211_WORKING ) {
+		rc = -EINVAL;
+		goto err_close_netdev;
+	}
+
+	if ( ! was_opened ) {
+		rc = net80211_prepare_probe ( dev, dev->hw->bands, 0 );
+		if ( rc < 0 )
+			goto err_close_netdev;
+	}
+
+	ctx = net80211_probe_start ( dev, "", 0 );
+	if ( ! ctx ) {
+		rc = -ENOMEM;
+		goto err_close_netdev;
+	}
+
+	while ( ! ( rc = net80211_probe_step ( ctx ) ) ) {
+		step();
+	}
+
+	networks = net80211_probe_finish_all ( ctx );
+
+	if ( list_empty ( networks ) ) {
+		goto err_free_networks;
+	}
+
+	rc = 0;
+
+	printf ( "Networks on %s:\n\n", dev->netdev->name );
+
+	/* Output format:
+	 * 0         1         2         3         4         5         6
+	 * 0123456789012345678901234567890123456789012345678901234567890
+	 * [Sig] SSID                  BSSID              Ch  Crypt/Auth
+	 * -------------------------------------------------------------
+	 * [ 15] abcdefghijklmnopqrst> 00:00:00:00:00:00  11  Open
+	 *                                             ... or WPA   PSK etc.
+	 */
+
+	/* Quoting the dashes and spaces verbatim uses less code space
+	   than generating them programmatically. */
+	printf ( "[Sig] SSID                  BSSID              Ch  Crypt/Auth\n"
+		 "-------------------------------------------------------------\n" );
+
+	list_for_each_entry ( wlan, networks, list ) {
+
+		/* Format SSID into 22-character string, space-padded,
+		   with '>' indicating truncation */
+
+		snprintf ( ssid_buf, sizeof ( ssid_buf ), "%s", wlan->essid );
+		for ( i = strlen ( ssid_buf ); i < sizeof ( ssid_buf ) - 1;
+		      i++ )
+			ssid_buf[i] = ' ';
+		if ( ssid_buf[sizeof ( ssid_buf ) - 2] != ' ' )
+			ssid_buf[sizeof ( ssid_buf ) - 2] = '>';
+		ssid_buf[sizeof ( ssid_buf ) - 1] = 0;
+
+		/* Sanity check */
+		if ( wlan->crypto >= NR_CRYPTO_TYPES ||
+		     wlan->handshaking >= NR_AUTH_TYPES )
+			continue;
+
+		printf ( "[%3d] %s %s  %2d  %s  %s\n",
+			 wlan->signal < 0 ? 100 + wlan->signal : wlan->signal,
+			 ssid_buf, eth_ntoa ( wlan->bssid ), wlan->channel,
+			 crypto_types[wlan->crypto],
+			 auth_types[wlan->handshaking] );
+	}
+	printf ( "\n" );
+
+ err_free_networks:
+	net80211_free_wlanlist ( networks );
+
+ err_close_netdev:
+	if ( ! was_opened ) {
+		dev->state &= ~NET80211_NO_ASSOC;
+		netdev_close ( dev->netdev );
+	} else {
+		net80211_change_channel ( dev, was_channel );
+	}
+
+	if ( ! rc )
+		return 0;
+
+ err:
+	printf ( "Scanning for networks on %s: %s\n",
+		 dev->netdev->name, strerror ( rc ) );
+	return rc;
+}
+
+
+/* Record error codes as though they come from the 802.11 stack */
+#undef ERRFILE
+#define ERRFILE ERRFILE_net80211
+
+/** Common 802.11 errors */
+struct errortab common_wireless_errors[] __errortab = {
+	{ EINVAL | EUNIQ_06, "Packet decryption error" },
+	{ ECONNRESET | EUNIQ_01, "Unspecified reason" },
+	{ ECONNRESET | EUNIQ_04, "Disassociated due to inactivity" },
+	{ ECONNRESET | EUNIQ_0F, "4-Way Handshake timeout" },
+	{ ECONNRESET | EUNIQ_17, "IEEE 802.1X authentication failed" },
+	{ ECONNREFUSED | EUNIQ_01, "Unspecified failure" },
+	{ ECONNREFUSED | EUNIQ_0C, "Association denied" },
+	{ ECONNREFUSED | EUNIQ_0D, "Authentication method not supported" },
+};
diff --git a/gpxe/src/usr/pxemenu.c b/gpxe/src/usr/pxemenu.c
index 793cb62..519c200 100644
--- a/gpxe/src/usr/pxemenu.c
+++ b/gpxe/src/usr/pxemenu.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -151,7 +153,7 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
 			  ( num_menu_items * sizeof ( (*menu)->items[0] ) ) );
 	memcpy ( raw_menu_item, raw_menu, raw_menu_len );
 	for ( i = 0 ; i < num_menu_items ; i++ ) {
-		(*menu)->items[i].type = ntohs ( raw_menu_item->type );
+		(*menu)->items[i].type = le16_to_cpu ( raw_menu_item->type );
 		(*menu)->items[i].desc = raw_menu_item->desc;
 		/* Set type to 0; this ensures that the description
 		 * for the previous menu item is NUL-terminated.
diff --git a/gpxe/src/usr/route.c b/gpxe/src/usr/route.c
index f3a49f4..1875741 100644
--- a/gpxe/src/usr/route.c
+++ b/gpxe/src/usr/route.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+FILE_LICENCE ( GPL2_OR_LATER );
+
 #include <stdio.h>
 #include <gpxe/netdevice.h>
 #include <gpxe/ip.h>
@@ -34,7 +36,7 @@ void route ( void ) {
 		printf ( "%s: %s/", miniroute->netdev->name,
 			 inet_ntoa ( miniroute->address ) );
 		printf ( "%s", inet_ntoa ( miniroute->netmask ) );
-		if ( miniroute->gateway.s_addr != INADDR_NONE )
+		if ( miniroute->gateway.s_addr )
 			printf ( " gw %s", inet_ntoa ( miniroute->gateway ) );
 		if ( ! ( miniroute->netdev->state & NETDEV_OPEN ) )
 			printf ( " (inaccessible)" );
diff --git a/gpxe/src/util/.gitignore b/gpxe/src/util/.gitignore
index c256701..a375247 100644
--- a/gpxe/src/util/.gitignore
+++ b/gpxe/src/util/.gitignore
@@ -5,3 +5,4 @@ prototester
 elf2efi32
 elf2efi64
 efirom
+iccfix
diff --git a/gpxe/src/util/diffsize.pl b/gpxe/src/util/diffsize.pl
new file mode 100755
index 0000000..d4978c2
--- /dev/null
+++ b/gpxe/src/util/diffsize.pl
@@ -0,0 +1,101 @@
+#!/usr/bin/perl -w
+# usage:
+# [somebody at somewhere ~/gpxe/src]$ ./util/diffsize.pl [<old rev> [<new rev>]]
+# by default <old rev> is HEAD and <new rev> is the working tree
+
+use strict;
+
+-d "bin" or die "Please run me in the gPXE src directory\n";
+mkdir ".sizes";
+
+my($oldrev, $newrev);
+my($oldname, $newname);
+
+if (@ARGV) {
+  $oldname = shift;
+} else {
+  $oldname = "HEAD";
+}
+
+if (@ARGV) {
+  $newname = shift;
+} else {
+  $newrev = "tree" . time();
+}
+
+$oldrev = `git rev-parse $oldname`;
+chomp $oldrev;
+
+unless (defined $newrev) {
+  $newrev = `git rev-parse $newname`;
+  chomp $newrev;
+}
+
+sub calc_sizes($$) {
+  my($name, $rev) = @_;
+  my $output;
+  my $lastrev;
+  my $stashed = 0;
+  my $res = 0;
+
+  return if -e ".sizes/$rev.sizes";
+
+  if (defined $name) {
+    $output = `git stash`;
+    $stashed = 1 unless $output =~ /No local changes to save/;
+    $lastrev = `git name-rev --name-only HEAD`;
+    system("git checkout $name >/dev/null"); $res ||= $?;
+  }
+
+  system("make -j4 bin/gpxe.lkrn >/dev/null"); $res ||= $?;
+  system("make bin/gpxe.lkrn.sizes > .sizes/$rev.sizes"); $res ||= $?;
+
+  if (defined $name) {
+    system("git checkout $lastrev >/dev/null"); $res ||= $?;
+    system("git stash pop >/dev/null") if $stashed; $res ||= $?;
+  }
+
+  if ($res) {
+    unlink(".sizes/$rev.sizes");
+    die "Error making sizes file\n";
+  }
+}
+
+our %Sizes;
+
+sub save_sizes($$) {
+  my($id, $rev) = @_;
+  my $file = ".sizes/$rev.sizes";
+
+  open SIZES, $file or die "opening $file: $!\n";
+  while (<SIZES>) {
+    my($text, $data, $bss, $total, $hex, $name) = split;
+    $name =~ s|bin/||; $name =~ s|\.o$||;
+
+    # Skip the header and totals lines
+    next if $total =~ /[a-z]/ or $name =~ /TOTALS/;
+
+    # Skip files named with dash, due to old Makefile bug
+    next if $name =~ /-/;
+
+    $Sizes{$name} = {old => 0, new => 0} unless exists $Sizes{$name};
+    $Sizes{$name}{$id} = $total;
+  }
+}
+
+calc_sizes($oldname, $oldrev);
+calc_sizes($newname, $newrev);
+
+save_sizes('old', $oldrev);
+save_sizes('new', $newrev);
+
+my $total = 0;
+
+for (sort keys %Sizes) {
+  my $diff = $Sizes{$_}{new} - $Sizes{$_}{old};
+  if (abs($diff) >= 16) {
+    printf "%12s %+d\n", substr($_, 0, 12), $Sizes{$_}{new} - $Sizes{$_}{old};
+  }
+  $total += $diff;
+}
+printf "      TOTAL: %+d\n", $total;
diff --git a/gpxe/src/util/dskpad.pl b/gpxe/src/util/dskpad.pl
deleted file mode 100755
index 3ae325e..0000000
--- a/gpxe/src/util/dskpad.pl
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-use warnings;
-
-use constant FLOPPYSIZE => 1440 * 1024;
-
-while ( my $filename = shift ) {
-  die "$filename is not a file\n" unless -f $filename;
-  die "$filename is too large\n" unless ( -s $filename <= FLOPPYSIZE );
-  truncate $filename, FLOPPYSIZE or die "Could not truncate: $!\n";
-}
diff --git a/gpxe/src/util/elf2efi.c b/gpxe/src/util/elf2efi.c
index 886777d..bb766bd 100644
--- a/gpxe/src/util/elf2efi.c
+++ b/gpxe/src/util/elf2efi.c
@@ -29,6 +29,7 @@
 #include <bfd.h>
 #include <gpxe/efi/efi.h>
 #include <gpxe/efi/IndustryStandard/PeImage.h>
+#include <libgen.h>
 
 #define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
 
@@ -658,6 +659,7 @@ static void write_pe_file ( struct pe_header *pe_header,
  */
 static void elf2pe ( const char *elf_name, const char *pe_name,
 		     struct options *opts ) {
+	char pe_name_tmp[ strlen ( pe_name ) + 1 ];
 	bfd *bfd;
 	asymbol **symtab;
 	asection *section;
@@ -669,6 +671,9 @@ static void elf2pe ( const char *elf_name, const char *pe_name,
 	struct pe_header pe_header;
 	FILE *pe;
 
+	/* Create a modifiable copy of the PE name */
+	memcpy ( pe_name_tmp, pe_name, sizeof ( pe_name_tmp ) );
+
 	/* Open the file */
 	bfd = open_input_bfd ( elf_name );
 	symtab = read_symtab ( bfd );
@@ -703,7 +708,7 @@ static void elf2pe ( const char *elf_name, const char *pe_name,
 
 	/* Create the .reloc section */
 	*(next_pe_section) = create_debug_section ( &pe_header,
-						    basename ( pe_name ) );
+						    basename ( pe_name_tmp ) );
 	next_pe_section = &(*next_pe_section)->next;
 
 	/* Write out PE file */
diff --git a/gpxe/src/util/geniso b/gpxe/src/util/geniso
index d7ddbd2..7c2f767 100755
--- a/gpxe/src/util/geniso
+++ b/gpxe/src/util/geniso
@@ -17,13 +17,12 @@ esac
 isolinux_bin=${ISOLINUX_BIN:-util/isolinux.bin}
 if [ ! -r $isolinux_bin ]
 then
-	echo $0: $isolinux_bin not found, please install, or set ISOLINUX_BIN in arch/i386/config correctly
+	echo $0: $isolinux_bin not found, please install, or set ISOLINUX_BIN in arch/i386/Makefile correctly
 	exit 1
 fi
 out=$1
 shift
-dir=bin/iso.dir
-mkdir -p $dir
+dir=`mktemp -d bin/iso.dir.XXXXXX`
 cfg=$dir/isolinux.cfg
 cp -p $isolinux_bin $dir
 cat > $cfg <<EOF
diff --git a/gpxe/src/util/genliso b/gpxe/src/util/genliso
index 0a67eb3..1f1c116 100755
--- a/gpxe/src/util/genliso
+++ b/gpxe/src/util/genliso
@@ -2,51 +2,42 @@
 #
 # Generate a legacy floppy emulation ISO boot image
 #
-# genliso foo.liso foo.lkrn
+# genliso foo.liso foo.lkrn bar.lkrn ...
 #
-# the ISO image is the first argument so that a list of .lkrn images
-# to include can be specified
-#
-case $0 in
-*genliso)
-	;;
-*genfdimg)
-	genfdimg=1
-	;;
-esac
+# The .liso image filename is the first argument followed by
+#   a list of .lkrn images  include in .liso image
+
 case $# in
 0|1)
 	echo Usage: $0 foo.liso foo.lkrn ...
 	exit 1
 	;;
 esac
+
 case "`mtools -V`" in
-Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|Mtools\ version\ 4.*)
+Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|[mM]tools\ *\ [4-9].*)
 	;;
 *)
 	echo Mtools version 3.9.9 or later is required
 	exit 1
 	;;
 esac
+
 out=$1
 shift
-dir=bin/liso.dir
-mkdir -p $dir
-case "$genfdimg" in
-1)
-	img=$out
-	;;
-*)
-	img=$dir/boot.img
-	;;
-esac
+
+dir=`mktemp -d bin/liso.dir.XXXXXX`
+
+img=$dir/boot.img
 mformat -f 1440 -C -i $img ::
-cfg=bin/syslinux.cfg
+
+cfg=$dir/syslinux.cfg
 cat > $cfg <<EOF
 # These default options can be changed in the genliso script
-SAY Etherboot ISO boot image generated by genliso
+SAY gPXE ISO boot image generated by genliso
 TIMEOUT 30
 EOF
+
 first=
 for f
 do
@@ -70,16 +61,14 @@ do
 	echo "" KERNEL $g
 	mcopy -m -i $img $f ::$g
 done >> $cfg
+
 mcopy -i $img $cfg ::syslinux.cfg
+
 if ! syslinux $img
 then
 	exit 1
 fi
-case "$genfdimg" in
-1)
-	;;
-*)
-	mkisofs -o $out -c boot.cat -b boot.img $dir
-	;;
-esac
+
+mkisofs -q -o $out -c boot.cat -b boot.img $dir
+
 rm -fr $dir
diff --git a/gpxe/src/util/gensdsk b/gpxe/src/util/gensdsk
new file mode 100755
index 0000000..a866158
--- /dev/null
+++ b/gpxe/src/util/gensdsk
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# Generate a syslinux floppy that loads a gPXE image
+#
+# gensdsk foo.sdsk foo.lkrn
+#
+# the floppy image is the first argument
+#   followed by list of .lkrn images
+#
+
+case $# in
+0|1)
+	echo Usage: $0 foo.sdsk foo.lkrn ...
+	exit 1
+	;;
+esac
+case "`mtools -V`" in
+Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|[mM]tools\ *\ [4-9].*)
+	;;
+*)
+	echo Mtools version 3.9.9 or later is required
+	exit 1
+	;;
+esac
+img=$1
+shift
+dir=`mktemp -d bin/sdsk.dir.XXXXXX`
+
+mformat -f 1440 -C -i $img ::
+cfg=$dir/syslinux.cfg
+cat > $cfg <<EOF
+
+# These default options can be changed in the gensdsk script
+TIMEOUT 30
+EOF
+first=
+for f
+do
+	if [ ! -r $f ]
+	then
+		echo $f does not exist, skipping 1>&2
+		continue
+	fi
+	# shorten name for 8.3 filesystem
+	b=$(basename $f)
+	g=${b%.lkrn}
+	g=${g//[^a-z0-9]}
+	g=${g:0:8}.krn
+	case "$first" in
+	"")
+		echo DEFAULT $g
+		;;
+	esac
+	first=$g
+	echo LABEL $b
+	echo "" KERNEL $g
+	mcopy -m -i $img $f ::$g
+done >> $cfg
+mcopy -i $img $cfg ::syslinux.cfg
+if ! syslinux $img
+then
+	exit 1
+fi
+
+rm -fr $dir
diff --git a/gpxe/src/util/iccfix.c b/gpxe/src/util/iccfix.c
new file mode 100644
index 0000000..303ae9c
--- /dev/null
+++ b/gpxe/src/util/iccfix.c
@@ -0,0 +1,156 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <elf.h>
+#include <gpxe/tables.h>
+
+#define DEBUG 0
+
+#define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
+
+#define dprintf(...) do {						\
+	if ( DEBUG )							\
+		fprintf ( stderr, __VA_ARGS__ );			\
+	} while ( 0 )
+
+#ifdef SELF_INCLUDED
+
+/**
+ * Fix up ICC alignments
+ *
+ * @v elf		ELF header
+ * @ret rc		Return status code
+ *
+ * See comments in tables.h for an explanation of why this monstrosity
+ * is necessary.
+ */
+static int ICCFIX ( void *elf ) {
+	ELF_EHDR *ehdr = elf;
+	ELF_SHDR *shdr = ( elf + ehdr->e_shoff );
+	size_t shentsize = ehdr->e_shentsize;
+	unsigned int shnum = ehdr->e_shnum;
+	ELF_SHDR *strtab = ( ( ( void * ) shdr ) +
+			     ( ehdr->e_shstrndx * shentsize ) );
+	char *strings = ( elf + strtab->sh_offset );
+
+	for ( ; shnum-- ; shdr = ( ( ( void * ) shdr ) + shentsize ) ) {
+		char *name = ( strings + shdr->sh_name );
+		unsigned long align = shdr->sh_addralign;
+		unsigned long new_align;
+
+		if ( ( strncmp ( name, ".tbl.", 5 ) == 0 ) &&
+		     ( align >= ICC_ALIGN_HACK_FACTOR ) ) {
+			new_align = ( align / ICC_ALIGN_HACK_FACTOR );
+			shdr->sh_addralign = new_align;
+			dprintf ( "Section \"%s\": alignment %d->%d\n",
+				  name, align, new_align );
+		}
+	}
+	return 0;
+}
+
+#else /* SELF_INCLUDED */
+
+#define SELF_INCLUDED
+
+/* Include iccfix32() function */
+#define ELF_EHDR Elf32_Ehdr
+#define ELF_SHDR Elf32_Shdr
+#define ICCFIX iccfix32
+#include "iccfix.c"
+#undef ELF_EHDR
+#undef ELF_SHDR
+#undef ICCFIX
+
+/* Include iccfix64() function */
+#define ELF_EHDR Elf64_Ehdr
+#define ELF_SHDR Elf64_Shdr
+#define ICCFIX iccfix64
+#include "iccfix.c"
+#undef ELF_EHDR
+#undef ELF_SHDR
+#undef ICCFIX
+
+static int iccfix ( const char *filename ) {
+	int fd;
+	struct stat stat;
+	void *elf;
+	unsigned char *eident;
+	int rc;
+
+	/* Open and mmap file */
+	fd = open ( filename, O_RDWR );
+	if ( fd < 0 ) {
+		eprintf ( "Could not open %s: %s\n",
+			  filename, strerror ( errno ) );
+		rc = -1;
+		goto err_open;
+	}
+	if ( fstat ( fd, &stat ) < 0 ) {
+		eprintf ( "Could not determine size of %s: %s\n",
+			  filename, strerror ( errno ) );
+		rc = -1;
+		goto err_fstat;
+	}
+	elf = mmap ( NULL, stat.st_size, ( PROT_READ | PROT_WRITE ),
+		     MAP_SHARED, fd, 0 );
+	if ( elf == MAP_FAILED ) {
+		eprintf ( "Could not map %s: %s\n",
+			  filename, strerror ( errno ) );
+		rc = -1;
+		goto err_mmap;
+	}
+
+	/* Perform fixups */
+	eident = elf;
+	switch ( eident[EI_CLASS] ) {
+	case ELFCLASS32:
+		rc = iccfix32 ( elf );
+		break;
+	case ELFCLASS64:
+		rc = iccfix64 ( elf );
+		break;
+	default:
+		eprintf ( "Unknown ELF class %d in %s\n",
+			  eident[EI_CLASS], filename );
+		rc = -1;
+		break;
+	}
+
+	munmap ( elf, stat.st_size );
+ err_mmap:
+ err_fstat:
+	close ( fd );
+ err_open:
+	return rc;
+}
+
+int main ( int argc, char **argv ) {
+	int i;
+	int rc;
+
+	/* Parse command line */
+	if ( argc < 2 ) {
+		eprintf ( "Syntax: %s <object_file>...\n", argv[0] );
+		exit ( 1 );
+	}
+
+	/* Process each object in turn */
+	for ( i = 1 ; i < argc ; i++ ) {
+		if ( ( rc = iccfix ( argv[i] ) ) != 0 ) {
+			eprintf ( "Could not fix up %s\n", argv[i] );
+			exit ( 1 );
+		}
+	}
+
+	return 0;
+}
+
+#endif /* SELF_INCLUDED */
diff --git a/gpxe/src/util/licence.pl b/gpxe/src/util/licence.pl
new file mode 100755
index 0000000..c37685d
--- /dev/null
+++ b/gpxe/src/util/licence.pl
@@ -0,0 +1,149 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2008 Michael Brown <mbrown at fensystems.co.uk>.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+# List of licences we can handle
+my $known_licences = {
+  gpl_any => {
+    desc => "GPL (any version)",
+    can_subsume => {
+      public_domain => 1,
+      bsd3 => 1,
+      bsd2 => 1,
+      mit  => 1,
+      isc  => 1,
+    },
+  },
+  gpl2_or_later => {
+    desc => "GPL version 2 (or, at your option, any later version)",
+    can_subsume => {
+      gpl_any => 1,
+      public_domain => 1,
+      bsd3 => 1,
+      bsd2 => 1,
+      mit  => 1,
+      isc  => 1,
+    },
+  },
+  gpl2_only => {
+    desc => "GPL version 2 only",
+    can_subsume => {
+      gpl_any => 1,
+      gpl2_or_later => 1,
+      public_domain => 1,
+      bsd3 => 1,
+      bsd2 => 1,
+      mit  => 1,
+      isc  => 1,
+    },
+  },
+  public_domain => {
+    desc => "Public Domain",
+    can_subsume => {},
+  },
+  bsd4 => {
+    desc => "BSD Licence (with advertising clause)",
+    can_subsume => {
+      public_domain => 1,
+      bsd3 => 1,
+      bsd2 => 1,
+      mit  => 1,
+      isc  => 1,
+    },
+  },
+  bsd3 => {
+    desc => "BSD Licence (without advertising clause)",
+    can_subsume => {
+      public_domain => 1,
+      bsd2 => 1,
+      mit  => 1,
+      isc  => 1,
+    },
+  },
+  bsd2 => {
+    desc => "BSD Licence (without advertising or endorsement clauses)",
+    can_subsume => {
+      public_domain => 1,
+      mit  => 1,
+      isc  => 1,
+    },
+  },
+  mit => {
+    desc => "MIT/X11/Xorg Licence",
+    can_subsume => {
+      public_domain => 1,
+      isc => 1,
+    },
+  },
+  isc => {
+    desc => "ISC Licence",
+    can_subsume => {
+      public_domain => 1,
+    },
+  },
+};
+
+# Parse command-line options
+my $verbosity = 1;
+Getopt::Long::Configure ( 'bundling', 'auto_abbrev' );
+GetOptions (
+  'verbose|v+' => sub { $verbosity++; },
+  'quiet|q+' => sub { $verbosity--; },
+) or die "Could not parse command-line options\n";
+
+# Parse licence list from command line
+my $licences = {};
+foreach my $licence ( @ARGV ) {
+  die "Unknown licence \"$licence\"\n"
+      unless exists $known_licences->{$licence};
+  $licences->{$licence} = $known_licences->{$licence};
+}
+die "No licences specified\n" unless %$licences;
+
+# Dump licence list
+if ( $verbosity >= 1 ) {
+  print "The following licences appear within this file:\n";
+  foreach my $licence ( keys %$licences ) {
+    print "  ".$licences->{$licence}->{desc}."\n"
+  }
+}
+
+# Apply licence compatibilities to reduce to a single resulting licence
+foreach my $licence ( keys %$licences ) {
+  # Skip already-deleted licences
+  next unless exists $licences->{$licence};
+  # Subsume any subsumable licences
+  foreach my $can_subsume ( keys %{$licences->{$licence}->{can_subsume}} ) {
+    if ( exists $licences->{$can_subsume} ) {
+      print $licences->{$licence}->{desc}." subsumes ".
+	  $licences->{$can_subsume}->{desc}."\n"
+	  if $verbosity >= 1;
+      delete $licences->{$can_subsume};
+    }
+  }
+}
+
+# Print resulting licence
+die "Cannot reduce to a single resulting licence!\n"
+    if ( keys %$licences ) != 1;
+( my $licence ) = keys %$licences;
+print "The overall licence for this file is:\n  " if $verbosity >= 1;
+print $licences->{$licence}->{desc}."\n";
diff --git a/gpxe/src/util/makerom.pl b/gpxe/src/util/makerom.pl
index aed3a56..68c3be9 100755
--- a/gpxe/src/util/makerom.pl
+++ b/gpxe/src/util/makerom.pl
@@ -130,14 +130,14 @@ sub writerom ($$) {
 	close(R);
 }
 
-sub checksum ($) {
-	my ($romref) = @_;
+sub checksum ($$) {
+	my ($romref, $romsize) = @_;
 
 	substr($$romref, 6, 1) = "\x00";
-	my $sum = unpack('%8C*', $$romref);
+	my $sum = unpack('%8C*', substr($$romref, 0, $romsize));
 	substr($$romref, 6, 1) = chr(256 - $sum);
 	# Double check
-	$sum = unpack('%8C*', $$romref);
+	$sum = unpack('%8C*', substr($$romref, 0, $romsize));
 	if ($sum != 0) {
 		print "Checksum fails\n"
 	} elsif ($opts{'v'}) {
@@ -146,10 +146,10 @@ sub checksum ($) {
 }
 
 sub makerom () {
-	my ($rom, $romsize);
+	my ($rom, $romsize, $stubsize);
 
-	getopts('3xi:p:s:v', \%opts);
-	$ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-x] [-3] rom-file\n";
+	getopts('3xni:p:s:v', \%opts);
+	$ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-n] [-x] [-3] rom-file\n";
 	open(R, $ARGV[0]) or die "$ARGV[0]: $!\n";
 	# Read in the whole ROM in one gulp
 	my $filesize = read(R, $rom, MAXROMSIZE+1);
@@ -183,10 +183,16 @@ sub makerom () {
 	}
 	# Pad with 0xFF to $romsize
 	$rom .= "\xFF" x ($romsize - length($rom));
-	if ($romsize >= 128 * 1024) {
-		print "Warning: ROM size exceeds extension BIOS limit\n";
+	# If this is a stub ROM, don't force header size to the full amount
+	if (!$opts{'n'}) {
+		if ($romsize >= 128 * 1024) {
+			print "Warning: ROM size exceeds extension BIOS limit\n";
+		}
+		substr($rom, 2, 1) = chr(($romsize / 512) % 256);
+	} else {
+		$stubsize = ord(substr($rom, 2, 1)) * 512;
+		print "Stub size is $stubsize\n" if $opts{'v'};
 	}
-	substr($rom, 2, 1) = chr(($romsize / 512) % 256);
 	print "ROM size is $romsize\n" if $opts{'v'};
 	# set the product string only if we don't have one yet
 	my $pnp_hdr_offset = unpack('v', substr($rom, PNP_PTR_LOC, 2));
@@ -196,7 +202,7 @@ sub makerom () {
 	# 3c503 requires last two bytes to be 0x80
 	substr($rom, MINROMSIZE-2, 2) = "\x80\x80"
 		if ($opts{'3'} and $romsize == MINROMSIZE);
-	checksum(\$rom);
+	checksum(\$rom, $opts{'n'} ? $stubsize : $romsize);
 	writerom($ARGV[0], \$rom);
 }
 
@@ -213,7 +219,7 @@ sub modrom () {
 	print "$filesize bytes read\n" if $opts{'v'};
 	pcipnpheaders(\$rom, undef);
 	undiheaders(\$rom);
-	checksum(\$rom);
+	checksum(\$rom, ord(substr($rom, 2, 1)) * 512);
 	writerom($ARGV[0], \$rom);
 }
 
diff --git a/gpxe/src/util/modrom.pl b/gpxe/src/util/modrom.pl
index 695468c..cdac0b9 100755
--- a/gpxe/src/util/modrom.pl
+++ b/gpxe/src/util/modrom.pl
@@ -131,9 +131,9 @@ sub writerom ($$) {
 sub checksum ($) {
 	my ($romref) = @_;
 
-	substr($$romref, 5, 1) = "\x00";
+	substr($$romref, 6, 1) = "\x00";
 	my $sum = unpack('%8C*', $$romref);
-	substr($$romref, 5, 1) = chr(256 - $sum);
+	substr($$romref, 6, 1) = chr(256 - $sum);
 	# Double check
 	$sum = unpack('%8C*', $$romref);
 	if ($sum != 0) {
diff --git a/gpxe/src/util/padimg.pl b/gpxe/src/util/padimg.pl
new file mode 100755
index 0000000..4421aaf
--- /dev/null
+++ b/gpxe/src/util/padimg.pl
@@ -0,0 +1,44 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Fcntl;
+
+my $verbosity = 0;
+my $blksize = 512;
+my $byte = 0;
+
+my %opts = (
+  'verbose|v+' => sub { $verbosity++; },
+  'quiet|q+' => sub { $verbosity--; },
+  'blksize|s=o' => sub { $blksize = $_[1]; },
+  'byte|b=o' => sub { $byte = $_[1]; },
+);
+
+Getopt::Long::Configure ( 'bundling', 'auto_abbrev' );
+GetOptions ( %opts ) or die "Could not parse command-line options\n";
+
+while ( my $filename = shift ) {
+  die "$filename is not a file\n" unless -f $filename;
+  my $oldsize = -s $filename;
+  my $padsize = ( ( -$oldsize ) % $blksize );
+  my $newsize = ( $oldsize + $padsize );
+  next unless $padsize;
+  if ( $verbosity >= 1 ) {
+      printf "Padding %s from %d to %d bytes with %d x 0x%02x\n",
+	     $filename, $oldsize, $newsize, $padsize, $byte;
+  }
+  if ( $byte ) {
+    sysopen ( my $fh, $filename, ( O_WRONLY | O_APPEND ) )
+	or die "Could not open $filename for appending: $!\n";
+    syswrite $fh, ( chr ( $byte ) x $padsize )
+	or die "Could not append to $filename: $!\n";
+    close ( $fh );
+  } else {
+    truncate $filename, $newsize
+	or die "Could not resize $filename: $!\n";
+  }
+  die "Failed to pad $filename\n"
+      unless ( ( ( -s $filename ) % $blksize ) == 0 );
+}
diff --git a/gpxe/src/util/parserom.pl b/gpxe/src/util/parserom.pl
index 3d41b54..578eb1d 100644
--- a/gpxe/src/util/parserom.pl
+++ b/gpxe/src/util/parserom.pl
@@ -45,7 +45,8 @@ while ( <DRV> ) {
          \s*0x([0-9A-Fa-f]{4})\s*, # PCI vendor
          \s*0x([0-9A-Fa-f]{4})\s*, # PCI device
          \s*\"([^\"]*)\"\s*,	   # Image
-         \s*\"([^\"]*)\"\s*	   # Description
+         \s*\"([^\"]*)\"\s*,	   # Description
+         \s*.*\s*		   # Driver data
        \)/x ) {
     ( my $vendor, my $device, my $image, my $desc ) = ( lc $1, lc $2, $3, $4 );
     rom ( "pci", $image, $desc, $vendor, $device );
diff --git a/gpxe/src/util/zbin.c b/gpxe/src/util/zbin.c
index c1082b3..707ae99 100644
--- a/gpxe/src/util/zbin.c
+++ b/gpxe/src/util/zbin.c
@@ -38,7 +38,7 @@ struct zinfo_pack {
 	uint32_t align;
 };
 
-struct zinfo_subtract {
+struct zinfo_add {
 	char type[4];
 	uint32_t offset;
 	uint32_t divisor;
@@ -49,7 +49,7 @@ union zinfo_record {
 	struct zinfo_common common;
 	struct zinfo_copy copy;
 	struct zinfo_pack pack;
-	struct zinfo_subtract subtract;
+	struct zinfo_add add;
 };
 
 struct zinfo_file {
@@ -57,6 +57,10 @@ struct zinfo_file {
 	unsigned int num_entries;
 };
 
+static unsigned long align ( unsigned long value, unsigned long align ) {
+	return ( ( value + align - 1 ) & ~( align - 1 ) );
+}
+
 static int read_file ( const char *filename, void **buf, size_t *len ) {
 	FILE *file;
 	struct stat stat;
@@ -77,13 +81,13 @@ static int read_file ( const char *filename, void **buf, size_t *len ) {
 	*len = stat.st_size;
 	*buf = malloc ( *len );
 	if ( ! *buf ) {
-		fprintf ( stderr, "Could not malloc() %d bytes for %s: %s\n",
+		fprintf ( stderr, "Could not malloc() %zd bytes for %s: %s\n",
 			  *len, filename, strerror ( errno ) );
 		goto err;
 	}
 
 	if ( fread ( *buf, 1, *len, file ) != *len ) {
-		fprintf ( stderr, "Could not read %d bytes from %s: %s\n",
+		fprintf ( stderr, "Could not read %zd bytes from %s: %s\n",
 			  *len, filename, strerror ( errno ) );
 		goto err;
 	}
@@ -111,7 +115,7 @@ static int read_zinfo_file ( const char *filename,
 		return -1;
 
 	if ( ( len % sizeof ( *(zinfo->zinfo) ) ) != 0 ) {
-		fprintf ( stderr, ".zinfo file %s has invalid length %d\n",
+		fprintf ( stderr, ".zinfo file %s has invalid length %zd\n",
 			  filename, len );
 		return -1;
 	}
@@ -126,7 +130,7 @@ static int alloc_output_file ( size_t max_len, struct output_file *output ) {
 	output->max_len = ( max_len );
 	output->buf = malloc ( max_len );
 	if ( ! output->buf ) {
-		fprintf ( stderr, "Could not allocate %d bytes for output\n",
+		fprintf ( stderr, "Could not allocate %zd bytes for output\n",
 			  max_len );
 		return -1;
 	}
@@ -140,22 +144,22 @@ static int process_zinfo_copy ( struct input_file *input,
 	struct zinfo_copy *copy = &zinfo->copy;
 	size_t offset = copy->offset;
 	size_t len = copy->len;
-	unsigned int align = copy->align;
 
 	if ( ( offset + len ) > input->len ) {
 		fprintf ( stderr, "Input buffer overrun on copy\n" );
 		return -1;
 	}
 
-	output->len = ( ( output->len + align - 1 ) & ~( align - 1 ) );
+	output->len = align ( output->len, copy->align );
 	if ( ( output->len + len ) > output->max_len ) {
 		fprintf ( stderr, "Output buffer overrun on copy\n" );
 		return -1;
 	}
 
 	if ( DEBUG ) {
-		fprintf ( stderr, "COPY [%#zx,%#zx) to [%#zx,%#zx)\n", offset, ( offset + len ),
-			  output->len, ( output->len + len ) );
+		fprintf ( stderr, "COPY [%#zx,%#zx) to [%#zx,%#zx)\n",
+			  offset, ( offset + len ), output->len,
+			  ( output->len + len ) );
 	}
 
 	memcpy ( ( output->buf + output->len ),
@@ -170,7 +174,6 @@ static int process_zinfo_pack ( struct input_file *input,
 	struct zinfo_pack *pack = &zinfo->pack;
 	size_t offset = pack->offset;
 	size_t len = pack->len;
-	unsigned int align = pack->align;
 	unsigned long packed_len;
 
 	if ( ( offset + len ) > input->len ) {
@@ -178,7 +181,7 @@ static int process_zinfo_pack ( struct input_file *input,
 		return -1;
 	}
 
-	output->len = ( ( output->len + align - 1 ) & ~( align - 1 ) );
+	output->len = align ( output->len, pack->align );
 	if ( output->len > output->max_len ) {
 		fprintf ( stderr, "Output buffer overrun on pack\n" );
 		return -1;
@@ -192,8 +195,9 @@ static int process_zinfo_pack ( struct input_file *input,
 	}
 
 	if ( DEBUG ) {
-		fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n", offset, ( offset + len ),
-			  output->len, ( output->len + packed_len ) );
+		fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n",
+			  offset, ( offset + len ), output->len,
+			  ( size_t )( output->len + packed_len ) );
 	}
 
 	output->len += packed_len;
@@ -205,76 +209,102 @@ static int process_zinfo_pack ( struct input_file *input,
 	return 0;
 }
 
-static int process_zinfo_subtract ( struct input_file *input,
-				    struct output_file *output,
-				    struct zinfo_subtract *subtract,
-				    size_t datasize ) {
-	size_t offset = subtract->offset;
+static int process_zinfo_add ( struct input_file *input,
+			       struct output_file *output,
+			       struct zinfo_add *add,
+			       size_t datasize ) {
+	size_t offset = add->offset;
 	void *target;
-	long delta;
-	unsigned long old;
-	unsigned long new;
+	signed long addend;
+	unsigned long size;
+	signed long val;
+	unsigned long mask;
 
 	if ( ( offset + datasize ) > output->len ) {
-		fprintf ( stderr, "Subtract at %#zx outside output buffer\n",
+		fprintf ( stderr, "Add at %#zx outside output buffer\n",
 			  offset );
 		return -1;
 	}
 
 	target = ( output->buf + offset );
-	delta = ( ( output->len / subtract->divisor ) -
-		  ( input->len / subtract->divisor ) );
+	size = ( align ( output->len, add->divisor ) / add->divisor );
 
 	switch ( datasize ) {
-	case 1: {
-		uint8_t *byte = target;
-		old = *byte;
-		*byte += delta;
-		new = *byte;
-		break; }
-	case 2: {
-		uint16_t *word = target;
-		old = *word;
-		*word += delta;
-		new = *word;
-		break; }
-	case 4: {
-		uint32_t *dword = target;
-		old = *dword;
-		*dword += delta;
-		new = *dword;
-		break; }
+	case 1:
+		addend = *( ( int8_t * ) target );
+		break;
+	case 2:
+		addend = *( ( int16_t * ) target );
+		break;
+	case 4:
+		addend = *( ( int32_t * ) target );
+		break;
 	default:
-		fprintf ( stderr, "Unsupported subtract datasize %d\n",
+		fprintf ( stderr, "Unsupported add datasize %zd\n",
 			  datasize );
 		return -1;
 	}
 
+	val = size + addend;
+
+	/* The result of 1UL << ( 8 * sizeof(unsigned long) ) is undefined */
+	mask = ( ( datasize < sizeof ( mask ) ) ?
+		 ( ( 1UL << ( 8 * datasize ) ) - 1 ) : ~0UL );
+
+	if ( val < 0 ) {
+		fprintf ( stderr, "Add %s%#x+%#lx at %#zx %sflows field\n",
+			  ( ( addend < 0 ) ? "-" : "" ), abs ( addend ), size,
+			  offset, ( ( addend < 0 ) ? "under" : "over" ) );
+		return -1;
+	}
+
+	if ( val & ~mask ) {
+		fprintf ( stderr, "Add %s%#x+%#lx at %#zx overflows %zd-byte "
+			  "field (%d bytes too big)\n",
+			  ( ( addend < 0 ) ? "-" : "" ), abs ( addend ), size,
+			  offset, datasize,
+			  ( int )( ( val - mask - 1 ) * add->divisor ) );
+		return -1;
+	}
+
+	switch ( datasize ) {
+	case 1:
+		*( ( uint8_t * ) target ) = val;
+		break;
+	case 2:
+		*( ( uint16_t * ) target ) = val;
+		break;
+	case 4:
+		*( ( uint32_t * ) target ) = val;
+		break;
+	}
+
 	if ( DEBUG ) {
-		fprintf ( stderr, "SUBx [%#zx,%#zx) (%#lx+(%#lx/%#lx)-(%#lx/%#lx)) = %#lx\n",
-			  offset, ( offset + datasize ), old, output->len, subtract->divisor,
-			  input->len, subtract->divisor, new );
+		fprintf ( stderr, "ADDx [%#zx,%#zx) (%s%#x+(%#zx/%#x)) = "
+			  "%#lx\n", offset, ( offset + datasize ),
+			  ( ( addend < 0 ) ? "-" : "" ), abs ( addend ),
+			  output->len, add->divisor, val );
 	}
 
 	return 0;
 }
 
-static int process_zinfo_subb ( struct input_file *input,
+static int process_zinfo_addb ( struct input_file *input,
 				struct output_file *output,
 				union zinfo_record *zinfo ) {
-	return process_zinfo_subtract ( input, output, &zinfo->subtract, 1 );
+	return process_zinfo_add ( input, output, &zinfo->add, 1 );
 }
 
-static int process_zinfo_subw ( struct input_file *input,
+static int process_zinfo_addw ( struct input_file *input,
 				struct output_file *output,
 				union zinfo_record *zinfo ) {
-	return process_zinfo_subtract ( input, output, &zinfo->subtract, 2 );
+	return process_zinfo_add ( input, output, &zinfo->add, 2 );
 }
 
-static int process_zinfo_subl ( struct input_file *input,
+static int process_zinfo_addl ( struct input_file *input,
 				struct output_file *output,
 				union zinfo_record *zinfo ) {
-	return process_zinfo_subtract ( input, output, &zinfo->subtract, 4 );
+	return process_zinfo_add ( input, output, &zinfo->add, 4 );
 }
 
 struct zinfo_processor {
@@ -287,9 +317,9 @@ struct zinfo_processor {
 static struct zinfo_processor zinfo_processors[] = {
 	{ "COPY", process_zinfo_copy },
 	{ "PACK", process_zinfo_pack },
-	{ "SUBB", process_zinfo_subb },
-	{ "SUBW", process_zinfo_subw },
-	{ "SUBL", process_zinfo_subl },
+	{ "ADDB", process_zinfo_addb },
+	{ "ADDW", process_zinfo_addw },
+	{ "ADDL", process_zinfo_addl },
 };
 
 static int process_zinfo ( struct input_file *input,
@@ -314,7 +344,7 @@ static int process_zinfo ( struct input_file *input,
 
 static int write_output_file ( struct output_file *output ) {
 	if ( fwrite ( output->buf, 1, output->len, stdout ) != output->len ) {
-		fprintf ( stderr, "Could not write %d bytes of output: %s\n",
+		fprintf ( stderr, "Could not write %zd bytes of output: %s\n",
 			  output->len, strerror ( errno ) );
 		return -1;
 	}



More information about the Syslinux-commits mailing list