/* src/prism2/driver/hfa384x.c
*
* Implements the functions of the Intersil hfa384x MAC
*
* Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
* --------------------------------------------------------------------
*
* linux-wlan
*
*   The contents of this file are subject to the Mozilla Public
*   License Version 1.1 (the "License"); you may not use this file
*   except in compliance with the License. You may obtain a copy of
*   the License at http://www.mozilla.org/MPL/
*
*   Software distributed under the License is distributed on an "AS
*   IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
*   implied. See the License for the specific language governing
*   rights and limitations under the License.
*
*   Alternatively, the contents of this file may be used under the
*   terms of the GNU Public License version 2 (the "GPL"), in which
*   case the provisions of the GPL are applicable instead of the
*   above.  If you wish to allow the use of your version of this file
*   only under the terms of the GPL and not to allow others to use
*   your version of this file under the MPL, indicate your decision
*   by deleting the provisions above and replace them with the notice
*   and other provisions required by the GPL.  If you do not delete
*   the provisions above, a recipient may use your version of this
*   file under either the MPL or the GPL.
*
* --------------------------------------------------------------------
*
* Inquiries regarding the linux-wlan Open Source project can be
* made directly to:
*
* AbsoluteValue Systems Inc.
* info@linux-wlan.com
* http://www.linux-wlan.com
*
* --------------------------------------------------------------------
*
* Portions of the development of this software were funded by 
* Intersil Corporation as part of PRISM(R) chipset product development.
*
* --------------------------------------------------------------------
*
* This file implements functions that correspond to the prism2/hfa384x
* 802.11 MAC hardware and firmware host interface.
*
* The functions can be considered to represent several levels of 
* abstraction.  The lowest level functions are simply C-callable wrappers
* around the register accesses.  The next higher level represents C-callable
* prism2 API functions that match the Intersil documentation as closely
* as is reasonable.  The next higher layer implements common sequences 
* of invokations of the API layer (e.g. write to bap, followed by cmd).
*
* Common sequences:
* hfa384x_drvr_xxx	Highest level abstractions provided by the 
*			hfa384x code.  They are driver defined wrappers 
*			for common sequences.  These functions generally
*			use the services of the lower levels.
*
* hfa384x_drvr_xxxconfig  An example of the drvr level abstraction. These
*			functions are wrappers for the RID get/set 
*			sequence. They 	call copy_[to|from]_bap() and 
*			cmd_access().	These functions operate on the 
*			RIDs and buffers without validation.  The caller
*			is responsible for that.
*
* API wrapper functions:
* hfa384x_cmd_xxx	functions that provide access to the f/w commands.  
*			The function arguments correspond to each command
*			argument, even command arguments that get packed
*			into single registers.  These functions _just_
*			issue the command by setting the cmd/parm regs
*			& reading the status/resp regs.  Additional
*			activities required to fully use a command
*			(read/write from/to bap, get/set int status etc.)
*			are implemented separately.  Think of these as
*			C-callable prism2 commands.
*
* Lowest Layer Functions:
* hfa384x_docmd_xxx	These functions implement the sequence required
*			to issue any prism2 command.  Primarily used by the
*			hfa384x_cmd_xxx functions.
*
* hfa384x_bap_xxx	BAP read/write access functions.
*			Note: we usually use BAP0 for non-interrupt context
*			 and BAP1 for interrupt context.
*
* hfa384x_dl_xxx	download related functions.
*                 	
* Driver State Issues:
* Note that there are two pairs of functions that manage the
* 'initialized' and 'running' states of the hw/MAC combo.  The four
* functions are create(), destroy(), start(), and stop().  create()
* sets up the data structures required to support the hfa384x_*
* functions and destroy() cleans them up.  The start() function gets
* the actual hardware running and enables the interrupts.  The stop()
* function shuts the hardware down.  The sequence should be:
* create()
*  .
*  .  Self contained test routines can run here, particularly
*  .  corereset() and test_hostif().
*  .
* start()
*  .
*  .  Do interesting things w/ the hardware
*  .
* stop()
* destroy()
*
* Note that destroy() can be called without calling stop() first.
* --------------------------------------------------------------------
*/

/*================================================================*/

/* System Includes */
#define WLAN_DBVAR	prism2_debug
#include <wlan/version.h>

#include <linux/config.h>
#include <linux/version.h>

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/wireless.h>
#include <linux/netdevice.h>
#include <linux/timer.h>
#include <asm/semaphore.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <asm/byteorder.h>
#include <linux/list.h>

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
#include <linux/tqueue.h>
#else
#include <linux/workqueue.h>
#endif

#if (WLAN_HOSTIF == WLAN_PCMCIA)
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#endif

#if ((WLAN_HOSTIF == WLAN_PLX) || (WLAN_HOSTIF == WLAN_PCI))
#include <linux/ioport.h>
#include <linux/pci.h>
#endif

#include <wlan/wlan_compat.h>

// XXXX #define CMD_IRQ

/*================================================================*/
/* Project Includes */

#include <wlan/p80211types.h>
#include <wlan/p80211hdr.h>
#include <wlan/p80211mgmt.h>
#include <wlan/p80211conv.h>
#include <wlan/p80211msg.h>
#include <wlan/p80211netdev.h>
#include <wlan/p80211req.h>
#include <wlan/p80211metadef.h>
#include <wlan/p80211metastruct.h>
#include <prism2/hfa384x.h>
#include <prism2/prism2mgmt.h>

/*================================================================*/
/* Local Constants */

const UINT16 crc16tab[256] =
{
	0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
	0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
	0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
	0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
	0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
	0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
	0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
	0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
	0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
	0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
	0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
	0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
	0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
	0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
	0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
	0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
	0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
	0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
	0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
	0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
	0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
	0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
	0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
	0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
	0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
	0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
	0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
	0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
	0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
	0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
	0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
	0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
};

/*================================================================*/
/* Local Macros */

/*================================================================*/
/* Local Types */

/*================================================================*/
/* Local Static Definitions */
extern int prism2_debug;

/*================================================================*/
/* Local Function Declarations */

static void	hfa384x_int_dtim(wlandevice_t *wlandev);
static void	hfa384x_int_infdrop(wlandevice_t *wlandev);

#ifdef DECLARE_TASKLET
static void     hfa384x_bap_tasklet(unsigned long data);
#else
static void     hfa384x_bap_tasklet(void *data);
#endif

static void	hfa384x_int_info(wlandevice_t *wlandev);
static void	hfa384x_int_txexc(wlandevice_t *wlandev);
static void	hfa384x_int_tx(wlandevice_t *wlandev);
static void	hfa384x_int_rx(wlandevice_t *wlandev);

#ifdef CMD_IRQ
static void	hfa384x_int_cmd(wlandevice_t *wlandev);
#endif
static void	hfa384x_int_rxmonitor( wlandevice_t *wlandev, 
			UINT16 rxfid, hfa384x_rx_frame_t *rxdesc);
static void	hfa384x_int_alloc(wlandevice_t *wlandev);

static int hfa384x_docmd_wait( hfa384x_t *hw, hfa384x_metacmd_t *cmd);

static int hfa384x_dl_docmd_wait( hfa384x_t *hw, hfa384x_metacmd_t *cmd);

static UINT16 
hfa384x_mkcrc16(UINT8 *p, int len);

int hfa384x_copy_to_bap4(hfa384x_t *hw, UINT16 bap, UINT16 id, UINT16 offset,
			 void *buf, UINT len, void* buf2, UINT len2,
			 void *buf3, UINT len3, void* buf4, UINT len4);

/*================================================================*/
/* Function Definitions */

UINT16
txfid_queue_empty(hfa384x_t *hw)
{
	return (hw->txfid_head == hw->txfid_tail) ? 1 : 0;
}

UINT16
txfid_queue_remove(hfa384x_t *hw)
{
	UINT16 result= 0;

	if (txfid_queue_empty(hw)) {
		WLAN_LOG_DEBUG(3,"queue empty.\n");
	} else {
		result = hw->txfid_queue[hw->txfid_head];
		hw->txfid_head = (hw->txfid_head + 1) % hw->txfid_N;
	}

	return (UINT16)result;
}

INT16
txfid_queue_add(hfa384x_t *hw, UINT16 val)
{
	INT16 result = 0;

	if (hw->txfid_head == ((hw->txfid_tail + 1) % hw->txfid_N)) {
		result = -1;
		WLAN_LOG_DEBUG(3,"queue full.\n");
	} else {
		hw->txfid_queue[hw->txfid_tail] = val;
		result = hw->txfid_tail;
		hw->txfid_tail = (hw->txfid_tail + 1) % hw->txfid_N;
	}

	return result;
}

/*----------------------------------------------------------------
* hfa384x_create
*
* Initializes the hfa384x_t data structure for use.  Note this
* does _not_ intialize the actual hardware, just the data structures
* we use to keep track of its state.
*
* Arguments:
*	hw		device structure
*	irq		device irq number
*	iobase		[pcmcia] i/o base address for register access
*			[pci] zero
*			[plx] i/o base address for register access
*	membase		[pcmcia] pcmcia_cs "link" pointer
*			[pci] memory base address for register access
*			[plx] memory base address for card attribute memory
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
void hfa384x_create( hfa384x_t *hw, UINT irq, UINT32 iobase, UINT8 *membase)
{
	DBFENTER;
	memset(hw, 0, sizeof(hfa384x_t));
	hw->irq = irq;
	hw->iobase = iobase;
	hw->membase = membase;
	spin_lock_init(&(hw->cmdlock));

	/* BAP setup */
 	spin_lock_init(&(hw->baplock));
#ifdef DECLARE_TASKLET
	tasklet_init(&hw->bap_tasklet,
		     hfa384x_bap_tasklet,
		     (unsigned long) hw);
#else
	INIT_WORK(&hw->bap_tasklet, hfa384x_bap_tasklet, hw);
#endif

	init_waitqueue_head(&hw->cmdq);
	sema_init(&hw->infofid_sem, 1);

        hw->txfid_head = 0;
        hw->txfid_tail = 0;
        hw->txfid_N = HFA384x_DRVR_FIDSTACKLEN_MAX;
        memset(hw->txfid_queue, 0, sizeof(hw->txfid_queue));

	hw->isram16 = 1;

	/* Init the auth queue head */
	skb_queue_head_init(&hw->authq);

	INIT_WORK(&hw->link_bh, prism2sta_processing_defer, hw);

	hw->link_status = HFA384x_LINK_NOTCONNECTED;
	hw->state = HFA384x_STATE_INIT;

	DBFEXIT;
}


/*----------------------------------------------------------------
* hfa384x_destroy
*
* Partner to hfa384x_create().  This function cleans up the hw
* structure so that it can be freed by the caller using a simple
* kfree.  Currently, this function is just a placeholder.  If, at some
* point in the future, an hw in the 'shutdown' state requires a 'deep'
* kfree, this is where it should be done.  Note that if this function
* is called on a _running_ hw structure, the drvr_stop() function is
* called.
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	nothing, this function is not allowed to fail.
*
* Side effects:
*
* Call context:
*	process 
----------------------------------------------------------------*/
void
hfa384x_destroy( hfa384x_t *hw)
{
	struct sk_buff *skb;

	DBFENTER;

	if ( hw->state == HFA384x_STATE_RUNNING ) {
		hfa384x_drvr_stop(hw);
	}
	hw->state = HFA384x_STATE_PREINIT;		

	if (hw->scanresults) {
		kfree(hw->scanresults);
		hw->scanresults = NULL;
	}

        /* Now to clean out the auth queue */
        while ( (skb = skb_dequeue(&hw->authq)) ) {
                dev_kfree_skb(skb);
        }

	DBFEXIT;
}


/*----------------------------------------------------------------
* hfa384x_drvr_getconfig
*
* 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
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*	-ENODATA 	length mismatch between argument and retrieved
*			record.
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_getconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len)
{
	int 		result = 0;
	DBFENTER;

	result = hfa384x_cmd_access( hw, 0, rid, buf, len);

	DBFEXIT;
	return result;
}

/*----------------------------------------------------------------
* hfa384x_drvr_getconfig16
*
* Performs the sequence necessary to read a 16 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 bit buffer to receive value (in host order)
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_getconfig16(hfa384x_t *hw, UINT16 rid, void *val)
{
	int		result = 0;
	DBFENTER;
	result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT16));
	if ( result == 0 ) {
		*((UINT16*)val) = hfa384x2host_16(*((UINT16*)val));
	}

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_getconfig32
*
* Performs the sequence necessary to read a 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 32 bit buffer to receive value (in host order)
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val)
{
	int		result = 0;
	DBFENTER;
	result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT32));
	if ( result == 0 ) {
		*((UINT32*)val) = hfa384x2host_32(*((UINT32*)val));
	}

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_setconfig
*
* 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
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_setconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len)
{
	int		result = 0;
	DBFENTER;

	result = hfa384x_cmd_access( hw, 1, rid, buf, len);

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_readpda
*
* Performs the sequence to read the PDA space.  Note there is no
* drvr_writepda() function.  Writing a PDA is
* generally implemented by a calling component via calls to 
* cmd_download and writing to the flash download buffer via the 
* aux regs.
*
* Arguments:
*	hw		device structure
*	buf		buffer to store PDA in
*	len		buffer length
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*	-ETIMEOUT	timout waiting for the cmd regs to become
*			available, or waiting for the control reg
*			to indicate the Aux port is enabled.
*	-ENODATA	the buffer does NOT contain a valid PDA.
*			Either the card PDA is bad, or the auxdata
*			reads are giving us garbage.

*
* Side effects:
*
* Call context:
*	process thread or non-card interrupt.
----------------------------------------------------------------*/
int hfa384x_drvr_readpda(hfa384x_t *hw, void *buf, UINT len)
{
	int		result = 0;
	UINT16		*pda = buf;
	int		pdaok = 0;
	int		morepdrs = 1;
	int		currpdr = 0;	/* word offset of the current pdr */
	int		i;
	UINT16		pdrlen;		/* pdr length in bytes, host order */
	UINT16		pdrcode;	/* pdr code, host order */
	UINT16		crc;
	UINT16		pdacrc;
	struct pdaloc {
		UINT32	cardaddr;
		UINT16	auxctl;
	} pdaloc[] =
	{
		{ HFA3842_PDA_BASE,		HFA384x_AUX_CTL_NV},
		{ HFA3842_PDA_BASE,		HFA384x_AUX_CTL_EXTDS},
		{ HFA3841_PDA_BASE,		HFA384x_AUX_CTL_NV}, 
		{ HFA3841_PDA_BASE,		HFA384x_AUX_CTL_EXTDS}, 
		{ HFA3841_PDA_BOGUS_BASE,	HFA384x_AUX_CTL_NV}
	};

	DBFENTER;
	/* Check for aux available */
	result = hfa384x_cmd_aux_enable(hw, 0);
	if ( result ) {
		WLAN_LOG_DEBUG(1,"aux_enable() failed. result=%d\n", result);
		goto failed;
	}

	/* Read the pda from each known address.  */
	for ( i = 0; i < (sizeof(pdaloc)/sizeof(pdaloc[0])); i++) {
		WLAN_LOG_DEBUG( 3, "Checking PDA@(0x%08lx,%s)\n",
			pdaloc[i].cardaddr,
			pdaloc[i].auxctl == HFA384x_AUX_CTL_NV ?
			"CTL_NV" : "CTL_EXTDS");

		/* Copy bufsize bytes from our current pdaloc */
		hfa384x_copy_from_aux(hw, 
			pdaloc[i].cardaddr, 
			pdaloc[i].auxctl, 
			buf, 
			len);

		/* Test for garbage */
		/* Traverse the PDR list Looking for PDA-END */
		pdaok = 1;	/* intially assume good */
		morepdrs = 1;
		currpdr = 0;
		while ( pdaok && morepdrs ) {
			pdrlen = hfa384x2host_16(pda[currpdr]) * 2;
			pdrcode = hfa384x2host_16(pda[currpdr+1]);
				
			/* Test for completion at END record */
			if ( pdrcode == HFA384x_PDR_END_OF_PDA ) {
				if ( pdrlen == 4 ) { 
					morepdrs = 0;
					/* Calculate CRC-16 and compare to PDA
					 * value.  Note the addition of 2 words
					 * for ENDREC.len and ENDREC.code
					 * fields.
					 */
					crc = hfa384x_mkcrc16( (UINT8*)pda, 
						(currpdr + 2) * sizeof(UINT16));
					pdacrc =hfa384x2host_16(pda[currpdr+2]);
					if ( crc != pdacrc ) {
						WLAN_LOG_DEBUG(3,
						"PDA crc failed:"
						"calc_crc=0x%04x,"
						"pdr_crc=0x%04x.\n",
						crc, pdacrc);
						pdaok = 0;
					}
				} else {
					WLAN_LOG_DEBUG(3, 
					"END record detected w/ "
					"len(%d) != 2, assuming bad PDA\n",
					pdrlen);
					pdaok = 0;

				}
				break;
			}
	
			/* Test the record length */
			if ( pdrlen > HFA384x_PDR_LEN_MAX || pdrlen == 0) {
				WLAN_LOG_DEBUG(3,
					"pdrlen for address #%d "
					"at %#lx:%#x:%d\n",
					i, pdaloc[i].cardaddr, 
					pdaloc[i].auxctl, pdrlen);
				WLAN_LOG_DEBUG(3,"pdrlen invalid=%d\n", 
					pdrlen);
				pdaok = 0;
				break;
			}

			/* Move to the next pdr */
			if ( morepdrs ) {
				/* note the access to pda[], we need words */
				currpdr += hfa384x2host_16(pda[currpdr]) + 1;
				if (currpdr*sizeof(UINT16) > len) {
					WLAN_LOG_DEBUG(3,
					"Didn't find PDA_END in buffer, "
					"trying next location.\n");
					pdaok = 0;
					break;
				}
			}
		}	
		if ( pdaok ) {
			WLAN_LOG_INFO(
				"PDA Read from 0x%08lx in %s space.\n",
				pdaloc[i].cardaddr, 
				pdaloc[i].auxctl == 0 ? "EXTDS" :
				pdaloc[i].auxctl == 1 ? "NV" :
				pdaloc[i].auxctl == 2 ? "PHY" :
				pdaloc[i].auxctl == 3 ? "ICSRAM" : 
				"<bogus auxctl>");
			break;
		} 
	}
	result = pdaok ? 0 : -ENODATA;

	if ( result ) {
		WLAN_LOG_DEBUG(3,"Failure: pda is not okay\n");
	}

	hfa384x_cmd_aux_disable(hw);
failed:
	DBFEXIT;
	return result;
}



/*----------------------------------------------------------------
* mkpda_crc
*
* Calculates the CRC16 for the given PDA and inserts the value
* into the end record.
*
* Arguments:
*	pda	ptr to the PDA data structure.
*
* Returns: 
*	0	- success 
*	~0	- failure (probably an errno)
----------------------------------------------------------------*/
static UINT16 
hfa384x_mkcrc16(UINT8 *p, int len)
{
	UINT16	crc = 0;
	UINT8	*lim = p + len;

	while (p < lim) {	
		crc = (crc >> 8 ) ^ crc16tab[(crc & 0xff) ^ *p++];
	}

	return crc;
}


/*----------------------------------------------------------------
* hfa384x_drvr_ramdl_enable
*
* Begins the ram download state.  Checks to see that we're not
* already in a download state and that a port isn't enabled.
* Sets the download state and calls cmd_download with the 
* ENABLE_VOLATILE subcommand and the exeaddr argument.
*
* Arguments:
*	hw		device structure
*	exeaddr		the card execution address that will be 
*                       jumped to when ramdl_disable() is called
*			(host order).
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_ramdl_enable(hfa384x_t *hw, UINT32 exeaddr)
{
	int		result = 0;
	UINT16		lowaddr;
	UINT16		hiaddr;
	int		i;
	DBFENTER;
	/* Check that a port isn't active */
	for ( i = 0; i < HFA384x_PORTID_MAX; i++) {
		if ( hw->port_enabled[i] ) {
			WLAN_LOG_DEBUG(1,"Can't download with a port enabled.\n");
			result = -EINVAL; 
			goto done;
		}
	}

	/* Check that we're not already in a download state */
	if ( hw->dlstate != HFA384x_DLSTATE_DISABLED ) {
		WLAN_LOG_DEBUG(1,"Download state not disabled.\n");
		result = -EINVAL;
		goto done;
	}

	/* Are we supposed to go into genesis mode? */
	if (exeaddr == 0x3f0000) {
		UINT16 initseq[2] = { 0xe100, 0xffa1 };
		UINT16 readbuf[2];
		UINT8 hcr = 0x0f; /* Default to x16 SRAM */
		hw->isram16 = 1;

		WLAN_LOG_DEBUG(1, "Dropping into Genesis mode\n");

		/* Issue card reset and enable aux port */
		hfa384x_corereset(hw, prism2_reset_holdtime,
				  prism2_reset_settletime, 0);
		hfa384x_cmd_aux_enable(hw, 1);

		/* Genesis set */
		hfa384x_copy_to_aux(hw, 0x7E0038, HFA384x_AUX_CTL_EXTDS, 
				    initseq, sizeof(initseq));

		hfa384x_corereset(hw, prism2_reset_holdtime,
				  prism2_reset_settletime, hcr);

		/* Validate memory config */
		hfa384x_copy_to_aux(hw, 0x7E0038, HFA384x_AUX_CTL_EXTDS, 
				    initseq, sizeof(initseq));
		hfa384x_copy_from_aux(hw, 0x7E0038, HFA384x_AUX_CTL_EXTDS, 
				    readbuf, sizeof(initseq));
		WLAN_HEX_DUMP(3, "readback", readbuf, sizeof(readbuf));

		if (memcmp(initseq, readbuf, sizeof(readbuf))) {
			hcr = 0x1f; /* x8 SRAM */
			hw->isram16 = 0;

			hfa384x_copy_to_aux(hw, 0x7E0038, HFA384x_AUX_CTL_EXTDS, 
					    initseq, sizeof(initseq));
			hfa384x_corereset(hw, prism2_reset_holdtime,
					  prism2_reset_settletime, hcr);

			hfa384x_copy_to_aux(hw, 0x7E0038, HFA384x_AUX_CTL_EXTDS, 
					    initseq, sizeof(initseq));
			hfa384x_copy_from_aux(hw, 0x7E0038, HFA384x_AUX_CTL_EXTDS, 
					    readbuf, sizeof(initseq));
			WLAN_HEX_DUMP(2, "readback", readbuf, sizeof(readbuf));

			if (memcmp(initseq, readbuf, sizeof(readbuf))) {
				WLAN_LOG_ERROR("Genesis mode failed\n");
				result = -1;
				goto done;
			}
		}

		/* Now we're in genesis mode */
		hw->dlstate = HFA384x_DLSTATE_GENESIS;
		goto done;
	}

	/* Retrieve the buffer loc&size and timeout */
	if ( (result = hfa384x_drvr_getconfig(hw, HFA384x_RID_DOWNLOADBUFFER, 
				&(hw->bufinfo), sizeof(hw->bufinfo))) ) {
		goto done;
	}
	hw->bufinfo.page = hfa384x2host_16(hw->bufinfo.page);
	hw->bufinfo.offset = hfa384x2host_16(hw->bufinfo.offset);
	hw->bufinfo.len = hfa384x2host_16(hw->bufinfo.len);
	if ( (result = hfa384x_drvr_getconfig16(hw, HFA384x_RID_MAXLOADTIME, 
				&(hw->dltimeout))) ) {
		goto done;
	}
	hw->dltimeout = hfa384x2host_16(hw->dltimeout);

	/* Enable the aux port */
	if ( (result = hfa384x_cmd_aux_enable(hw, 0)) ) {
		WLAN_LOG_DEBUG(1,"Aux enable failed, result=%d.\n", result);
		goto done;
	}

	/* Call the download(1,addr) function */
	lowaddr = HFA384x_ADDR_CMD_MKOFF(exeaddr);
	hiaddr =  HFA384x_ADDR_CMD_MKPAGE(exeaddr);

	result = hfa384x_cmd_download(hw, HFA384x_PROGMODE_RAM, 
			lowaddr, hiaddr, 0);
	if ( result == 0) {
		/* Set the download state */
		hw->dlstate = HFA384x_DLSTATE_RAMENABLED;
	} else {
		WLAN_LOG_DEBUG(1,"cmd_download(0x%04x, 0x%04x) failed, result=%d.\n",
				lowaddr,hiaddr, result);
		/* Disable  the aux port */
		hfa384x_cmd_aux_disable(hw);
	}

 done:
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_ramdl_disable
*
* Ends the ram download state.
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_ramdl_disable(hfa384x_t *hw)
{
	DBFENTER;
	/* Check that we're already in the download state */
	if ( ( hw->dlstate != HFA384x_DLSTATE_RAMENABLED ) && 
	     ( hw->dlstate != HFA384x_DLSTATE_GENESIS ) ) {
		return -EINVAL;
	}

	if (hw->dlstate == HFA384x_DLSTATE_GENESIS) {
		hfa384x_corereset(hw, prism2_reset_holdtime, 
				  prism2_reset_settletime, 
				  hw->isram16 ? 0x07: 0x17);
		goto done;
	}

	/* Disable the aux port */
	hfa384x_cmd_download(hw, HFA384x_PROGMODE_DISABLE, 0, 0 , 0);

 done:
	hw->dlstate = HFA384x_DLSTATE_DISABLED;
	hfa384x_cmd_aux_disable(hw);

	DBFEXIT;
	return 0;
}


/*----------------------------------------------------------------
* hfa384x_drvr_ramdl_write
*
* Performs a RAM download of a chunk of data. First checks to see
* that we're in the RAM download state, then uses the aux functions
* to 1) copy the data, 2) readback and compare.  The download
* state is unaffected.  When all data has been written using
* this function, call drvr_ramdl_disable() to end the download state
* and restart the MAC.
*
* Arguments:
*	hw		device structure
*	daddr		Card address to write to. (host order)
*	buf		Ptr to data to write.
*	len		Length of data (host order).
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_ramdl_write(hfa384x_t *hw, UINT32 daddr, void* buf, UINT32 len)
{
	int		result = 0;
	UINT8		*verbuf;
	DBFENTER;
	/* Check that we're in the ram download state */
	if ( ( hw->dlstate != HFA384x_DLSTATE_RAMENABLED ) && 
	     ( hw->dlstate != HFA384x_DLSTATE_GENESIS ) ) {
		return -EINVAL;
	}

	WLAN_LOG_INFO("Writing %ld bytes to ram @0x%06lx\n", len, daddr);
#if 0
WLAN_HEX_DUMP(1, "dldata", buf, len);
#endif
	/* Copy the data via the aux port */
	hfa384x_copy_to_aux(hw, daddr, HFA384x_AUX_CTL_EXTDS, buf, len);

	/* Create a buffer for the verify */
	verbuf = kmalloc(len, GFP_KERNEL);
	if (verbuf == NULL ) return 1;

	/* Read back and compare */
	hfa384x_copy_from_aux(hw, daddr, HFA384x_AUX_CTL_EXTDS, verbuf, len);

	if ( memcmp(buf, verbuf, len) ) {
		WLAN_LOG_DEBUG(1,"ramdl verify failed!\n");
		result = -EINVAL;
	}

	kfree_s(verbuf, len);
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_flashdl_enable
*
* Begins the flash download state.  Checks to see that we're not
* already in a download state and that a port isn't enabled.
* Sets the download state and retrieves the flash download
* buffer location, buffer size, and timeout length.
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_flashdl_enable(hfa384x_t *hw)
{
	int		result = 0;
	int		i;

	DBFENTER;
	/* Check that a port isn't active */
	for ( i = 0; i < HFA384x_PORTID_MAX; i++) {
		if ( hw->port_enabled[i] ) {
			WLAN_LOG_DEBUG(1,"called when port enabled.\n");
			return -EINVAL; 
		}
	}

	/* Check that we're not already in a download state */
	if ( hw->dlstate != HFA384x_DLSTATE_DISABLED ) {
		return -EINVAL;
	}

	/* Retrieve the buffer loc&size and timeout */
	if ( (result = hfa384x_drvr_getconfig(hw, HFA384x_RID_DOWNLOADBUFFER, 
				&(hw->bufinfo), sizeof(hw->bufinfo))) ) {
		return result;
	}
	hw->bufinfo.page = hfa384x2host_16(hw->bufinfo.page);
	hw->bufinfo.offset = hfa384x2host_16(hw->bufinfo.offset);
	hw->bufinfo.len = hfa384x2host_16(hw->bufinfo.len);
	if ( (result = hfa384x_drvr_getconfig16(hw, HFA384x_RID_MAXLOADTIME, 
				&(hw->dltimeout))) ) {
		return result;
	}
	hw->dltimeout = hfa384x2host_16(hw->dltimeout);

	/* Enable the aux port */
	if ( (result = hfa384x_cmd_aux_enable(hw, 0)) ) {
		return result;
	}

	hw->dlstate = HFA384x_DLSTATE_FLASHENABLED;
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_flashdl_disable
*
* Ends the flash download state.  Note that this will cause the MAC
* firmware to restart.
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_flashdl_disable(hfa384x_t *hw)
{
	DBFENTER;
	/* Check that we're already in the download state */
	if ( hw->dlstate != HFA384x_DLSTATE_FLASHENABLED ) {
		return -EINVAL;
	}

	/* There isn't much we can do at this point, so I don't */
	/*  bother  w/ the return value */
	hfa384x_cmd_download(hw, HFA384x_PROGMODE_DISABLE, 0, 0 , 0);
	hw->dlstate = HFA384x_DLSTATE_DISABLED;

	/* Disable the aux port */
	hfa384x_cmd_aux_disable(hw);

	DBFEXIT;
	return 0;
}


/*----------------------------------------------------------------
* hfa384x_drvr_flashdl_write
*
* Performs a FLASH download of a chunk of data. First checks to see
* that we're in the FLASH download state, then sets the download
* mode, uses the aux functions to 1) copy the data to the flash
* buffer, 2) sets the download 'write flash' mode, 3) readback and 
* compare.  Lather rinse, repeat as many times an necessary to get
* all the given data into flash.  
* When all data has been written using this function (possibly 
* repeatedly), call drvr_flashdl_disable() to end the download state
* and restart the MAC.
*
* Arguments:
*	hw		device structure
*	daddr		Card address to write to. (host order)
*	buf		Ptr to data to write.
*	len		Length of data (host order).
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_flashdl_write(hfa384x_t *hw, UINT32 daddr, void* buf, UINT32 len)
{
	int		result = 0;
	UINT8		*verbuf;
	UINT32		dlbufaddr;
	UINT32		currlen;
	UINT32		currdaddr;
	UINT16		destlo;
	UINT16		desthi;
	int		nwrites;
	int		i;

	DBFENTER;
	/* Check that we're in the flash download state */
	if ( hw->dlstate != HFA384x_DLSTATE_FLASHENABLED ) {
		return -EINVAL;
	}

	WLAN_LOG_INFO("Download %ld bytes to flash @0x%06lx\n", len, daddr);

	/* Need a flat address for arithmetic */
	dlbufaddr = HFA384x_ADDR_AUX_MKFLAT(
			hw->bufinfo.page,
			hw->bufinfo.offset);
	verbuf = kmalloc(hw->bufinfo.len, GFP_KERNEL);

#if 0
WLAN_LOG_WARNING("dlbuf@0x%06lx len=%d to=%d\n", dlbufaddr, hw->bufinfo.len, hw->dltimeout);
#endif
	/* Figure out how many times to to the flash prog */
	nwrites = len / hw->bufinfo.len;
	nwrites += (len % hw->bufinfo.len) ? 1 : 0;

	if ( verbuf == NULL ) {
		WLAN_LOG_ERROR("Failed to allocate flash verify buffer\n");
		return 1;
	}
	/* For each */
	for ( i = 0; i < nwrites; i++) {
		/* Get the dest address and len */
		currlen = (len - (hw->bufinfo.len * i)) > hw->bufinfo.len ?
				hw->bufinfo.len : 
				(len - (hw->bufinfo.len * i));
		currdaddr = daddr + (hw->bufinfo.len * i);
		destlo = HFA384x_ADDR_CMD_MKOFF(currdaddr);
		desthi = HFA384x_ADDR_CMD_MKPAGE(currdaddr);
		WLAN_LOG_INFO("Writing %ld bytes to flash @0x%06lx\n", currlen, currdaddr);
#if 0
WLAN_HEX_DUMP(1, "dldata", buf+(hw->bufinfo.len*i), currlen);
#endif
		/* Set the download mode */
		result = hfa384x_cmd_download(hw, HFA384x_PROGMODE_NV, 
				destlo, desthi, currlen);
		if ( result ) {
			WLAN_LOG_ERROR("download(NV,lo=%x,hi=%x,len=%lx) "
				"cmd failed, result=%d. Aborting d/l\n",
				destlo, desthi, currlen, result);
			goto exit_proc;
		}
		/* copy the data to the flash buffer */
		hfa384x_copy_to_aux(hw, dlbufaddr, HFA384x_AUX_CTL_EXTDS,
					buf+(hw->bufinfo.len*i), currlen);
		/* set the download 'write flash' mode */
		result = hfa384x_cmd_download(hw, HFA384x_PROGMODE_NVWRITE, 0,0,0);
		if ( result ) {
			WLAN_LOG_ERROR(
				"download(NVWRITE,lo=%x,hi=%x,len=%lx) "
				"cmd failed, result=%d. Aborting d/l\n",
				destlo, desthi, currlen, result);
			goto exit_proc;
		}
		/* readback and compare, if fail...bail */
		hfa384x_copy_from_aux(hw, 
				currdaddr, HFA384x_AUX_CTL_NV, 
				verbuf, currlen);

		if ( memcmp(buf+(hw->bufinfo.len*i), verbuf, currlen) ) {
			return -EINVAL;
		}
	}

exit_proc:
         /* DOH! This kfree's for you Mark :-) My forehead hurts... */
         kfree(verbuf);

	/* Leave the firmware in the 'post-prog' mode.  flashdl_disable will */
	/*  actually disable programming mode.  Remember, that will cause the */
	/*  the firmware to effectively reset itself. */
	
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_initialize
*
* Issues the initialize command and sets the hw->state based
* on the result.
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_initialize(hfa384x_t *hw)
{
	int result = 0;
	int i;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	/* we don't want to be interrupted during the reset */
	hfa384x_setreg(hw, 0, HFA384x_INTEN);
	hfa384x_setreg(hw, 0xffff, HFA384x_EVACK);

	cmd.cmd = HFA384x_CMDCODE_INIT;
	cmd.parm0 = 0;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);

	if ( result == 0 ) {
		for ( i = 0; i < HFA384x_NUMPORTS_MAX; i++) {
			hw->port_enabled[i] = 0;
		}
	}

        hw->link_status = HFA384x_LINK_NOTCONNECTED;

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_commtallies
*
* Send a commtallies inquiry to the MAC.  Note that this is an async
* call that will result in an info frame arriving sometime later.
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	zero		success.
*
* Side effects:
*
* Call context:
*	process
----------------------------------------------------------------*/
int hfa384x_drvr_commtallies( hfa384x_t *hw )
{
	hfa384x_metacmd_t cmd;
	int result;

	DBFENTER;

	cmd.cmd = HFA384x_CMDCODE_INQ;
	cmd.parm0 = HFA384x_IT_COMMTALLIES;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);
	
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_enable
*
* Issues the enable command to enable communications on one of 
* the MACs 'ports'.  Only macport 0 is valid  for stations.
* APs may also enable macports 1-6.  Only ports that are currently
* disabled may be enabled.
*
* Arguments:
*	hw		device structure
*	macport		MAC port number
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_enable(hfa384x_t *hw, UINT16 macport)
{
	int	result = 0;

	DBFENTER;
	if ((!hw->isap && macport != 0) || 
	    (hw->isap && !(macport <= HFA384x_PORTID_MAX)) ||
	    (hw->port_enabled[macport]) ){
		result = -EINVAL;
	} else {
		result = hfa384x_cmd_enable(hw, macport);
		if ( result == 0 ) {
			hw->port_enabled[macport] = 1;
		}
	}
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_enable
*
* Issues the the enable command to enable communications on one of the
* MACs 'ports'.
*
* Arguments:
*	hw		device structure
*	macport		MAC port number
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_enable(hfa384x_t *hw, UINT16 macport)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ENABLE) |
		  HFA384x_CMD_MACPORT_SET(macport);
	cmd.parm0 = 0;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_disable
*
* Issues the disable command to stop communications on one of 
* the MACs 'ports'.  Only macport 0 is valid  for stations.
* APs may also disable macports 1-6.  Only ports that have been
* previously enabled may be disabled.
*
* Arguments:
*	hw		device structure
*	macport		MAC port number (host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_disable(hfa384x_t *hw, UINT16 macport)
{
	int	result = 0;

	DBFENTER;
	if ((!hw->isap && macport != 0) || 
	    (hw->isap && !(macport <= HFA384x_PORTID_MAX)) ||
	    !(hw->port_enabled[macport]) ){
		result = -EINVAL;
	} else {
		result = hfa384x_cmd_disable(hw, macport);
		if ( result == 0 ) {
			hw->port_enabled[macport] = 0;
		}
	}
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_disable
*
* Issues the command to disable a port.
*
* Arguments:
*	hw		device structure
*	macport		MAC port number (host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_disable(hfa384x_t *hw, UINT16 macport)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_DISABLE) |
		  HFA384x_CMD_MACPORT_SET(macport);
	cmd.parm0 = 0;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_diagnose
*
* Issues the diagnose command to test the: register interface,
* MAC controller (including loopback), External RAM, Non-volatile
* memory integrity, and synthesizers.  Following execution of this
* command, MAC/firmware are in the 'initial state'.  Therefore,
* the Initialize command should be issued after successful
* completion of this command.  This function may only be called
* when the MAC is in the 'communication disabled' state.
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
#define DIAG_PATTERNA ((UINT16)0xaaaa)
#define DIAG_PATTERNB ((UINT16)~0xaaaa)

int hfa384x_cmd_diagnose(hfa384x_t *hw)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_DIAG);
	cmd.parm0 = DIAG_PATTERNA;
	cmd.parm1 = DIAG_PATTERNB;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_allocate
*
* Issues the allocate command instructing the firmware to allocate
* a 'frame structure buffer' in MAC controller RAM.  This command
* does not provide the result, it only initiates one of the f/w's
* asynchronous processes to construct the buffer.  When the 
* allocation is complete, it will be indicated via the Alloc
* bit in the EvStat register and the FID identifying the allocated
* space will be available from the AllocFID register.  Some care
* should be taken when waiting for the Alloc event.  If a Tx or 
* Notify command w/ Reclaim has been previously executed, it's
* possible the first Alloc event after execution of this command
* will be for the reclaimed buffer and not the one you asked for.
* This case must be handled in the Alloc event handler.
*
* Arguments:
*	hw		device structure
*	len		allocation length, must be an even value
*			in the range [4-2400]. (host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_allocate(hfa384x_t *hw, UINT16 len)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	if ( (len % 2) || 
	     len < HFA384x_CMD_ALLOC_LEN_MIN || 
	     len > HFA384x_CMD_ALLOC_LEN_MAX ) {
		result = -EINVAL;
	} else {
		cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ALLOC);
		cmd.parm0 = len;
		cmd.parm1 = 0;
		cmd.parm2 = 0;

		spin_lock_bh(&hw->cmdlock);
		result = hfa384x_docmd_wait(hw, &cmd);
		spin_unlock_bh(&hw->cmdlock);
	}
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_transmit
*
* Instructs the firmware to transmit a frame previously copied
* to a given buffer.  This function returns immediately, the Tx
* results are available via the Tx or TxExc events (if the frame
* control bits are set).  The reclaim argument specifies if the 
* FID passed will be used by the f/w tx process or returned for
* use w/ another transmit command.  If reclaim is set, expect an 
* Alloc event signalling the availibility of the FID for reuse.
*
* NOTE: hw->cmdlock MUST BE HELD before calling this function!
*
* Arguments:
*	hw		device structure
*	reclaim		[0|1] indicates whether the given FID will
*			be handed back (via Alloc event) for reuse.
*			(host order)
*	qos		[0-3] Value to put in the QoS field of the 
*			tx command, identifies a queue to place the 
*			outgoing frame in.
*			(host order)
*	fid		FID of buffer containing the frame that was
*			previously copied to MAC memory via the bap.
*			(host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*	hw->resp0 will contain the FID being used by async tx
*	process.  If reclaim==0, resp0 will be the same as the fid
*	argument.  If reclaim==1, resp0 will be the different and
*	is the value to watch for in the Tx|TxExc to indicate completion
*	of the frame passed in fid.
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_transmit(hfa384x_t *hw, UINT16 reclaim, UINT16 qos, UINT16 fid)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;
	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_TX) |
		HFA384x_CMD_RECL_SET(reclaim) |
		HFA384x_CMD_QOS_SET(qos);
	cmd.parm0 = fid;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	result = hfa384x_docmd_wait(hw, &cmd);
	
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_clearpersist
*
* Instructs the firmware to clear the persistence bit in a given
* FID.  This has the effect of telling the firmware to drop the
* persistent frame.  The FID must be one that was previously used
* to transmit a PRST frame.
*
* Arguments:
*	hw		device structure
*	fid		FID of the persistent frame (host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_clearpersist(hfa384x_t *hw, UINT16 fid)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_CLRPRST);
	cmd.parm0 = fid;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);
	
	DBFEXIT;
	return result;
}

/*----------------------------------------------------------------
* hfa384x_cmd_notify
*
* Sends an info frame to the firmware to alter the behavior
* of the f/w asynch processes.  Can only be called when the MAC
* is in the enabled state.
*
* Arguments:
*	hw		device structure
*	reclaim		[0|1] indicates whether the given FID will
*			be handed back (via Alloc event) for reuse.
*			(host order)
*	fid		FID of buffer containing the frame that was
*			previously copied to MAC memory via the bap.
*			(host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*	hw->resp0 will contain the FID being used by async notify
*	process.  If reclaim==0, resp0 will be the same as the fid
*	argument.  If reclaim==1, resp0 will be the different.
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_notify(hfa384x_t *hw, UINT16 reclaim, UINT16 fid,
		       void *buf, UINT16 len)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;
	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_NOTIFY) |
		HFA384x_CMD_RECL_SET(reclaim);
	cmd.parm0 = fid;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);

        /* Copy the record to FID */
        result = hfa384x_copy_to_bap(hw, HFA384x_BAP_PROC, hw->infofid, 0, buf, len);
        if ( result ) {
                WLAN_LOG_DEBUG(1, 
			"copy_to_bap(%04x, 0, %d) failed, result=0x%x\n",
                        hw->infofid, len, result);
		result = -EIO;
                goto failed;
        }

	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);
	
 failed:
	spin_unlock_bh(&hw->cmdlock);

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_inquiry
*
* Requests an info frame from the firmware.  The info frame will
* be delivered asynchronously via the Info event.
*
* Arguments:
*	hw		device structure
*	fid		FID of the info frame requested. (host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_inquiry(hfa384x_t *hw, UINT16 fid)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_INQ);
	cmd.parm0 = fid;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);
	
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_access
*
* Requests that a given record be copied to/from the record 
* buffer.  If we're writing from the record buffer, the contents
* must previously have been written to the record buffer via the 
* bap.  If we're reading into the record buffer, the record can
* be read out of the record buffer after this call.
*
* 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)
*	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
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_access(hfa384x_t *hw, UINT16 write, UINT16 rid, 
		       void* buf, UINT16 len)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;
	hfa384x_rec_t	rec;

	DBFENTER;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0))
	/* This should NOT be called in interrupt context! */
	if (in_irq()) {
		WLAN_LOG_ERROR("Krap, in Interrupt context!");
#ifdef WLAN_INCLUDE_DEBUG
		BUG();
#endif
	}
#endif
	spin_lock_bh(&hw->cmdlock);

	if (write) {
		rec.rid = host2hfa384x_16(rid);
		rec.reclen = host2hfa384x_16((len/2) + 1); /* note conversion to words, +1 for rid field */
		/* write the record */
		result = hfa384x_copy_to_bap4( hw, HFA384x_BAP_PROC, rid, 0, 
					       &rec, sizeof(rec),
					       buf, len, 
					       NULL, 0, NULL, 0);
		if ( result ) {
			WLAN_LOG_DEBUG(3,"Failure writing record header+data\n");
			goto fail;
		}

	}

	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ACCESS) |
		HFA384x_CMD_WRITE_SET(write);
	cmd.parm0 = rid;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	result = hfa384x_docmd_wait(hw, &cmd);
	if ( result ) {
		WLAN_LOG_ERROR("Call to hfa384x_docmd_wait failed (%d %d)\n",
				result, cmd.resp0);
		goto fail;
	}

	if (!write) {
		result = hfa384x_copy_from_bap( hw, HFA384x_BAP_PROC, rid, 0, &rec, sizeof(rec));
		if ( result ) {
			WLAN_LOG_DEBUG(3,"Call to hfa384x_copy_from_bap failed\n");
			goto fail;
		}
		
		/* Validate the record length */
		if ( ((hfa384x2host_16(rec.reclen)-1)*2) != len ) {  /* note body len calculation in bytes */
			WLAN_LOG_DEBUG(1, "RID len mismatch, rid=0x%04x hlen=%d fwlen=%d\n",
					rid, len, (hfa384x2host_16(rec.reclen)-1)*2);
			result = -ENODATA;
			goto fail;
		}

		result = hfa384x_copy_from_bap( hw, HFA384x_BAP_PROC, rid, sizeof(rec), buf, len);

	}

 fail:	
	spin_unlock_bh(&hw->cmdlock);
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_monitor
*
* Enables the 'monitor mode' of the MAC.  Here's the description of
* monitor mode that I've received thus far:
*
*  "The "monitor mode" of operation is that the MAC passes all 
*  frames for which the PLCP checks are correct. All received 
*  MPDUs are passed to the host with MAC Port = 7, with a  
*  receive status of good, FCS error, or undecryptable. Passing 
*  certain MPDUs is a violation of the 802.11 standard, but useful 
*  for a debugging tool."  Normal communication is not possible
*  while monitor mode is enabled.
*
* Arguments:
*	hw		device structure
*	enable		a code (0x0b|0x0f) that enables/disables
*			monitor mode. (host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_monitor(hfa384x_t *hw, UINT16 enable)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_MONITOR) |
		HFA384x_CMD_AINFO_SET(enable);
	cmd.parm0 = 0;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);
	
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_download
*
* Sets the controls for the MAC controller code/data download
* process.  The arguments set the mode and address associated 
* with a download.  Note that the aux registers should be enabled
* prior to setting one of the download enable modes.
*
* Arguments:
*	hw		device structure
*	mode		0 - Disable programming and begin code exec
*			1 - Enable volatile mem programming
*			2 - Enable non-volatile mem programming
*			3 - Program non-volatile section from NV download
*			    buffer. 
*			(host order)
*	lowaddr		
*	highaddr	For mode 1, sets the high & low order bits of 
*			the "destination address".  This address will be
*			the execution start address when download is
*			subsequently disabled.
*			For mode 2, sets the high & low order bits of 
*			the destination in NV ram.
*			For modes 0 & 3, should be zero. (host order)
*			NOTE: these address args are in CMD format
*	codelen		Length of the data to write in mode 2, 
*			zero otherwise. (host order)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_download(hfa384x_t *hw, UINT16 mode, UINT16 lowaddr, 
				UINT16 highaddr, UINT16 codelen)
{
	int	result = 0;
	hfa384x_metacmd_t cmd;

	DBFENTER;

	cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_DOWNLD) |
		HFA384x_CMD_PROGMODE_SET(mode);
	cmd.parm0 = lowaddr;
	cmd.parm1 = highaddr;
	cmd.parm2 = codelen;

	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_dl_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);
	
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_aux_enable
*
* Goes through the process of enabling the auxilary port.  This 
* is necessary prior to raw reads/writes to card data space.  
* Direct access to the card data space is only used for downloading
* code and debugging.
* Note that a call to this function is required before attempting
* a download.
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_aux_enable(hfa384x_t *hw, int force)
{
	int		result = -ETIMEDOUT;
	unsigned long	flags;
	UINT32		retries_remaining;
	UINT16		reg;
	UINT		auxen_mirror = hw->auxen;

	DBFENTER;

	/* Check for existing enable */
	if ( hw->auxen ) {
		hw->auxen++;
		return 0;
	}

	/* acquire the lock */
	spin_lock_irqsave( &(hw->cmdlock), flags);
	/* wait for cmd register busy bit to clear */
	retries_remaining = 100000;
	do {
		reg = hfa384x_getreg(hw, HFA384x_CMD);
		udelay(10);
	}
	while (HFA384x_CMD_ISBUSY(reg) && --retries_remaining);
	if (retries_remaining != 0) {
		/* busy bit clear, it's OK to write to ParamX regs */
		hfa384x_setreg(hw, HFA384x_AUXPW0,
			HFA384x_PARAM0);
		hfa384x_setreg(hw, HFA384x_AUXPW1,
			HFA384x_PARAM1);
		hfa384x_setreg(hw, HFA384x_AUXPW2,
			HFA384x_PARAM2);

		/* Set the aux enable in the Control register */
		hfa384x_setreg(hw, HFA384x_CONTROL_AUX_DOENABLE, 
			HFA384x_CONTROL);

		/* Now wait for completion */
		retries_remaining = 100000;
		do {
			reg = hfa384x_getreg(hw, HFA384x_CONTROL);
			udelay(10);
		}
		while ( ((reg & (BIT14|BIT15)) != HFA384x_CONTROL_AUX_ISENABLED) &&
			--retries_remaining );
		if (retries_remaining != 0) {
			result = 0;
			hw->auxen++;
		}
	}

	/* Force it enabled even if the command failed, if told.. */
	if ((hw->auxen == auxen_mirror) && force)
		hw->auxen++;

	spin_unlock_irqrestore( &(hw->cmdlock), flags);
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_cmd_aux_disable
*
* Goes through the process of disabling the auxilary port 
* enabled with aux_enable().
*
* Arguments:
*	hw		device structure
*
* Returns: 
*	0		success
*	>0		f/w reported failure - f/w status code
*	<0		driver reported error (timeout)
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_cmd_aux_disable(hfa384x_t *hw)
{
	int		result = -ETIMEDOUT;
	UINT32		timeout;
	UINT16		reg = 0;

	DBFENTER;

	/* See if there's more than one enable */
	if (hw->auxen) hw->auxen--;
	if (hw->auxen) return 0;

	/* Clear the aux enable in the Control register */
	hfa384x_setreg(hw, 0, HFA384x_PARAM0);
	hfa384x_setreg(hw, 0, HFA384x_PARAM1);
	hfa384x_setreg(hw, 0, HFA384x_PARAM2);
	hfa384x_setreg(hw, HFA384x_CONTROL_AUX_DODISABLE, 
		HFA384x_CONTROL);

	/* Now wait for completion */
	timeout = jiffies + 1*HZ;
	reg = hfa384x_getreg(hw, HFA384x_CONTROL);
	while ( ((reg & (BIT14|BIT15)) != HFA384x_CONTROL_AUX_ISDISABLED) &&
		time_before(jiffies,timeout) ){
		udelay(10);
		reg = hfa384x_getreg(hw, HFA384x_CONTROL);
	}
	if ((reg & (BIT14|BIT15)) == HFA384x_CONTROL_AUX_ISDISABLED ) {
		result = 0;
	}
	DBFEXIT;
	return result;
}

/*----------------------------------------------------------------
* hfa384x_drvr_low_level
*
* Write test commands to the card.  Some test commands don't make
* sense without prior set-up.  For example, continous TX isn't very
* useful until you set the channel.  That functionality should be
*
* Side effects:
*
* Call context:
*      process thread 
* -----------------------------------------------------------------*/
int hfa384x_drvr_low_level(hfa384x_t *hw, hfa384x_metacmd_t *cmd)
{
	int             result = 0;
	DBFENTER;
	
	/* Do i need a host2hfa... conversion ? */
#if 0
	printk(KERN_INFO "%#x %#x %#x %#x\n", cmd->cmd, cmd->parm0, cmd->parm1, cmd->parm2);
#endif
	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, cmd);
	spin_unlock_bh(&hw->cmdlock);

	DBFEXIT;
	return result;
}


/* TODO: determine if these will ever be needed */
#if 0
int hfa384x_cmd_readmif(hfa384x_t *hw)
{
	DBFENTER;
	DBFEXIT;
	return 0;
}


int hfa384x_cmd_writemif(hfa384x_t *hw)
{
	DBFENTER;
	DBFEXIT;
	return 0;
}
#endif

/*----------------------------------------------------------------
* hfa384x_drvr_mmi_read
*
* Read mmi registers.  mmi is intersil-speak for the baseband
* processor registers.
*
* Arguments:
*       hw              device structure
*       register        The test register to be accessed (must be even #).
*
* Returns:
*       0               success
*       >0              f/w reported error - f/w status code
*       <0              driver reported error
*
* Side effects:
*
* Call context:
*       process thread
----------------------------------------------------------------*/
int hfa384x_drvr_mmi_read(hfa384x_t *hw, UINT32 addr, UINT32 *resp)
{
        int             result = 0;
	hfa384x_metacmd_t cmd;

        DBFENTER;
	cmd.cmd = (UINT16) 0x30;
	cmd.parm0 = (UINT16) addr;
	cmd.parm1 = 0;
	cmd.parm2 = 0;

        /* Do i need a host2hfa... conversion ? */
	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);

	*resp = (UINT32) cmd.resp0;

        DBFEXIT;
        return result;
}

/*----------------------------------------------------------------
* hfa384x_drvr_mmi_write
*
* Read mmi registers.  mmi is intersil-speak for the baseband
* processor registers.
*
* Arguments:
*       hw              device structure
*       addr            The test register to be accessed (must be even #).
*       data            The data value to write to the register.
*
* Returns:
*       0               success
*       >0              f/w reported error - f/w status code
*       <0              driver reported error
*
* Side effects:
*
* Call context:
*       process thread
----------------------------------------------------------------*/

int
hfa384x_drvr_mmi_write(hfa384x_t *hw, UINT32 addr, UINT32 data)
{
        int             result = 0;
	hfa384x_metacmd_t cmd;

        DBFENTER;
	cmd.cmd = (UINT16) 0x31;
	cmd.parm0 = (UINT16) addr;
	cmd.parm1 = (UINT16) data;
	cmd.parm2 = 0;

        WLAN_LOG_DEBUG(1,"mmi write : addr = 0x%08lx\n", addr);
        WLAN_LOG_DEBUG(1,"mmi write : data = 0x%08lx\n", data);

        /* Do i need a host2hfa... conversion ? */
	spin_lock_bh(&hw->cmdlock);
	result = hfa384x_docmd_wait(hw, &cmd);
	spin_unlock_bh(&hw->cmdlock);

        DBFEXIT;
        return result;
}


/* TODO: determine if these will ever be needed */
#if 0
int hfa384x_cmd_readmif(hfa384x_t *hw)
{
        DBFENTER;
        DBFEXIT;
        return 0;
}


int hfa384x_cmd_writemif(hfa384x_t *hw)
{
        DBFENTER;
        DBFEXIT;
        return 0;
}
#endif



/*----------------------------------------------------------------
* hfa384x_copy_from_bap
*
* Copies a collection of bytes from the MAC controller memory via
* one set of BAP registers.
*
* Arguments:
*	hw		device structure
*	bap		[0|1] which BAP to use
*	id		FID or RID, destined for the select register (host order)
*	offset		An _even_ offset into the buffer for the given
*			FID/RID.  We haven't the means to validate this,
*			so be careful. (host order)
*	buf		ptr to array of bytes
*	len		length of data to transfer in bytes
*
* Returns: 
*	0		success
*	>0		f/w reported failure - value of offset reg.
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread
*	interrupt
----------------------------------------------------------------*/
int hfa384x_copy_from_bap(hfa384x_t *hw, UINT16 bap, UINT16 id, UINT16 offset,
				void *buf, UINT len)
{
	int		result = 0;
	unsigned long	flags = 0;
	UINT8		*d = (UINT8*)buf;
	UINT		selectreg;
	UINT		offsetreg;
	UINT		datareg;
	UINT		i;
	UINT16		reg = 0;

	DBFENTER;

	/* Validate bap, offset, buf, and len */
	if ( (bap > 1) || 
	     (offset > HFA384x_BAP_OFFSET_MAX) || 
	     (offset % 2) ||
	     (buf == NULL) ||
	     (len > HFA384x_BAP_DATALEN_MAX) ){
	     	result = -EINVAL;
	} else {
		selectreg = (bap == 1) ?  HFA384x_SELECT1 : HFA384x_SELECT0 ;
		offsetreg = (bap == 1) ?  HFA384x_OFFSET1 : HFA384x_OFFSET0 ;
		datareg =   (bap == 1) ?  HFA384x_DATA1 : HFA384x_DATA0 ;

		/* Obtain lock */
		spin_lock_irqsave( &(hw->baplock), flags);
		
		/* Write id to select reg */
		hfa384x_setreg(hw, id, selectreg);
		/* Write offset to offset reg */
		hfa384x_setreg(hw, offset, offsetreg);
		/* Wait for offset[busy] to clear (see BAP_TIMEOUT) */
		i = 0; 
		do {
			reg = hfa384x_getreg(hw, offsetreg);
			if ( i > 0 ) udelay(10);
			i++;
		} while ( i < prism2_bap_timeout && HFA384x_OFFSET_ISBUSY(reg));
#if (WLAN_HOSTIF != WLAN_PCI)
		/* Release lock */
		spin_unlock_irqrestore( &(hw->baplock), flags);
#endif

		if ( HFA384x_OFFSET_ISBUSY(reg) ){
			/* If timeout, return -ETIMEDOUT */
			result = reg;
		} else if ( HFA384x_OFFSET_ISERR(reg) ){
			/* If offset[err] == 1, return -EINVAL */
			result = reg;
		} else {
			/* Read even(len) buf contents from data reg */
			for ( i = 0; i < (len & 0xfffe); i+=2 ) {
				*(UINT16*)(&(d[i])) = 
					hfa384x_getreg_noswap(hw, datareg);
			}
			/* If len odd, handle last byte */
			if ( len % 2 ){
				reg = hfa384x_getreg_noswap(hw, datareg);
				d[len-1] = ((UINT8*)(&reg))[0];
			}
		}

		/* According to Intersil errata dated 9/16/02:
	
		"In PRISM PCI MAC host interface, if both BAPs are concurrently 
		 requesing memory access, both will accept the Ack.   There is no
		 firmware workaround possible.  To prevent BAP access failures or
		 hang conditions the host MUST NOT access both BAPs in sucession
		 unless at least 5us elapses between accesses.  The safest choice
		 is to USE ONLY ONE BAP for all data movement operations."
	
		 What this means:
	
		 We have to serialize ALL BAP accesses, and furthermore, add a 5us
		 delay after access if we're using a PCI platform.
	
		 Unfortunately, this means we have to lock out interrupts througout
		 the entire BAP copy.
	
		 It remains to be seen if "BAP access" means "BAP setup" or the more
		 literal definition of "copying data back and forth"  I'm erring for
		 the latter, safer definition.  -- SLP.
	
		*/

#if (WLAN_HOSTIF == WLAN_PCI)
		udelay(5);
		/* Release lock */
		spin_unlock_irqrestore( &(hw->baplock), flags);
#endif

	}

	if (result) {
	  WLAN_LOG_DEBUG(1, 
			  "copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", 
			  reg, len, result);
	}
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_copy_to_bap
*
* Copies a collection of bytes to the MAC controller memory via
* one set of BAP registers.
*
* Arguments:
*	hw		device structure
*	bap		[0|1] which BAP to use
*	id		FID or RID, destined for the select register (host order)
*	offset		An _even_ offset into the buffer for the given
*			FID/RID.  We haven't the means to validate this,
*			so be careful. (host order)
*	buf		ptr to array of bytes
*	len		length of data to transfer (in bytes)
*
* Returns: 
*	0		success
*	>0		f/w reported failure - value of offset reg.
*	<0		driver reported error (timeout|bad arg)
*
* Side effects:
*
* Call context:
*	process thread
*	interrupt
----------------------------------------------------------------*/
int hfa384x_copy_to_bap(hfa384x_t *hw, UINT16 bap, UINT16 id, UINT16 offset,
				void *buf, UINT len)
{
	return hfa384x_copy_to_bap4(hw, bap, id, offset, buf, len, NULL, 0, NULL, 0, NULL, 0);
}

int hfa384x_copy_to_bap4(hfa384x_t *hw, UINT16 bap, UINT16 id, UINT16 offset,
			 void *buf, UINT len1, void* buf2, UINT len2,
			 void *buf3, UINT len3, void *buf4, UINT len4)
{
	int		result = 0;
	unsigned long	flags = 0;
	UINT8		*d;
	UINT		selectreg;
	UINT		offsetreg;
	UINT		datareg;
	UINT		i;
	UINT16		reg;

	DBFENTER;

//	printk(KERN_DEBUG "ctb1 %d id %04x o %d %d %d %d %d\n", bap, id, offset, len1, len2, len3, len4);

	/* Validate bap, offset, buf, and len */
	if ( (bap > 1) || 
	     (offset > HFA384x_BAP_OFFSET_MAX) || 
	     (offset % 2) ||
	     (buf == NULL) ||
	     (len1+len2+len3+len4 > HFA384x_BAP_DATALEN_MAX) ){
	     	result = -EINVAL;
	} else {
		selectreg = (bap == 1) ? HFA384x_SELECT1 : HFA384x_SELECT0;
		offsetreg = (bap == 1) ? HFA384x_OFFSET1 : HFA384x_OFFSET0;
		datareg =   (bap == 1) ? HFA384x_DATA1   : HFA384x_DATA0;
		/* Obtain lock */
		spin_lock_irqsave( &(hw->baplock), flags);
		
		/* Write id to select reg */
		hfa384x_setreg(hw, id, selectreg);
		udelay(10);
		/* Write offset to offset reg */
		hfa384x_setreg(hw, offset, offsetreg);
		/* Wait for offset[busy] to clear (see BAP_TIMEOUT) */
		i = 0; 
		do {
			reg = hfa384x_getreg(hw, offsetreg);
			if ( i > 0 ) udelay(10);
			i++;
		} while ( i < prism2_bap_timeout && HFA384x_OFFSET_ISBUSY(reg));
	
#if (WLAN_HOSTIF != WLAN_PCI)
		/* Release lock */
		spin_unlock_irqrestore( &(hw->baplock), flags);
#endif

		if ( HFA384x_OFFSET_ISBUSY(reg) ){
			/* If timeout, return reg */
			result = reg;
		} else if ( HFA384x_OFFSET_ISERR(reg) ){
			/* If offset[err] == 1, return reg */
			result = reg;
		} else {
			d = (UINT8*)buf;
			/* Write even(len1) buf contents to data reg */
			for ( i = 0; i < (len1 & 0xfffe); i+=2 ) {
				hfa384x_setreg_noswap(hw, 
					*(UINT16*)(&(d[i])), datareg);
			}
			if (len1 & 1) {
				UINT16 data;
				UINT8 *b = (UINT8 *) &data;
				b[0] = d[len1-1];
				if (buf2 != NULL) {
					d = (UINT8*)buf2;
					b[1] = d[0];
					len2--;		
					buf2++;
				}
				hfa384x_setreg_noswap(hw, data, datareg);
			}
			if ((buf2 != NULL) && (len2 > 0)) {
				/* Write even(len2) buf contents to data reg */
				d = (UINT8*)buf2;
				for ( i = 0; i < (len2 & 0xfffe); i+=2 ) {
					hfa384x_setreg_noswap(hw, *(UINT16*)(&(d[i])), datareg);
				}
				if (len2 & 1) {
					UINT16 data;
					UINT8 *b = (UINT8 *) &data;
					b[0] = d[len2-1];
					if (buf3 != NULL) {
						d = (UINT8*)buf3;
						b[1] = d[0];
						len3--;	
						buf3++;
					}
					hfa384x_setreg_noswap(hw, data, datareg);
				}
			}

			if ((buf3 != NULL) && (len3 > 0)) {
				/* Write even(len3) buf contents to data reg */
				d = (UINT8*)buf3;
				for ( i = 0; i < (len3 & 0xfffe); i+=2 ) {
					hfa384x_setreg_noswap(hw, *(UINT16*)(&(d[i])), datareg);
				}
				if (len3 & 1) {
					UINT16 data;
					UINT8 *b = (UINT8 *) &data;
					b[0] = d[len3-1];
					if (buf4 != NULL) {
						d = (UINT8*)buf4;
						b[1] = d[0];
						len4--;
						buf4++;
					}
					hfa384x_setreg_noswap(hw, data, datareg);
				}
			}
			if ((buf4 != NULL) && (len4 > 0)) {
				/* Write even(len4) buf contents to data reg */
				d = (UINT8*)buf4;
				for ( i = 0; i < (len4 & 0xfffe); i+=2 ) {
					hfa384x_setreg_noswap(hw, *(UINT16*)(&(d[i])), datareg);
				}
				if (len4 & 1) {
					UINT16 data;
					UINT8 *b = (UINT8 *) &data;
					b[0] = d[len4-1];
					b[1] = 0;

					hfa384x_setreg_noswap(hw, data, datareg);
				}
			}
//			printk(KERN_DEBUG "ctb2 %d id %04x o %d %d %d %d %d\n", bap, id, offset, len1, len2, len3, len4);

		}

#if (WLAN_HOSTIF == WLAN_PCI)
		udelay(5);
		/* Release lock */
		spin_unlock_irqrestore( &(hw->baplock), flags);
#endif

	}
	
	if (result)
		WLAN_LOG_ERROR("copy_to_bap() failed.\n");

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_copy_from_aux
*
* Copies a collection of bytes from the controller memory.  The
* Auxiliary port MUST be enabled prior to calling this function.
* We _might_ be in a download state.
*
* Arguments:
*	hw		device structure
*	cardaddr	address in hfa384x data space to read
*	auxctl		address space select
*	buf		ptr to destination host buffer
*	len		length of data to transfer (in bytes)
*
* Returns: 
*	nothing
*
* Side effects:
*	buf contains the data copied
*
* Call context:
*	process thread
*	interrupt
----------------------------------------------------------------*/
void 
hfa384x_copy_from_aux(
	hfa384x_t *hw, UINT32 cardaddr, UINT32 auxctl, void *buf, UINT len)
{
	UINT16		currpage;
	UINT16		curroffset;
	UINT		i = 0;

	DBFENTER;

	if ( !(hw->auxen) ) {
		WLAN_LOG_DEBUG(1,
			"Attempt to read 0x%04lx when aux not enabled\n",
			cardaddr);
		return;
			
	}
	/* Build appropriate aux page and offset */
	currpage = HFA384x_AUX_MKPAGE(cardaddr);
	curroffset = HFA384x_AUX_MKOFF(cardaddr, auxctl);
	hfa384x_setreg(hw, currpage, HFA384x_AUXPAGE);
	hfa384x_setreg(hw, curroffset, HFA384x_AUXOFFSET);
	udelay(5);	/* beat */

	/* read the data */
	while ( i < len) {
		*((UINT16*)(buf+i)) = hfa384x_getreg_noswap(hw, HFA384x_AUXDATA);
		i+=2;
		curroffset+=2;
		if ( (curroffset&HFA384x_ADDR_AUX_OFF_MASK) > 
			HFA384x_ADDR_AUX_OFF_MAX ) {
			currpage++;
			curroffset = 0;
			curroffset = HFA384x_AUX_MKOFF(curroffset, auxctl);
			hfa384x_setreg(hw, currpage, HFA384x_AUXPAGE);
			hfa384x_setreg(hw, curroffset, HFA384x_AUXOFFSET);
			udelay(5);	/* beat */
		}
	}
	/* Make sure the auxctl bits are clear */
	hfa384x_setreg(hw, 0, HFA384x_AUXOFFSET);
	DBFEXIT;
}


/*----------------------------------------------------------------
* hfa384x_copy_to_aux
*
* Copies a collection of bytes to the controller memory.  The
* Auxiliary port MUST be enabled prior to calling this function.
* We _might_ be in a download state.
*
* Arguments:
*	hw		device structure
*	cardaddr	address in hfa384x data space to read
*	auxctl		address space select
*	buf		ptr to destination host buffer
*	len		length of data to transfer (in bytes)
*
* Returns: 
*	nothing
*
* Side effects:
*	Controller memory now contains a copy of buf
*
* Call context:
*	process thread
*	interrupt
----------------------------------------------------------------*/
void 
hfa384x_copy_to_aux(
	hfa384x_t *hw, UINT32 cardaddr, UINT32 auxctl, void *buf, UINT len)
{
	UINT16		currpage;
	UINT16		curroffset;
	UINT		i = 0;

	DBFENTER;

	if ( !(hw->auxen) ) {
		WLAN_LOG_DEBUG(1,
			"Attempt to read 0x%04lx when aux not enabled\n",
			cardaddr);
		return;
			
	}
	/* Build appropriate aux page and offset */
	currpage = HFA384x_AUX_MKPAGE(cardaddr);
	curroffset = HFA384x_AUX_MKOFF(cardaddr, auxctl);
	hfa384x_setreg(hw, currpage, HFA384x_AUXPAGE);
	hfa384x_setreg(hw, curroffset, HFA384x_AUXOFFSET);
	udelay(5);	/* beat */

	/* write the data */
	while ( i < len) {
		hfa384x_setreg_noswap(hw, 
			*((UINT16*)(buf+i)), HFA384x_AUXDATA);
		i+=2;
		curroffset+=2;
		if ( curroffset > HFA384x_ADDR_AUX_OFF_MAX ) {
			currpage++;
			curroffset = 0;
			hfa384x_setreg(hw, currpage, HFA384x_AUXPAGE);
			hfa384x_setreg(hw, curroffset, HFA384x_AUXOFFSET);
			udelay(5);	/* beat */
		}
	}
	DBFEXIT;
}


/*----------------------------------------------------------------
* hfa384x_cmd_wait
*
* Waits for availability of the Command register, then
* issues the given command.  Then polls the Evstat register
* waiting for command completion.  Timeouts shouldn't be 
* possible since we're preventing overlapping commands and all
* commands should be cleared and acknowledged.
*
* Arguments:
*	wlandev		device structure
*       cmd             cmd structure.  Includes all arguments and result
*                       data points.  All in host order.
*
* Returns: 
*	0		success
*	-ETIMEDOUT	timed out waiting for register ready or
*			command completion
*	>0		command indicated error, Status and Resp0-2 are
*			in hw structure.
*
* Side effects:
*	
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_docmd_wait( hfa384x_t *hw, hfa384x_metacmd_t *cmd)
{
	int		result = -ETIMEDOUT;
	UINT16		reg = 0;
	UINT16          counter;

	DBFENTER;

	hw->cmdflag = 0;
	hw->cmddata = cmd;

	/* 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)) {
		WLAN_LOG_ERROR("hfa384x_cmd timeout(1), reg=0x%0hx.\n", reg);
		goto failed;
	}
	if (!HFA384x_CMD_ISBUSY(reg)) {
		/* busy bit clear, write command */
		hfa384x_setreg(hw, cmd->parm0, HFA384x_PARAM0);
		hfa384x_setreg(hw, cmd->parm1, HFA384x_PARAM1);
		hfa384x_setreg(hw, cmd->parm2, HFA384x_PARAM2);
		hfa384x_setreg(hw, cmd->cmd, HFA384x_CMD);

#ifdef CMD_IRQ

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0))
		while (! hw->cmdflag)
			interruptible_sleep_on(&hw->cmdq);
#else
		wait_event_interruptible(hw->cmdq, hw->cmdflag);
#endif
		result = HFA384x_STATUS_RESULT_GET(cmd->status);
#else // CMD_IRQ
		/* 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) ) {
			result = 0;
			cmd->status = hfa384x_getreg(hw, HFA384x_STATUS);
			cmd->resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
			cmd->resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
			cmd->resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
			hfa384x_setreg(hw, HFA384x_EVACK_CMD, 
				HFA384x_EVACK);
			result = HFA384x_STATUS_RESULT_GET(cmd->status);
		} else {
			WLAN_LOG_ERROR("hfa384x_cmd timeout(2), reg=0x%0hx.\n", reg);
		}
#endif  /* CMD_IRQ */
	}

 failed:
	hw->cmdflag = 0;
	hw->cmddata = NULL;

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_dl_docmd_wait
*
* Waits for availability of the Command register, then
* issues the given command.  Then polls the Evstat register
* waiting for command completion.  Timeouts shouldn't be 
* possible since we're preventing overlapping commands and all
* commands should be cleared and acknowledged.
*
* This routine is only used for downloads.  Since it doesn't lock out
* interrupts the system response is much better.
*
* Arguments:
*	wlandev		device structure
*       cmd             cmd structure.  Includes all arguments and result
*                       data points.  All in host order.
*
* Returns: 
*	0		success
*	-ETIMEDOUT	timed out waiting for register ready or
*			command completion
*	>0		command indicated error, Status and Resp0-2 are
*			in hw structure.
*
* Side effects:
*	
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_dl_docmd_wait( hfa384x_t *hw, hfa384x_metacmd_t *cmd)
{
	int		result = -ETIMEDOUT;
	UINT32		timeout;
	UINT16		reg = 0;

	DBFENTER;
	/* wait for the busy bit to clear */	
	timeout = jiffies + 1*HZ;
	reg = hfa384x_getreg(hw, HFA384x_CMD);
	while ( HFA384x_CMD_ISBUSY(reg) && time_before( jiffies, timeout) ) {
		reg = hfa384x_getreg(hw, HFA384x_CMD);
		udelay(10);
	}
	if (HFA384x_CMD_ISBUSY(reg)) {
		WLAN_LOG_WARNING("Timed out waiting for cmd register.\n");
		goto failed;
	}

	if (!HFA384x_CMD_ISBUSY(reg)) {
		/* busy bit clear, write command */
		hfa384x_setreg(hw, cmd->parm0, HFA384x_PARAM0);
		hfa384x_setreg(hw, cmd->parm1, HFA384x_PARAM1);
		hfa384x_setreg(hw, cmd->parm2, HFA384x_PARAM2);
		hfa384x_setreg(hw, cmd->cmd, HFA384x_CMD);

		/* Now wait for completion */
		if ( (HFA384x_CMD_CMDCODE_GET(cmd->cmd) == HFA384x_CMDCODE_DOWNLD) ) { 
			/* dltimeout is in ms */
			timeout = (((UINT32)hw->dltimeout) / 1000UL) * HZ;
			if ( timeout > 0 ) {
				timeout += jiffies;
			} else {
				timeout = jiffies + 1*HZ;
			}
		} else {
			timeout = jiffies + 1*HZ;
		}
		reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
		while ( !HFA384x_EVSTAT_ISCMD(reg) && time_before(jiffies,timeout) ) {
			udelay(100);
			reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
		}
		if ( HFA384x_EVSTAT_ISCMD(reg) ) {
			result = 0;
			cmd->status = hfa384x_getreg(hw, HFA384x_STATUS);
			cmd->resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
			cmd->resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
			cmd->resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
			hfa384x_setreg(hw, HFA384x_EVACK_CMD, HFA384x_EVACK);
			result = HFA384x_STATUS_RESULT_GET(cmd->status);
		}
	}

failed:
	DBFEXIT;
	return result;
}

/*----------------------------------------------------------------
* hfa384x_drvr_start
*
* Issues the MAC initialize command, sets up some data structures,
* and enables the interrupts.  After this function completes, the
* low-level stuff should be ready for any/all commands.
*
* Arguments:
*	hw		device structure
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_start(hfa384x_t *hw)
{
	int	result = 0;
	UINT16			reg;
	int			i;
	int			j;
	DBFENTER;

	/* call initialize */
	result = hfa384x_cmd_initialize(hw);
	if (result != 0) {
		WLAN_LOG_ERROR("Initialize command failed.\n");
		goto failed;
	}


	/* make sure interrupts are disabled and any layabout events cleared */
	hfa384x_setreg(hw, 0, HFA384x_INTEN);
	hfa384x_setreg(hw, 0xffff, HFA384x_EVACK);

        hw->txfid_head = 0;
        hw->txfid_tail = 0;
        hw->txfid_N = HFA384x_DRVR_FIDSTACKLEN_MAX;
        memset(hw->txfid_queue, 0, sizeof(hw->txfid_queue));

	/* Allocate tx and notify FIDs */
	/* First, tx */
	for ( i = 0; i < HFA384x_DRVR_FIDSTACKLEN_MAX-1; i++) {
		result = hfa384x_cmd_allocate(hw, HFA384x_DRVR_TXBUF_MAX);
		if (result != 0) {
			WLAN_LOG_ERROR("Allocate(tx) command failed.\n");
			goto failed;
		}
		j = 0;
		do {
			reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
			udelay(10);
			j++;
		} while ( !HFA384x_EVSTAT_ISALLOC(reg) && j < 50); /* 50 is timeout */
		if ( j >= 50 ) {
			WLAN_LOG_ERROR("Timed out waiting for evalloc(tx).\n");
			result = -ETIMEDOUT;
			goto failed;
		}
		reg = hfa384x_getreg(hw, HFA384x_ALLOCFID);

		txfid_queue_add(hw, reg);

		WLAN_LOG_DEBUG(4,"hw->txfid_queue[%d]=0x%04x\n",i,reg);

		reg = HFA384x_EVACK_ALLOC_SET(1);
		hfa384x_setreg(hw, reg, HFA384x_EVACK);

	}

	/* Now, the info frame fid */
	result = hfa384x_cmd_allocate(hw, HFA384x_INFOFRM_MAXLEN);
	if (result != 0) {
		goto failed;
		WLAN_LOG_ERROR("Allocate(tx) command failed.\n");
	}
	i = 0;
	do {
		reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
		udelay(10);
		i++;
	} while ( !HFA384x_EVSTAT_ISALLOC(reg) && i < 50); /* 50 is timeout */
	if ( i >= 50 ) {
		WLAN_LOG_ERROR("Timed out waiting for evalloc(info).\n");
		result = -ETIMEDOUT;
		goto failed;
	}
	hw->infofid = hfa384x_getreg(hw, HFA384x_ALLOCFID);
	reg = HFA384x_EVACK_ALLOC_SET(1);
	hfa384x_setreg(hw, reg, HFA384x_EVACK);
	WLAN_LOG_DEBUG(4,"hw->infofid=0x%04x\n", hw->infofid);

	/* Set swsupport regs to magic # for card presence detection */
	hfa384x_setreg(hw, HFA384x_DRVR_MAGIC, HFA384x_SWSUPPORT0);

	/* Now enable the interrupts and set the running state */
	hfa384x_setreg(hw, 0xffff, HFA384x_EVSTAT);
	hfa384x_events_all(hw);

	hw->state = HFA384x_STATE_RUNNING;

	goto done;
failed:
	WLAN_LOG_ERROR("Failed, result=%d\n", result);
done:
	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_stop
*
* Issues the initialize command to leave us in the 'reset' state.
*
* Arguments:
*	hw		device structure
* Returns: 
*	0		success
*	>0		f/w reported error - f/w status code
*	<0		driver reported error
*
* Side effects:
*
* Call context:
*	process thread 
----------------------------------------------------------------*/
int hfa384x_drvr_stop(hfa384x_t *hw)
{
	int	result = 0;
	DBFENTER;

	flush_scheduled_work();
#ifdef DECLARE_TASKLET
	tasklet_kill(&hw->bap_tasklet);
#else
#warning "We aren't cleaning up the bap_tasklet cleanly!"
#endif

	if (hw->state == HFA384x_STATE_RUNNING) {
		/* 
		 * Send the MAC initialize cmd.
		 */
		hfa384x_cmd_initialize(hw);
	
		/* 
		 * Make absolutely sure interrupts are disabled and any 
		 * layabout events cleared 
		 */ 
		hfa384x_setreg(hw, 0, HFA384x_INTEN);
		hfa384x_setreg(hw, 0xffff, HFA384x_EVACK);
	}

	DBFEXIT;
	return result;
}


/*----------------------------------------------------------------
* hfa384x_drvr_txframe
*
* Takes a frame from prism2sta and queues it for transmission.
*
* Arguments:
*	hw		device structure
*	skb		packet buffer struct.  Contains an 802.11
*			data frame.
*       p80211_hdr      points to the 802.11 header for the packet.
* Returns: 
*	0		Success and more buffs available
*	1		Success but no more buffs
*	2		Allocation failure
*	3		MAC Tx command failed
*	4		Buffer full or queue busy
*
* Side effects:
*
* Call context:
*	process thread
----------------------------------------------------------------*/
int hfa384x_drvr_txframe(hfa384x_t *hw, struct sk_buff *skb, p80211_hdr_t *p80211_hdr, p80211_metawep_t *p80211_wep)
{
	hfa384x_tx_frame_t	txdesc;
	UINT16			macq = 0;
	UINT16			fid;
	int			result;

	DBFENTER;

	/* Build Tx frame structure */
	/* Set up the control field */
	memset(&txdesc, 0, sizeof(txdesc));

/* Tx complete and Tx exception disable per dleach.  Might be causing 
 * buf depletion 
 */
#define DOBOTH 1
#if DOBOTH
	txdesc.tx_control = 
		HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) | 
		HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1);	
#elif DOEXC
	txdesc.tx_control = 
		HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
		HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(0);	
#else
	txdesc.tx_control = 
		HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
		HFA384x_TX_TXEX_SET(0) | HFA384x_TX_TXOK_SET(0);	
#endif

	/* if we're using host WEP, increase size by IV+ICV */
	if (p80211_wep->data) {
		txdesc.data_len = host2hfa384x_16(skb->len+8);
		//		txdesc.tx_control |= HFA384x_TX_NOENCRYPT_SET(1);
	} else {
		txdesc.data_len =  host2hfa384x_16(skb->len);
	}

	txdesc.tx_control = host2hfa384x_16(txdesc.tx_control);
	/* copy the header over to the txdesc */
	memcpy(&(txdesc.frame_control), p80211_hdr, sizeof(p80211_hdr_t));

	/* Since tbusy is set whenever the stack is empty, there should 
	 * always be something on the stack if we get to this point.
	 * [MSM]: NOT TRUE!!!!! so I added the test of fid below.
	 */

	/* Allocate FID */

	fid = txfid_queue_remove(hw);

	if ( fid == 0 ) { /* stack or queue was empty */
		return 4;
	}

	/* now let's get the cmdlock */
	spin_lock(&hw->cmdlock);

	/* Copy descriptor+payload to FID */
        if (p80211_wep->data) { 
		result = hfa384x_copy_to_bap4(hw, HFA384x_BAP_PROC, fid, 0,
					      &txdesc, sizeof(txdesc),
					      p80211_wep->iv, sizeof(p80211_wep->iv),
					      p80211_wep->data, skb->len,
					      p80211_wep->icv, sizeof(p80211_wep->icv));
	} else {
		result = hfa384x_copy_to_bap4(hw, HFA384x_BAP_PROC, fid, 0,
					      &txdesc, sizeof(txdesc),
					      skb->data, skb->len,
					      NULL, 0, NULL, 0);
	}

	if ( result ) {
		WLAN_LOG_DEBUG(1,
			"copy_to_bap(%04x, %d, %d) failed, result=0x%x\n", 
			fid,
		 	sizeof(txdesc), 
	 		skb->len,
			result);

		/* put the fid back in the queue */
		txfid_queue_add(hw, fid);

		result = 3;
		goto failed;
	}

	/* Issue Tx command */
	result = hfa384x_cmd_transmit(hw, HFA384x_TXCMD_RECL, macq, fid);
		
	if ( result != 0 ) {
		txfid_queue_add(hw, fid);

		WLAN_LOG_DEBUG(1,"cmd_tx(%04x) failed, result=%d\n", 
			fid, result);
		result = 3;
		goto failed;
	}
	
	/* indicate we haven't any buffers, int_alloc will clear */
	result = txfid_queue_empty(hw); 
failed:

	spin_unlock(&hw->cmdlock);

	DBFEXIT;
	return result;
}

/*----------------------------------------------------------------
* hfa384x_interrupt
*
* Driver interrupt handler.
*
* Arguments:
*	irq		irq number
*	dev_id		pointer to the device
*	regs		registers
*
* Returns: 
*	nothing
*
* Side effects:
*	May result in a frame being passed up the stack or an info
*	frame being handled.  
*
* Call context:
*	Ummm, could it be interrupt?
----------------------------------------------------------------*/
irqreturn_t hfa384x_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	int			reg;
	wlandevice_t		*wlandev = (wlandevice_t*)dev_id;
	hfa384x_t		*hw = wlandev->priv;
	int			ev_read = 0;
	DBFENTER;

	if (!wlandev || wlandev->hwremoved)
		return IRQ_NONE;  /* Not much we can do w/o hardware */
#if (WLAN_HOSTIF == WLAN_PCMCIA)
	if (hw->iobase == 0)  /* XXX FIXME Properly */
		return IRQ_NONE;
#endif

	for (;;ev_read++) {
		if (ev_read >= prism2_irq_evread_max)
			break;

		/* Check swsupport reg magic # for card presence */
		reg = hfa384x_getreg(hw, HFA384x_SWSUPPORT0);
		if ( reg != HFA384x_DRVR_MAGIC) {
			WLAN_LOG_DEBUG(2, "irq=%d, no magic.  Card removed?.\n", irq);
			break;
		}		

		/* read the EvStat register for interrupt enabled events */
		reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
		
		/* AND with the enabled interrupts */
		reg &= hfa384x_getreg(hw, HFA384x_INTEN);

		/* Handle the events */
		if ( HFA384x_EVSTAT_ISWTERR(reg) ){
			WLAN_LOG_ERROR(
			"Error: WTERR interrupt received (unhandled).\n");
			hfa384x_setreg(hw, HFA384x_EVACK_WTERR_SET(1), 
				HFA384x_EVACK);
		}
		
		if ( HFA384x_EVSTAT_ISINFDROP(reg) ){
			hfa384x_int_infdrop(wlandev);
			hfa384x_setreg(hw, HFA384x_EVACK_INFDROP_SET(1), 
				HFA384x_EVACK);
		}
	
		if (HFA384x_EVSTAT_ISBAP_OP(reg)) {
			/* Disable the BAP interrupts */
			hfa384x_events_nobap(hw);
#ifdef DECLARE_TASKLET
			tasklet_schedule(&hw->bap_tasklet);
#else
			queue_task(&hw->bap_tasklet, &tq_immediate);
			mark_bh(IMMEDIATE_BH);
#endif
		}

		if ( HFA384x_EVSTAT_ISALLOC(reg) ){
			hfa384x_int_alloc(wlandev);
			hfa384x_setreg(hw, HFA384x_EVACK_ALLOC_SET(1),
				HFA384x_EVACK);
		}

		if ( HFA384x_EVSTAT_ISDTIM(reg) ){
			hfa384x_int_dtim(wlandev);
			hfa384x_setreg(hw, HFA384x_EVACK_DTIM_SET(1),
				HFA384x_EVACK);
		}
#ifdef CMD_IRQ
		if ( HFA384x_EVSTAT_ISCMD(reg) ){
			hfa384x_int_cmd(wlandev);
			hfa384x_setreg(hw, HFA384x_EVACK_CMD_SET(1),
				       HFA384x_EVACK);
		}
#endif

		/* allow the evstat to be updated after the evack */
		udelay(20);
	}

	DBFEXIT;
	return IRQ_HANDLED;
}

#ifdef CMD_IRQ
/*----------------------------------------------------------------
* hfa384x_int_cmd
*
* Handles command completion event.
*
* Arguments:
*	wlandev		wlan device structure
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	interrupt
----------------------------------------------------------------*/
void hfa384x_int_cmd(wlandevice_t *wlandev)
{
	hfa384x_t		*hw = wlandev->priv;
	DBFENTER;

	// check to make sure it's the right command?
	if (hw->cmddata) {
		hw->cmddata->status = hfa384x_getreg(hw, HFA384x_STATUS);
		hw->cmddata->resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
		hw->cmddata->resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
		hw->cmddata->resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
	}
	hw->cmdflag = 1;

	printk(KERN_INFO "um. int_cmd\n");

	wake_up_interruptible(&hw->cmdq);

	// XXXX perform a bap copy too?

	DBFEXIT;
	return;
}
#endif

/*----------------------------------------------------------------
* hfa384x_int_dtim
*
* Handles the DTIM early warning event.
*
* Arguments:
*	wlandev		wlan device structure
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	interrupt
----------------------------------------------------------------*/
void hfa384x_int_dtim(wlandevice_t *wlandev)
{
#if 0
	hfa384x_t		*hw = wlandev->priv;
#endif
	DBFENTER;
	prism2sta_ev_dtim(wlandev);
	DBFEXIT;
	return;
}


/*----------------------------------------------------------------
* hfa384x_int_infdrop
*
* Handles the InfDrop event.
*
* Arguments:
*	wlandev		wlan device structure
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	interrupt
----------------------------------------------------------------*/
void hfa384x_int_infdrop(wlandevice_t *wlandev)
{
#if 0
	hfa384x_t		*hw = wlandev->priv;
#endif
	DBFENTER;
	prism2sta_ev_infdrop(wlandev);
	DBFEXIT;
	return;
}


/*----------------------------------------------------------------
* hfa384x_int_info
*
* Handles the Info event.
*
* Arguments:
*	wlandev		wlan device structure
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	tasklet
----------------------------------------------------------------*/
void hfa384x_int_info(wlandevice_t *wlandev)
{
	hfa384x_t		*hw = wlandev->priv;
	UINT16			reg;
	hfa384x_InfFrame_t	inf;
	int			result;
	DBFENTER;
	/* Retrieve the FID */
	reg = hfa384x_getreg(hw, HFA384x_INFOFID);

	/* Retrieve the length */
	result = hfa384x_copy_from_bap( hw, 
		HFA384x_BAP_INT, reg, 0, &inf.framelen, sizeof(UINT16));
	if ( result ) {
		WLAN_LOG_DEBUG(1, 
			"copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", 
			reg, sizeof(inf), result);
		goto failed;
	}
	inf.framelen = hfa384x2host_16(inf.framelen);

	/* Retrieve the rest */
	result = hfa384x_copy_from_bap( hw, 
		HFA384x_BAP_INT, reg, sizeof(UINT16),
		&(inf.infotype), inf.framelen * sizeof(UINT16));
	if ( result ) {
		WLAN_LOG_DEBUG(1, 
			"copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", 
			reg, sizeof(inf), result);
		goto failed;
	}

	prism2sta_ev_info(wlandev, &inf);
failed:
	DBFEXIT;
	return;
}


/*----------------------------------------------------------------
* hfa384x_int_txexc
*
* Handles the TxExc event.  A Transmit Exception event indicates
* that the MAC's TX process was unsuccessful - so the packet did
* not get transmitted.
*
* Arguments:
*	wlandev		wlan device structure
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	tasklet  
----------------------------------------------------------------*/
void hfa384x_int_txexc(wlandevice_t *wlandev)
{
	hfa384x_t		*hw = wlandev->priv;
	UINT16			status;
	UINT16			fid;
	int			result = 0;
	DBFENTER;
	/* Collect the status and display */
	fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID);
	result = hfa384x_copy_from_bap(hw, HFA384x_BAP_INT, fid, 0, &status, sizeof(status));
	if ( result ) {
		WLAN_LOG_DEBUG(1, 
			"copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", 
			fid, sizeof(status), result);
		goto failed;
	}
	status = hfa384x2host_16(status);
	prism2sta_ev_txexc(wlandev, status);
failed:
	DBFEXIT;
	return;
}


/*----------------------------------------------------------------
* hfa384x_int_tx
*
* Handles the Tx event.
*
* Arguments:
*	wlandev		wlan device structure
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	tasklet  
----------------------------------------------------------------*/
void hfa384x_int_tx(wlandevice_t *wlandev)
{
	hfa384x_t		*hw = wlandev->priv;
	UINT16			fid;
	UINT16			status;
	int			result = 0;
	DBFENTER;
	fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID);
	result = hfa384x_copy_from_bap(hw, HFA384x_BAP_INT, fid, 0, &status, sizeof(status));
	if ( result ) {
		WLAN_LOG_DEBUG(1, 
			"copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", 
			fid, sizeof(status), result);
		goto failed;
	}
	status = hfa384x2host_16(status);
	prism2sta_ev_tx(wlandev, status);
failed:
	DBFEXIT;
	return;
}

/*----------------------------------------------------------------
* hfa384x_int_rx
*
* Handles the Rx event.
*
* Arguments:
*	wlandev		wlan device structure
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	tasklet  
----------------------------------------------------------------*/
void hfa384x_int_rx(wlandevice_t *wlandev)
{
	hfa384x_t		*hw = wlandev->priv;
	UINT16			rxfid;
	hfa384x_rx_frame_t	rxdesc;
	int			result;
	int                     hdrlen;
	UINT16                  fc;
	p80211_rxmeta_t	*rxmeta;
	struct sk_buff          *skb = NULL;
	UINT8 *datap;

	DBFENTER;

	/* Get the FID */
	rxfid = hfa384x_getreg(hw, HFA384x_RXFID);
	/* Get the descriptor (including headers) */
	result = hfa384x_copy_from_bap(hw, 
			HFA384x_BAP_INT, 
			rxfid, 
			0, 
			&rxdesc, 
			sizeof(rxdesc));
	if ( result ) {
		WLAN_LOG_DEBUG(1, 
			"copy_from_bap(0x%04x, %d, %d) failed, result=0x%x\n", 
			rxfid, 
			0, 
			sizeof(rxdesc),
			result);
		goto done;
	}

	/* Byte order convert once up front. */
	rxdesc.status =	hfa384x2host_16(rxdesc.status);
	rxdesc.time =	hfa384x2host_32(rxdesc.time);

	/* drop errors and whatnot in promisc mode */
	if (( wlandev->netdev->flags & IFF_PROMISC ) &&
	    (HFA384x_RXSTATUS_ISFCSERR(rxdesc.status) || 
	     HFA384x_RXSTATUS_ISUNDECR(rxdesc.status))) 
	  goto done;

	/* Now handle frame based on port# */
	switch( HFA384x_RXSTATUS_MACPORT_GET(rxdesc.status) )
	{
	case 0:

		fc = ieee2host16(rxdesc.frame_control);

		/* If exclude and we receive an unencrypted, drop it */
		if ( (wlandev->hostwep & HOSTWEP_EXCLUDEUNENCRYPTED) && 
		     !WLAN_GET_FC_ISWEP(fc)) {
			goto done;
		}

		hdrlen = p80211_headerlen(fc);

		/* Allocate the buffer, note CRC (aka FCS). pballoc */
		/* assumes there needs to be space for one */
		skb = dev_alloc_skb(hfa384x2host_16(rxdesc.data_len) + hdrlen + WLAN_CRC_LEN + 2); /* a little extra */

		if ( ! skb ) {
			WLAN_LOG_ERROR("alloc_skb failed.\n");
			goto done;
                }

		skb->dev = wlandev->netdev;

		/* theoretically align the IP header on a 32-bit word. */
		if ( hdrlen == WLAN_HDR_A4_LEN )
			skb_reserve(skb, 2);

		/* Copy the 802.11 hdr to the buffer */
		datap = skb_put(skb, WLAN_HDR_A3_LEN);
		memcpy(datap, &rxdesc.frame_control, WLAN_HDR_A3_LEN);
		
		/* Snag the A4 address if present */
		if (hdrlen == WLAN_HDR_A4_LEN) {
			datap = skb_put(skb, WLAN_ADDR_LEN);
			memcpy(datap, &rxdesc.address4, WLAN_HDR_A3_LEN);
		}

		/* we can convert the data_len as we passed the original on */
		rxdesc.data_len = hfa384x2host_16(rxdesc.data_len);

		/* Copy the payload data to the buffer */
		if ( rxdesc.data_len > 0 ) {
			datap = skb_put(skb, rxdesc.data_len);
			result = hfa384x_copy_from_bap(hw, 
				HFA384x_BAP_INT, rxfid, HFA384x_RX_DATA_OFF, 
				datap, rxdesc.data_len);
			if ( result ) {
				WLAN_LOG_DEBUG(1, 
					"copy_from_bap(0x%04x, %d, %d) failed, result=0x%x\n", 
					rxfid, 
					HFA384x_RX_DATA_OFF, 
					rxdesc.data_len,
					result);
				goto failed;
			}
		}
		/* the prism2 cards don't return the FCS */
		datap = skb_put(skb, WLAN_CRC_LEN);
		memset (datap, 0xff, WLAN_CRC_LEN);
		skb->mac.raw = skb->data;

		/* Attach the rxmeta, set some stuff */
		p80211skb_rxmeta_attach(wlandev, skb);
		rxmeta = P80211SKB_RXMETA(skb);
		rxmeta->mactime = rxdesc.time;
		rxmeta->rxrate = rxdesc.rate;
		rxmeta->signal = rxdesc.signal;
		rxmeta->noise = rxdesc.silence;

		prism2sta_ev_rx(wlandev, skb);
		goto done;
	case 7:

        	if ( ! HFA384x_RXSTATUS_ISFCSERR(rxdesc.status) ) {
                        hfa384x_int_rxmonitor( wlandev, rxfid, &rxdesc);
                } else {
                        WLAN_LOG_DEBUG(3,"Received monitor frame: FCSerr set\n");
                }
		goto done;

	default:

		WLAN_LOG_WARNING("Received frame on unsupported port=%d\n",
			HFA384x_RXSTATUS_MACPORT_GET(rxdesc.status) );
		goto done;
	}

 failed:
	dev_kfree_skb(skb);
	
 done:
	DBFEXIT;
	return;
}


/*----------------------------------------------------------------
* hfa384x_int_rxmonitor
*
* Helper function for int_rx.  Handles monitor frames.
* Note that this function allocates space for the FCS and sets it
* to 0xffffffff.  The hfa384x doesn't give us the FCS value but the
* higher layers expect it.  0xffffffff is used as a flag to indicate
* the FCS is bogus.
*
* Arguments:
*	wlandev		wlan device structure
*	rxfid		received FID
*	rxdesc		rx descriptor read from card in int_rx
*
* Returns: 
*	nothing
*
* Side effects:
*	Allocates an skb and passes it up via the PF_PACKET interface.
* Call context:
*	interrupt
----------------------------------------------------------------*/
void hfa384x_int_rxmonitor( wlandevice_t *wlandev, UINT16 rxfid, hfa384x_rx_frame_t *rxdesc)
{
	hfa384x_t			*hw = wlandev->priv;
	UINT				hdrlen = 0;
	UINT				datalen = 0;
	UINT				skblen = 0;
	UINT				truncated = 0;
	UINT8				*datap;
	UINT16				fc;
	struct sk_buff			*skb;

	DBFENTER;
	/* Don't forget the status, time, and data_len fields are in host order */
	/* Figure out how big the frame is */
	fc = ieee2host16(rxdesc->frame_control);
	hdrlen = p80211_headerlen(fc);
	datalen = hfa384x2host_16(rxdesc->data_len);

	/* Allocate an ind message+framesize skb */
	skblen = sizeof(p80211msg_lnxind_wlansniffrm_t) + 
		hdrlen + datalen + WLAN_CRC_LEN;
	
	/* sanity check the length */
	if ( skblen > 
		(sizeof(p80211msg_lnxind_wlansniffrm_t) + 
		WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN) ) {
		WLAN_LOG_DEBUG(1, "overlen frm: len=%d\n", 
			skblen - sizeof(p80211msg_lnxind_wlansniffrm_t));
	}
			
	if ( (skb = dev_alloc_skb(skblen)) == NULL ) {
		WLAN_LOG_ERROR("alloc_skb failed trying to allocate %d bytes\n", skblen);
		return;
	}

	/* only prepend the prism header if in the right mode */
	if ((wlandev->netdev->type == ARPHRD_IEEE80211_PRISM) &&
	    (hw->sniffhdr == 0)) {
		p80211msg_lnxind_wlansniffrm_t	*msg;
		datap = skb_put(skb, sizeof(p80211msg_lnxind_wlansniffrm_t));
		msg = (p80211msg_lnxind_wlansniffrm_t*) datap;
	  
		/* Initialize the message members */
		msg->msgcode = DIDmsg_lnxind_wlansniffrm;
		msg->msglen = sizeof(p80211msg_lnxind_wlansniffrm_t);
		strcpy(msg->devname, wlandev->name);
		
		msg->hosttime.did = DIDmsg_lnxind_wlansniffrm_hosttime;
		msg->hosttime.status = 0;
		msg->hosttime.len = 4;
		msg->hosttime.data = jiffies;
		
		msg->mactime.did = DIDmsg_lnxind_wlansniffrm_mactime;
		msg->mactime.status = 0;
		msg->mactime.len = 4;
		msg->mactime.data = rxdesc->time * 1000;
		
		msg->channel.did = DIDmsg_lnxind_wlansniffrm_channel;
		msg->channel.status = 0;
		msg->channel.len = 4;
		msg->channel.data = hw->sniff_channel;
		
		msg->rssi.did = DIDmsg_lnxind_wlansniffrm_rssi;
		msg->rssi.status = P80211ENUM_msgitem_status_no_value;
		msg->rssi.len = 4;
		msg->rssi.data = 0;
		
		msg->sq.did = DIDmsg_lnxind_wlansniffrm_sq;
		msg->sq.status = P80211ENUM_msgitem_status_no_value;
		msg->sq.len = 4;
		msg->sq.data = 0;
		
		msg->signal.did = DIDmsg_lnxind_wlansniffrm_signal;
		msg->signal.status = 0;
		msg->signal.len = 4;
		msg->signal.data = rxdesc->signal;
		
		msg->noise.did = DIDmsg_lnxind_wlansniffrm_noise;
		msg->noise.status = 0;
		msg->noise.len = 4;
		msg->noise.data = rxdesc->silence;
		
		msg->rate.did = DIDmsg_lnxind_wlansniffrm_rate;
		msg->rate.status = 0;
		msg->rate.len = 4;
		msg->rate.data = rxdesc->rate / 5; /* set to 802.11 units */
		
		msg->istx.did = DIDmsg_lnxind_wlansniffrm_istx;
		msg->istx.status = 0;
		msg->istx.len = 4;
		msg->istx.data = P80211ENUM_truth_false;
		
		msg->frmlen.did = DIDmsg_lnxind_wlansniffrm_frmlen;
		msg->frmlen.status = 0;
		msg->frmlen.len = 4;
		msg->frmlen.data = hdrlen + datalen + WLAN_CRC_LEN;
	} else if ((wlandev->netdev->type == ARPHRD_IEEE80211_PRISM) &&
		   (hw->sniffhdr != 0)) {
		p80211_caphdr_t		*caphdr;
		/* The NEW header format! */
		datap = skb_put(skb, sizeof(p80211_caphdr_t));
		caphdr = (p80211_caphdr_t*) datap;

		caphdr->version =	htonl(P80211CAPTURE_VERSION);
		caphdr->length =	htonl(sizeof(p80211_caphdr_t));
		caphdr->mactime =	__cpu_to_be64(rxdesc->time);
		caphdr->hosttime =	__cpu_to_be64(jiffies);
		caphdr->phytype =	htonl(4); /* dss_dot11_b */
		caphdr->channel =	htonl(hw->sniff_channel);
		caphdr->datarate =	htonl(rxdesc->rate);
		caphdr->antenna =	htonl(0); /* unknown */
		caphdr->priority =	htonl(0); /* unknown */
		caphdr->ssi_type =	htonl(3); /* rssi_raw */
		caphdr->ssi_signal =	htonl(rxdesc->signal);
		caphdr->ssi_noise =	htonl(rxdesc->silence);
		caphdr->preamble =	htonl(0); /* unknown */
		caphdr->encoding =	htonl(1); /* cck */
	}
	/* Copy the 802.11 header to the skb (ctl frames may be less than a full header) */
	datap = skb_put(skb, hdrlen);
	memcpy( datap, &(rxdesc->frame_control), hdrlen);

	/* If any, copy the data from the card to the skb */
	if ( datalen > 0 )
	{
		/* Truncate the packet if the user wants us to */
		UINT	dataread = datalen;
		if(hw->sniff_truncate > 0 && dataread > hw->sniff_truncate) {
			dataread = hw->sniff_truncate;
			truncated = 1;
		}

		datap = skb_put(skb, dataread);
		hfa384x_copy_from_bap(hw, 
			HFA384x_BAP_INT, rxfid, HFA384x_RX_DATA_OFF, 
			datap, dataread);

		/* check for unencrypted stuff if WEP bit set. */
		if (*(datap - hdrlen + 1) & 0x40) // wep set
		  if ((*(datap) == 0xaa) && (*(datap+1) == 0xaa))
		    *(datap - hdrlen + 1) &= 0xbf; // clear wep; it's the 802.2 header!
	}

	if (!truncated && hw->sniff_fcs) {
		/* Set the FCS */
		datap = skb_put(skb, WLAN_CRC_LEN);
		memset( datap, 0xff, WLAN_CRC_LEN);
	}

	/* pass it back up */
	prism2sta_ev_rx(wlandev, skb);

	DBFEXIT;
	return;
}

/*----------------------------------------------------------------
* hfa384x_int_alloc
*
* Handles the Alloc event.
*
* Arguments:
*	wlandev		wlan device structure
*
* Returns: 
*	nothing
*
* Side effects:
*
* Call context:
*	interrupt
----------------------------------------------------------------*/
void hfa384x_int_alloc(wlandevice_t *wlandev)
{
	hfa384x_t		*hw = wlandev->priv;
	UINT16			fid;
	INT16			result;

	DBFENTER;

	/* Handle the reclaimed FID */
	/*   collect the FID and push it onto the stack */
	fid = hfa384x_getreg(hw, HFA384x_ALLOCFID);

	if ( fid != hw->infofid ) { /* It's a transmit fid */
		WLAN_LOG_DEBUG(5, "int_alloc(%#x)\n", fid);
		result = txfid_queue_add(hw, fid);
		if (result != -1) {
			prism2sta_ev_alloc(wlandev);
			WLAN_LOG_DEBUG(5, "q_add.\n");
		} else {
			WLAN_LOG_DEBUG(5, "q_full.\n");
		}
	} else {
		/* unlock the info fid */
		up(&hw->infofid_sem);
	}

	DBFEXIT;
	return;
}


/*----------------------------------------------------------------
* hfa384x_drvr_handover
*
* Sends a handover notification to the MAC.
*
* Arguments:
*	hw		device structure
*	addr		address of station that's left
*
* Returns: 
*	zero		success.
*	-ERESTARTSYS	received signal while waiting for semaphore.
*	-EIO		failed to write to bap, or failed in cmd.
*
* Side effects:
*
* Call context:
*	process thread, NOTE: this call may block on a semaphore! 
----------------------------------------------------------------*/
int hfa384x_drvr_handover( hfa384x_t *hw, UINT8 *addr)
{
	int			result = 0;
        hfa384x_HandoverAddr_t  rec;
        UINT                    len;
        DBFENTER;

	/* Acquire the infofid */
	if ( down_interruptible(&hw->infofid_sem) ) {
		result = -ERESTARTSYS;
		goto failed;
	}

        /* Set up the record */
        len = sizeof(hfa384x_HandoverAddr_t);
        rec.framelen = host2hfa384x_16(len/2 - 1);
        rec.infotype = host2hfa384x_16(HFA384x_IT_HANDOVERADDR);
        memcpy(rec.handover_addr, addr, sizeof(rec.handover_addr));

        /* Issue the command */
        result = hfa384x_cmd_notify(hw, 1, hw->infofid, &rec, len);

        if ( result != 0 ) {
                WLAN_LOG_DEBUG(1,"cmd_notify(%04x) failed, result=%d",
                        hw->infofid, result);
		result = -EIO;
                goto failed;
        }

failed:
	DBFEXIT;
	return result;	
}

void hfa384x_tx_timeout(wlandevice_t *wlandev)
{
	DBFENTER;

	WLAN_LOG_WARNING("Implement me.\n");

	DBFEXIT;
}

/* Handles all "rx" BAP operations */
#ifdef DECLARE_TASKLET
static void     hfa384x_bap_tasklet(unsigned long data)
#else
static void     hfa384x_bap_tasklet(void *data)
#endif
{
	hfa384x_t *hw = (hfa384x_t *) data;
	wlandevice_t *wlandev = hw->wlandev;
	int counter = prism2_irq_evread_max;
	int			reg;

	DBFENTER;

	while (counter-- > 0) {
		/* Get interrupt register */
		reg = hfa384x_getreg(hw, HFA384x_EVSTAT);

		if ((reg == 0xffff) || 
		    !(reg & HFA384x_INT_BAP_OP)) {
			break;
		}

		if ( HFA384x_EVSTAT_ISINFO(reg) ){
			hfa384x_int_info(wlandev);
			hfa384x_setreg(hw, HFA384x_EVACK_INFO_SET(1),
				HFA384x_EVACK);
		}
		if ( HFA384x_EVSTAT_ISTXEXC(reg) ){
			hfa384x_int_txexc(wlandev);
			hfa384x_setreg(hw, HFA384x_EVACK_TXEXC_SET(1),
				HFA384x_EVACK);
		}
		if ( HFA384x_EVSTAT_ISTX(reg) ){
			hfa384x_int_tx(wlandev);
			hfa384x_setreg(hw, HFA384x_EVACK_TX_SET(1),
				HFA384x_EVACK);
		}
		if ( HFA384x_EVSTAT_ISRX(reg) ){
			hfa384x_int_rx(wlandev);
			hfa384x_setreg(hw, HFA384x_EVACK_RX_SET(1),
				HFA384x_EVACK);
		}
	}
         
	/* re-enable interrupts */
	hfa384x_events_all(hw);

	DBFEXIT;
}
