Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / drivers / net / prism2.c
diff --git a/qemu/roms/ipxe/src/drivers/net/prism2.c b/qemu/roms/ipxe/src/drivers/net/prism2.c
new file mode 100644 (file)
index 0000000..ab97426
--- /dev/null
@@ -0,0 +1,855 @@
+/**************************************************************************
+Etherboot -  BOOTP/TFTP Bootstrap Program
+Prism2 NIC driver for Etherboot
+
+Written by Michael Brown of Fen Systems Ltd
+$Id$
+***************************************************************************/
+
+/*
+ * 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 <etherboot.h>
+#include <nic.h>
+#include <ipxe/pci.h>
+#include <ipxe/ethernet.h>
+
+/*
+ * Hard-coded SSID
+ * Leave blank in order to connect to any available SSID
+ */
+
+static const char hardcoded_ssid[] = "";
+
+/*
+ * Maximum number of info packets to wait for on a join attempt.
+ * Some APs (including the Linksys WAP11) will send a "you are disconnected" packet
+ * before sending the "you are connected" packet, if the card has previously been
+ * attached to the AP.
+ *
+ * 2 is probably a sensible value, but YMMV.
+ */
+
+#define MAX_JOIN_INFO_COUNT 2
+
+/*
+ * Type of Prism2 interface to support
+ * If not already defined, select PLX
+ */
+#ifndef WLAN_HOSTIF
+#define WLAN_HOSTIF WLAN_PLX
+#endif
+
+/*
+ * Include wlan_compat, p80211 and hfa384x header files from Linux Prism2 driver
+ * We need to hack some defines in order to avoid compiling kernel-specific routines
+ */
+
+#define __LINUX_WLAN__
+#undef __KERNEL__
+#define __I386__
+#include "wlan_compat.h"
+#include "p80211hdr.h"
+#include "hfa384x.h"
+#define BAP_TIMEOUT ( 5000 )
+
+/*
+ * A few hacks to make the coding environment more Linux-like.  This makes it somewhat
+ * quicker to convert code from the Linux Prism2 driver.
+ */
+#include <errno.h>
+#define __le16_to_cpu(x) (x)
+#define __le32_to_cpu(x) (x)
+#define __cpu_to_le16(x) (x)
+#define __cpu_to_le32(x) (x)
+
+#define hfa384x2host_16(n)     (__le16_to_cpu((uint16_t)(n)))
+#define hfa384x2host_32(n)     (__le32_to_cpu((uint32_t)(n)))
+#define host2hfa384x_16(n)     (__cpu_to_le16((uint16_t)(n)))
+#define host2hfa384x_32(n)     (__cpu_to_le32((uint32_t)(n)))
+
+/*
+ * PLX9052 PCI register offsets
+ * Taken from PLX9052 datasheet available from http://www.plxtech.com/download/9052/databook/9052db-20.pdf
+ */
+
+#define PLX_LOCAL_CONFIG_REGISTER_BASE ( PCI_BASE_ADDRESS_1 )
+#define PLX_LOCAL_ADDRESS_SPACE_0_BASE ( PCI_BASE_ADDRESS_2 )
+#define PLX_LOCAL_ADDRESS_SPACE_1_BASE ( PCI_BASE_ADDRESS_3 )
+#define PLX_LOCAL_ADDRESS_SPACE_2_BASE ( PCI_BASE_ADDRESS_4 )
+#define PLX_LOCAL_ADDRESS_SPACE_3_BASE ( PCI_BASE_ADDRESS_5 )
+
+#define PRISM2_PLX_ATTR_MEM_BASE       ( PLX_LOCAL_ADDRESS_SPACE_0_BASE )
+#define PRISM2_PLX_IO_BASE             ( PLX_LOCAL_ADDRESS_SPACE_1_BASE )
+
+#define PRISM2_PCI_MEM_BASE            ( PCI_BASE_ADDRESS_0 )
+
+/*
+ * PCMCIA CIS types
+ * Taken from cistpl.h in pcmcia-cs
+ */
+
+#define CISTPL_VERS_1           ( 0x15 )
+#define CISTPL_END              ( 0xff )
+
+#define CIS_STEP                ( 2 )
+#define CISTPL_HEADER_LEN       ( 2 * CIS_STEP )
+#define CISTPL_LEN_OFF          ( 1 * CIS_STEP )
+#define CISTPL_VERS_1_STR_OFF   ( 4 * CIS_STEP )
+
+/*
+ * Prism2 constants
+ * Taken from prism2sta.c in linux-wlan-ng
+ */
+
+#define COR_OFFSET      ( 0x3e0 )   /* COR attribute offset of Prism2 PC card */
+#define COR_VALUE       ( 0x41 )    /* Enable PC card with irq in level trigger (but interrupts disabled) */
+
+/* NIC specific static variables */
+
+/* The hfa384x_t structure is used extensively in the Linux driver but is ifdef'd out in our include since __KERNEL__ is not defined.
+ * This is a dummy version that contains only the fields we are interested in.
+ */
+
+typedef struct hfa384x
+{
+  uint32_t iobase;
+  void *membase;
+  uint16_t lastcmd;
+  uint16_t status;         /* in host order */
+  uint16_t resp0;          /* in host order */
+  uint16_t resp1;          /* in host order */
+  uint16_t resp2;          /* in host order */
+  uint8_t  bssid[WLAN_BSSID_LEN];
+} hfa384x_t;
+
+/* The global instance of the hardware (i.e. where we store iobase and membase, in the absence of anywhere better to put them */
+static hfa384x_t hw_global;
+
+/*
+ * 802.11 headers in addition to those in hfa384x_tx_frame_t (LLC and SNAP)
+ * Taken from p80211conv.h
+ */
+
+typedef struct wlan_llc
+{
+  uint8_t   dsap;
+  uint8_t   ssap;
+  uint8_t   ctl;
+}  wlan_llc_t;
+
+static const wlan_llc_t wlan_llc_snap = { 0xaa, 0xaa, 0x03 }; /* LLC header indicating SNAP (?) */
+
+#define WLAN_IEEE_OUI_LEN 3
+typedef struct wlan_snap
+{
+  uint8_t   oui[WLAN_IEEE_OUI_LEN];
+  uint16_t  type;
+} wlan_snap_t;
+
+typedef struct wlan_80211hdr
+{
+  wlan_llc_t llc;
+  wlan_snap_t snap;
+} wlan_80211hdr_t;
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * Hardware-level hfa384x functions
+ * These are based on the ones in hfa384x.h (which are ifdef'd out since __KERNEL__ is not defined).
+ * Basically, these functions are the result of hand-evaluating all the ifdefs and defines in the hfa384x.h versions.
+ */
+
+/* Retrieve the value of one of the MAC registers. */
+static inline uint16_t hfa384x_getreg( hfa384x_t *hw, unsigned int reg )
+{
+#if (WLAN_HOSTIF == WLAN_PLX)
+  return inw ( hw->iobase + reg );
+#elif (WLAN_HOSTIF == WLAN_PCI)
+  return readw ( hw->membase + reg );
+#endif
+}
+
+/* Set the value of one of the MAC registers. */
+static inline void hfa384x_setreg( hfa384x_t *hw, uint16_t val, unsigned int reg )
+{
+#if (WLAN_HOSTIF == WLAN_PLX)
+  outw ( val, hw->iobase + reg );
+#elif (WLAN_HOSTIF == WLAN_PCI)
+  writew ( val, hw->membase + reg );
+#endif
+  return;
+}
+
+/*
+ * Noswap versions
+ * Etherboot is i386 only, so swap and noswap are the same...
+ */
+static inline uint16_t hfa384x_getreg_noswap( hfa384x_t *hw, unsigned int reg )
+{
+  return hfa384x_getreg ( hw, reg );
+}
+static inline void hfa384x_setreg_noswap( hfa384x_t *hw, uint16_t val, unsigned int reg )
+{
+  hfa384x_setreg ( hw, val, reg );
+}
+
+/*
+ * Low-level hfa384x functions
+ * These are based on the ones in hfa384x.c, modified to work in the Etherboot environment.
+ */
+
+/*
+ * hfa384x_docmd_wait
+ *
+ * Waits for availability of the Command register, then
+ * issues the given command.  Then polls the Evstat register
+ * waiting for command completion.
+ * Arguments:
+ *       hw              device structure
+ *       cmd             Command in host order
+ *       parm0           Parameter0 in host order
+ *       parm1           Parameter1 in host order
+ *       parm2           Parameter2 in host order
+ * Returns:
+ *       0               success
+ *       >0              command indicated error, Status and Resp0-2 are
+ *                       in hw structure.
+ */
+static int hfa384x_docmd_wait( hfa384x_t *hw, uint16_t cmd, uint16_t parm0, uint16_t parm1, uint16_t parm2)
+{
+  uint16_t reg = 0;
+  uint16_t counter = 0;
+
+  /* wait for the busy bit to clear */
+  counter = 0;
+  reg = hfa384x_getreg(hw, HFA384x_CMD);
+  while ( HFA384x_CMD_ISBUSY(reg) && (counter < 10) ) {
+    reg = hfa384x_getreg(hw, HFA384x_CMD);
+    counter++;
+    udelay(10);
+  }
+  if (HFA384x_CMD_ISBUSY(reg)) {
+    printf("hfa384x_cmd timeout(1), reg=0x%0hx.\n", reg);
+    return -ETIMEDOUT;
+  }
+
+  /* busy bit clear, write command */
+  hfa384x_setreg(hw, parm0, HFA384x_PARAM0);
+  hfa384x_setreg(hw, parm1, HFA384x_PARAM1);
+  hfa384x_setreg(hw, parm2, HFA384x_PARAM2);
+  hw->lastcmd = cmd;
+  hfa384x_setreg(hw, cmd, HFA384x_CMD);
+
+  /* Now wait for completion */
+  counter = 0;
+  reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+  /* Initialization is the problem.  It takes about
+     100ms. "normal" commands are typically is about
+     200-400 us (I've never seen less than 200).  Longer
+     is better so that we're not hammering the bus. */
+  while ( !HFA384x_EVSTAT_ISCMD(reg) && (counter < 5000)) {
+    reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+    counter++;
+    udelay(200);
+  }
+  if ( ! HFA384x_EVSTAT_ISCMD(reg) ) {
+    printf("hfa384x_cmd timeout(2), reg=0x%0hx.\n", reg);
+    return -ETIMEDOUT;
+  }
+
+  /* Read status and response */
+  hw->status = hfa384x_getreg(hw, HFA384x_STATUS);
+  hw->resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
+  hw->resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
+  hw->resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
+  hfa384x_setreg(hw, HFA384x_EVACK_CMD, HFA384x_EVACK);
+  return HFA384x_STATUS_RESULT_GET(hw->status);
+}
+
+/*
+ * Prepare BAP for access.  Assigns FID and RID, sets offset register
+ * and waits for BAP to become available.
+ *
+ * Arguments:
+ *     hw              device structure
+ *     id              FID or RID, destined for the select register (host order)
+ *     offset          An _even_ offset into the buffer for the given FID/RID.
+ * Returns:
+ *     0               success
+ */
+static int hfa384x_prepare_bap(hfa384x_t *hw, uint16_t id, uint16_t offset)
+{
+  int result = 0;
+  uint16_t reg;
+  uint16_t i;
+
+  /* Validate offset, buf, and len */
+  if ( (offset > HFA384x_BAP_OFFSET_MAX) || (offset % 2) ) {
+    result = -EINVAL;
+  } else {
+    /* Write fid/rid and offset */
+    hfa384x_setreg(hw, id, HFA384x_SELECT0);
+    udelay(10);
+    hfa384x_setreg(hw, offset, HFA384x_OFFSET0);
+    /* Wait for offset[busy] to clear (see BAP_TIMEOUT) */
+    i = 0;
+    do {
+      reg = hfa384x_getreg(hw, HFA384x_OFFSET0);
+      if ( i > 0 ) udelay(2);
+      i++;
+    } while ( i < BAP_TIMEOUT && HFA384x_OFFSET_ISBUSY(reg));
+    if ( i >= BAP_TIMEOUT ) {
+      /* failure */
+      result = reg;
+    } else if ( HFA384x_OFFSET_ISERR(reg) ){
+      /* failure */
+      result = reg;
+    }
+  }
+  return result;
+}
+
+/*
+ * Copy data from BAP to memory.
+ *
+ * Arguments:
+ *     hw              device structure
+ *     id              FID or RID, destined for the select register (host order)
+ *     offset          An _even_ offset into the buffer for the given FID/RID.
+ *     buf             ptr to array of bytes
+ *     len             length of data to transfer in bytes
+ * Returns:
+ *     0               success
+ */
+static int hfa384x_copy_from_bap(hfa384x_t *hw, uint16_t id, uint16_t offset,
+                         void *buf, unsigned int len)
+{
+  int result = 0;
+  uint8_t      *d = (uint8_t*)buf;
+  uint16_t i;
+  uint16_t reg = 0;
+
+  /* Prepare BAP */
+  result = hfa384x_prepare_bap ( hw, id, offset );
+  if ( result == 0 ) {
+    /* Read even(len) buf contents from data reg */
+    for ( i = 0; i < (len & 0xfffe); i+=2 ) {
+      *(uint16_t*)(&(d[i])) = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+    }
+    /* If len odd, handle last byte */
+    if ( len % 2 ){
+      reg = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+      d[len-1] = ((uint8_t*)(&reg))[0];
+    }
+  }
+  if (result) {
+    printf ( "copy_from_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result);
+  }
+  return result;
+}
+
+/*
+ * Copy data from memory to BAP.
+ *
+ * Arguments:
+ *     hw              device structure
+ *     id              FID or RID, destined for the select register (host order)
+ *     offset          An _even_ offset into the buffer for the given FID/RID.
+ *     buf             ptr to array of bytes
+ *     len             length of data to transfer in bytes
+ * Returns:
+ *     0               success
+ */
+static int hfa384x_copy_to_bap(hfa384x_t *hw, uint16_t id, uint16_t offset,
+                       void *buf, unsigned int len)
+{
+  int result = 0;
+  uint8_t      *d = (uint8_t*)buf;
+  uint16_t i;
+  uint16_t savereg;
+
+  /* Prepare BAP */
+  result = hfa384x_prepare_bap ( hw, id, offset );
+  if ( result == 0 ) {
+    /* Write even(len) buf contents to data reg */
+    for ( i = 0; i < (len & 0xfffe); i+=2 ) {
+      hfa384x_setreg_noswap(hw, *(uint16_t*)(&(d[i])), HFA384x_DATA0);
+    }
+    /* If len odd, handle last byte */
+    if ( len % 2 ){
+      savereg = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+      result = hfa384x_prepare_bap ( hw, id, offset + (len & 0xfffe) );
+      if ( result == 0 ) {
+       ((uint8_t*)(&savereg))[0] = d[len-1];
+       hfa384x_setreg_noswap(hw, savereg, HFA384x_DATA0);
+      }
+    }
+  }
+  if (result) {
+    printf ( "copy_to_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result);
+  }
+  return result;
+}
+
+/*
+ * Request a given record to be copied to/from the record buffer.
+ *
+ * Arguments:
+ *     hw              device structure
+ *     write           [0|1] copy the record buffer to the given
+ *                     configuration record. (host order)
+ *     rid             RID of the record to read/write. (host order)
+ *
+ * Returns:
+ *     0               success
+ */
+static inline int hfa384x_cmd_access(hfa384x_t *hw, uint16_t write, uint16_t rid)
+{
+  return hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ACCESS) | HFA384x_CMD_WRITE_SET(write), rid, 0, 0);
+}
+
+/*
+ * Performs the sequence necessary to read a config/info item.
+ *
+ * Arguments:
+ *     hw              device structure
+ *     rid             config/info record id (host order)
+ *     buf             host side record buffer.  Upon return it will
+ *                     contain the body portion of the record (minus the
+ *                     RID and len).
+ *     len             buffer length (in bytes, should match record length)
+ *
+ * Returns:
+ *     0               success
+ */
+static int hfa384x_drvr_getconfig(hfa384x_t *hw, uint16_t rid, void *buf, uint16_t len)
+{
+  int result = 0;
+  hfa384x_rec_t        rec;
+
+  /* Request read of RID */
+  result = hfa384x_cmd_access( hw, 0, rid);
+  if ( result ) {
+    printf("Call to hfa384x_cmd_access failed\n");
+    return -1;
+  }
+  /* Copy out record length */
+  result = hfa384x_copy_from_bap( hw, rid, 0, &rec, sizeof(rec));
+  if ( result ) {
+    return -1;
+  }
+  /* Validate the record length */
+  if ( ((hfa384x2host_16(rec.reclen)-1)*2) != len ) {  /* note body len calculation in bytes */
+    printf ( "RID len mismatch, rid=%#hx hlen=%d fwlen=%d\n", rid, len, (hfa384x2host_16(rec.reclen)-1)*2);
+    return -1;
+  }
+  /* Copy out record data */
+  result = hfa384x_copy_from_bap( hw, rid, sizeof(rec), buf, len);
+  return result;
+}
+
+/*
+ * Performs the sequence necessary to read a 16/32 bit config/info item
+ * and convert it to host order.
+ *
+ * Arguments:
+ *     hw              device structure
+ *     rid             config/info record id (in host order)
+ *     val             ptr to 16/32 bit buffer to receive value (in host order)
+ *
+ * Returns:
+ *     0               success
+ */
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_getconfig16(hfa384x_t *hw, uint16_t rid, void *val)
+{
+  int result = 0;
+  result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(uint16_t));
+  if ( result == 0 ) {
+    *((uint16_t*)val) = hfa384x2host_16(*((uint16_t*)val));
+  }
+  return result;
+}
+#endif
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_getconfig32(hfa384x_t *hw, uint16_t rid, void *val)
+{
+  int result = 0;
+  result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(uint32_t));
+  if ( result == 0 ) {
+    *((uint32_t*)val) = hfa384x2host_32(*((uint32_t*)val));
+  }
+  return result;
+}
+#endif
+
+/*
+ * Performs the sequence necessary to write a config/info item.
+ *
+ * Arguments:
+ *     hw              device structure
+ *     rid             config/info record id (in host order)
+ *     buf             host side record buffer
+ *     len             buffer length (in bytes)
+ *
+ * Returns:
+ *     0               success
+ */
+static int hfa384x_drvr_setconfig(hfa384x_t *hw, uint16_t rid, void *buf, uint16_t len)
+{
+  int result = 0;
+  hfa384x_rec_t        rec;
+
+  rec.rid = host2hfa384x_16(rid);
+  rec.reclen = host2hfa384x_16((len/2) + 1); /* note conversion to words, +1 for rid field */
+  /* write the record header */
+  result = hfa384x_copy_to_bap( hw, rid, 0, &rec, sizeof(rec));
+  if ( result ) {
+    printf("Failure writing record header\n");
+    return -1;
+  }
+  /* write the record data (if there is any) */
+  if ( len > 0 ) {
+    result = hfa384x_copy_to_bap( hw, rid, sizeof(rec), buf, len);
+    if ( result ) {
+      printf("Failure writing record data\n");
+      return -1;
+    }
+  }
+  /* Trigger setting of record */
+  result = hfa384x_cmd_access( hw, 1, rid);
+  return result;
+}
+
+/*
+ * Performs the sequence necessary to write a 16/32 bit config/info item.
+ *
+ * Arguments:
+ *     hw              device structure
+ *     rid             config/info record id (in host order)
+ *     val             16/32 bit value to store (in host order)
+ *
+ * Returns:
+ *     0               success
+ */
+static int hfa384x_drvr_setconfig16(hfa384x_t *hw, uint16_t rid, uint16_t *val)
+{
+  uint16_t value;
+  value = host2hfa384x_16(*val);
+  return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(uint16_t));
+}
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_setconfig32(hfa384x_t *hw, uint16_t rid, uint32_t *val)
+{
+  uint32_t value;
+  value = host2hfa384x_32(*val);
+  return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(uint32_t));
+}
+#endif
+
+/*
+ * Wait for an event, with specified checking interval and timeout.
+ * Automatically acknolwedges events.
+ *
+ * Arguments:
+ *     hw              device structure
+ *      event_mask      EVSTAT register mask of events to wait for
+ *     event_ack       EVACK register set of events to be acknowledged if they happen (can be
+ *                     used to acknowledge "ignorable" events in addition to the "main" event)
+ *      wait            Time (in us) to wait between each poll of the register
+ *      timeout         Maximum number of polls before timing out
+ *      descr           Descriptive text string of what is being waited for
+ *                      (will be printed out if a timeout happens)
+ *
+ * Returns:
+ *      value of EVSTAT register, or 0 on failure
+ */
+static int hfa384x_wait_for_event(hfa384x_t *hw, uint16_t event_mask, uint16_t event_ack, int wait, int timeout, const char *descr)
+{
+  uint16_t reg;
+  int count = 0;
+
+  do {
+    reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+    if ( count > 0 ) udelay(wait);
+    count++;
+  } while ( !(reg & event_mask) && count < timeout);
+  if ( count >= timeout ) {
+    printf("hfa384x: Timed out waiting for %s\n", descr);
+    return 0; /* Return failure */
+  }
+  /* Acknowledge all events that we were waiting on */
+  hfa384x_setreg(hw, reg & ( event_mask | event_ack ), HFA384x_EVACK);
+  return reg;
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int prism2_poll(struct nic *nic, int retrieve)
+{
+  uint16_t reg;
+  uint16_t rxfid;
+  uint16_t result;
+  hfa384x_rx_frame_t rxdesc;
+  hfa384x_t *hw = &hw_global;
+
+  /* Check for received packet */
+  reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+  if ( ! HFA384x_EVSTAT_ISRX(reg) ) {
+    /* No packet received - return 0 */
+    return 0;
+  }
+
+  if ( ! retrieve ) return 1;
+
+  /* Acknowledge RX event */
+  hfa384x_setreg(hw, HFA384x_EVACK_RX_SET(1), HFA384x_EVACK);
+  /* Get RX FID */
+  rxfid = hfa384x_getreg(hw, HFA384x_RXFID);
+  /* Get the descriptor (including headers) */
+  result = hfa384x_copy_from_bap(hw, rxfid, 0, &rxdesc, sizeof(rxdesc));
+  if ( result ) {
+    return 0; /* fail */
+  }
+  /* Byte order convert once up front. */
+  rxdesc.status = hfa384x2host_16(rxdesc.status);
+  rxdesc.time = hfa384x2host_32(rxdesc.time);
+  rxdesc.data_len = hfa384x2host_16(rxdesc.data_len);
+
+  /* Fill in nic->packetlen */
+  nic->packetlen = rxdesc.data_len;
+  if ( nic->packetlen > 0 ) {
+    /* Fill in nic->packet */
+    /*
+     * NOTE: Packets as received have an 8-byte header (LLC+SNAP(?)) terminating with the packet type.
+     * Etherboot expects a 14-byte header terminating with the packet type (it ignores the rest of the
+     * header), so we use a quick hack to achieve this.
+     */
+    result = hfa384x_copy_from_bap(hw, rxfid, HFA384x_RX_DATA_OFF,
+                                  nic->packet + ETH_HLEN - sizeof(wlan_80211hdr_t), nic->packetlen);
+    if ( result ) {
+      return 0; /* fail */
+    }
+  }
+  return 1; /* Packet successfully received */
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void prism2_transmit(
+                           struct nic *nic,
+                           const char *d,                      /* Destination */
+                           unsigned int t,                     /* Type */
+                           unsigned int s,                     /* size */
+                           const char *p)                      /* Packet */
+{
+  hfa384x_t *hw = &hw_global;
+  hfa384x_tx_frame_t txdesc;
+  wlan_80211hdr_t p80211hdr = { wlan_llc_snap, {{0,0,0},0} };
+  uint16_t fid;
+  uint16_t status;
+  int result;
+
+  // Request FID allocation
+  result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ALLOC), HFA384x_DRVR_TXBUF_MAX, 0, 0);
+  if (result != 0) {
+    printf("hfa384x: Tx FID allocate command failed: Aborting transmit..\n");
+    return;
+  }
+  if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_ALLOC, HFA384x_EVACK_INFO, 10, 50, "Tx FID to be allocated\n" ) ) return;
+  fid = hfa384x_getreg(hw, HFA384x_ALLOCFID);
+
+  /* Build Tx frame structure */
+  memset(&txdesc, 0, sizeof(txdesc));
+  txdesc.tx_control = host2hfa384x_16( HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
+                                      HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1) );
+  txdesc.frame_control =  host2ieee16( WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) |
+                                      WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY) |
+                                      WLAN_SET_FC_TODS(1) );
+  memcpy(txdesc.address1, hw->bssid, WLAN_ADDR_LEN);
+  memcpy(txdesc.address2, nic->node_addr, WLAN_ADDR_LEN);
+  memcpy(txdesc.address3, d, WLAN_ADDR_LEN);
+  txdesc.data_len = host2hfa384x_16( sizeof(txdesc) + sizeof(p80211hdr) + s );
+  /* Set up SNAP header */
+  /* Let OUI default to RFC1042 (0x000000) */
+  p80211hdr.snap.type = htons(t);
+
+  /* Copy txdesc, p80211hdr and payload parts to FID */
+  result = hfa384x_copy_to_bap(hw, fid, 0, &txdesc, sizeof(txdesc));
+  if ( result ) return; /* fail */
+  result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc), &p80211hdr, sizeof(p80211hdr) );
+  if ( result ) return; /* fail */
+  result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc) + sizeof(p80211hdr), (uint8_t*)p, s );
+  if ( result ) return; /* fail */
+
+  /* Issue Tx command */
+  result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_TX), fid, 0, 0);
+  if ( result != 0 ) {
+    printf("hfa384x: Transmit failed with result %#hx.\n", result);
+    return;
+  }
+
+  /* Wait for transmit completion (or exception) */
+  result = hfa384x_wait_for_event(hw, HFA384x_EVSTAT_TXEXC | HFA384x_EVSTAT_TX, HFA384x_EVACK_INFO,
+                                 200, 500, "Tx to complete\n" );
+  if ( !result ) return; /* timeout failure */
+  if ( HFA384x_EVSTAT_ISTXEXC(result) ) {
+    fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID);
+    printf ( "Tx exception occurred with fid %#hx\n", fid );
+    result = hfa384x_copy_from_bap(hw, fid, 0, &status, sizeof(status));
+    if ( result ) return; /* fail */
+    printf("hfa384x: Tx error occurred (status %#hx):\n", status);
+    if ( HFA384x_TXSTATUS_ISACKERR(status) ) { printf(" ...acknowledgement error\n"); }
+    if ( HFA384x_TXSTATUS_ISFORMERR(status) ) { printf(" ...format error\n"); }
+    if ( HFA384x_TXSTATUS_ISDISCON(status) ) { printf(" ...disconnected error\n"); }
+    if ( HFA384x_TXSTATUS_ISAGEDERR(status) ) { printf(" ...AGED error\n"); }
+    if ( HFA384x_TXSTATUS_ISRETRYERR(status) ) { printf(" ...retry error\n"); }
+    return; /* fail */
+  }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void prism2_disable ( struct nic *nic __unused ) {
+  /* put the card in its initial state */
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void prism2_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+  switch ( action ) {
+  case DISABLE :
+    break;
+  case ENABLE :
+    break;
+  case FORCE :
+    break;
+  }
+}
+
+/**************************************************************************
+Operations table
+***************************************************************************/
+static struct nic_operations prism2_operations = {
+       .connect        = dummy_connect,
+       .poll           = prism2_poll,
+       .transmit       = prism2_transmit,
+       .irq            = prism2_irq,
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+static int prism2_probe ( struct nic *nic, hfa384x_t *hw ) {
+  int result;
+  uint16_t tmp16 = 0;
+  uint16_t infofid;
+  hfa384x_InfFrame_t inf;
+  char ssid[HFA384x_RID_CNFDESIREDSSID_LEN];
+  int info_count = 0;
+
+  nic->irqno  = 0;
+
+  /* Initialize card */
+  result = hfa384x_docmd_wait(hw, HFA384x_CMDCODE_INIT, 0,0,0); /* Send initialize command */
+  if ( result ) printf ( "Initialize command returned %#hx\n", result );
+  hfa384x_setreg(hw, 0, HFA384x_INTEN); /* Disable interrupts */
+  hfa384x_setreg(hw, 0xffff, HFA384x_EVACK); /* Acknowledge any spurious events */
+
+  DBG ( "MAC address %s\n", eth_ntoa ( nic->node_addr ) );
+
+  /* Retrieve MAC address (and fill out nic->node_addr) */
+  hfa384x_drvr_getconfig ( hw, HFA384x_RID_CNFOWNMACADDR, nic->node_addr, HFA384x_RID_CNFOWNMACADDR_LEN );
+
+  /* Prepare card for autojoin */
+  /* This procedure is reverse-engineered from a register-level trace of the Linux driver's join process */
+  tmp16 = WLAN_DATA_MAXLEN; /* Set maximum data length */
+  result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, &tmp16);
+  if ( result ) printf ( "Set Max Data Length command returned %#hx\n", result );
+  tmp16 = 0x000f; /* Set transmit rate(?) */
+  result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, &tmp16);
+  if ( result ) printf ( "Set Transmit Rate command returned %#hx\n", result );
+  tmp16 = HFA384x_CNFAUTHENTICATION_OPENSYSTEM; /* Set authentication type to OpenSystem */
+  result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAUTHENTICATION, &tmp16);
+  if ( result ) printf ( "Set Authentication Type command returned %#hx\n", result );
+  /* Set SSID */
+  memset(ssid, 0, HFA384x_RID_CNFDESIREDSSID_LEN);
+  for ( tmp16=0; tmp16<sizeof(hardcoded_ssid); tmp16++ ) { ssid[2+tmp16] = hardcoded_ssid[tmp16]; }
+  ssid[0] = sizeof(hardcoded_ssid) - 1; /* Ignore terminating zero */
+  result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID, ssid, HFA384x_RID_CNFDESIREDSSID_LEN); /* Set the SSID */
+  if ( result ) printf ( "Set SSID command returned %#hx\n", result );
+  tmp16 = 1; /* Set port type to ESS port */
+  result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPORTTYPE, &tmp16);
+  if ( result ) printf ( "Set port type command returned %#hx\n", result );
+  /* Enable card */
+  result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ENABLE) | HFA384x_CMD_MACPORT_SET(0), 0,0,0);
+  if ( result ) printf ( "Enable command returned %#hx\n", result );
+
+  do {
+    /* Increment info_count, abort if too many attempts.
+     * See comment next to definition of MAX_JOIN_INFO_COUNT for explanation.
+     */
+    info_count++;
+    if ( info_count > MAX_JOIN_INFO_COUNT ) {
+      printf ( "Too many failed attempts - aborting\n" );
+      return 0;
+    }
+
+    /* Wait for info frame to indicate link status */
+    if ( sizeof(hardcoded_ssid) == 1 ) {
+      /* Empty SSID => join to any SSID */
+      printf ( "Attempting to autojoin to any available access point (attempt %d)...", info_count );
+    } else {
+      printf ( "Attempting to autojoin to SSID %s (attempt %d)...", &ssid[2], info_count );
+    }
+
+    if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_INFO, 0, 1000, 2000, "Info event" ) ) return 0;
+    printf("done\n");
+    infofid = hfa384x_getreg(hw, HFA384x_INFOFID);
+    /* Retrieve the length */
+    result = hfa384x_copy_from_bap( hw, infofid, 0, &inf.framelen, sizeof(uint16_t));
+    if ( result ) return 0; /* fail */
+    inf.framelen = hfa384x2host_16(inf.framelen);
+    /* Retrieve the rest */
+    result = hfa384x_copy_from_bap( hw, infofid, sizeof(uint16_t),
+                                   &(inf.infotype), inf.framelen * sizeof(uint16_t));
+    if ( result ) return 0; /* fail */
+    if ( inf.infotype != HFA384x_IT_LINKSTATUS ) {
+      /* Not a Link Status info frame: die */
+      printf ( "Unexpected info frame type %#hx (not LinkStatus type)\n", inf.infotype );
+      return 0;
+    }
+    inf.info.linkstatus.linkstatus = hfa384x2host_16(inf.info.linkstatus.linkstatus);
+    if ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED ) {
+      /* Link not connected - retry */
+      printf ( "Link not connected (status %#hx)\n", inf.info.linkstatus.linkstatus );
+    }
+  } while ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED );
+
+  /* Retrieve BSSID and print Connected message */
+  result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CURRENTBSSID, hw->bssid, WLAN_BSSID_LEN);
+
+  DBG ( "Link connected (BSSID %s - ", eth_ntoa ( hw->bssid ) );
+  DBG ( " MAC address %s)\n", eth_ntoa (nic->node_addr ) );
+
+  /* point to NIC specific routines */
+  nic->nic_op  = &prism2_operations;
+  return 1;
+}
+