提交 c9b01df3 编写于 作者: S Solomon Peachy 提交者: Greg Kroah-Hartman

Staging: wlan-ng: Remove PCI/PLX/PCMCIA files.

This portion of the driver is not needed at all, so remove it.
Signed-off-by: NSolomon Peachy <pizza@shaftnet.org>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 f02466fc
...@@ -3,6 +3,5 @@ TODO: ...@@ -3,6 +3,5 @@ TODO:
- sparse warnings - sparse warnings
- Lindent cleanups - Lindent cleanups
- move to use the in-kernel wireless stack - move to use the in-kernel wireless stack
- possible enable the pcmcia and pci portions of the driver
Please send all patches to Greg Kroah-Hartman <greg@kroah.com> Please send all patches to Greg Kroah-Hartman <greg@kroah.com>
/* 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 "version.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)
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) )
#include <pcmcia/version.h>
#endif
#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_compat.h"
// XXXX #define CMD_IRQ
/*================================================================*/
/* Project Includes */
#include "p80211types.h"
#include "p80211hdr.h"
#include "p80211mgmt.h"
#include "p80211conv.h"
#include "p80211msg.h"
#include "p80211netdev.h"
#include "p80211req.h"
#include "p80211metadef.h"
#include "p80211metastruct.h"
#include "hfa384x.h"
#include "prism2mgmt.h"
/*================================================================*/
/* Local Constants */
static 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);
static void hfa384x_bap_tasklet(unsigned long data);
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 */
static UINT16
txfid_queue_empty(hfa384x_t *hw)
{
return (hw->txfid_head == hw->txfid_tail) ? 1 : 0;
}
static 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;
}
static 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 __iomem *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));
tasklet_init(&hw->bap_tasklet,
hfa384x_bap_tasklet,
(unsigned long) hw);
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_WORK2(&hw->link_bh, prism2sta_processing_defer);
INIT_WORK2(&hw->commsqual_bh, prism2sta_commsqual_defer);
init_timer(&hw->commsqual_timer);
hw->commsqual_timer.data = (unsigned long) hw;
hw->commsqual_timer.function = prism2sta_commsqual_timer;
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;
return;
}
/*----------------------------------------------------------------
* 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_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%08x,%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 %#x:%#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%08x 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 %d bytes to ram @0x%06x\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 %d bytes to flash @0x%06x\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 %d bytes to flash @0x%06x\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=%x) "
"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=%x) "
"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)0x5555)
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);
failed:
spin_unlock_bh(&hw->cmdlock);
DBFEXIT;
return result;
}
#if 0
/*----------------------------------------------------------------
* 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
----------------------------------------------------------------*/
static 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;
}
#endif
/*----------------------------------------------------------------
* 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.result.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;
unsigned long 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.result.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%08x\n", addr);
WLAN_LOG_DEBUG(1,"mmi write : data = 0x%08x\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%04x 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%04x 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
----------------------------------------------------------------*/
static 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->result.status = hfa384x_getreg(hw, HFA384x_STATUS);
cmd->result.resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
cmd->result.resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
cmd->result.resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
hfa384x_setreg(hw, HFA384x_EVACK_CMD,
HFA384x_EVACK);
result = HFA384x_STATUS_RESULT_GET(cmd->result.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
----------------------------------------------------------------*/
static int hfa384x_dl_docmd_wait( hfa384x_t *hw, hfa384x_metacmd_t *cmd)
{
int result = -ETIMEDOUT;
unsigned long 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->result.status = hfa384x_getreg(hw, HFA384x_STATUS);
cmd->result.resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
cmd->result.resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
cmd->result.resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
hfa384x_setreg(hw, HFA384x_EVACK_CMD, HFA384x_EVACK);
result = HFA384x_STATUS_RESULT_GET(cmd->result.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) {
WLAN_LOG_ERROR("Allocate(tx) command failed.\n");
goto failed;
}
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;
int i;
DBFENTER;
del_timer_sync(&hw->commsqual_timer);
if ( hw->wlandev->hwremoved ) {
/* only flush when we're shutting down for good */
flush_scheduled_work();
}
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);
}
tasklet_kill(&hw->bap_tasklet);
hw->link_status = HFA384x_LINK_NOTCONNECTED;
hw->state = HFA384x_STATE_INIT;
/* Clear all the port status */
for ( i = 0; i < HFA384x_NUMPORTS_MAX; i++) {
hw->port_enabled[i] = 0;
}
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 PT_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);
tasklet_schedule(&hw->bap_tasklet);
}
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
----------------------------------------------------------------*/
static 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
----------------------------------------------------------------*/
static 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
----------------------------------------------------------------*/
static 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
----------------------------------------------------------------*/
static 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
----------------------------------------------------------------*/
static 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
----------------------------------------------------------------*/
static 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_reset_mac_header(skb);
/* 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 - hw->dbmadjust;
rxmeta->noise = rxdesc.silence - hw->dbmadjust;
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
----------------------------------------------------------------*/
static 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
----------------------------------------------------------------*/
static 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 */
static void hfa384x_bap_tasklet(unsigned long data)
{
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;
}
#define WLAN_HOSTIF WLAN_PCMCIA
#include "hfa384x.c"
#include "prism2mgmt.c"
#include "prism2mib.c"
#include "prism2sta.c"
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,21) )
#if (WLAN_CPU_FAMILY == WLAN_Ix86)
#ifndef CONFIG_ISA
#warning "You may need to enable ISA support in your kernel."
#endif
#endif
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) )
static u_int irq_mask = 0xdeb8; /* Interrupt mask */
static int irq_list[4] = { -1 }; /* Interrupt list */
#endif
static u_int prism2_ignorevcc=1; /* Boolean, if set, we
* ignore what the Vcc
* is set to and what the CIS
* says.
*/
module_param( prism2_ignorevcc, int, 0644);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) )
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,9))
static int numlist = 4;
module_param_array(irq_list, int, numlist, 0444);
#else
module_param_array(irq_list, int, NULL, 0444);
#endif
module_param( irq_mask, int, 0644);
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)
static int prism2_cs_suspend(struct pcmcia_device *pdev);
static int prism2_cs_resume(struct pcmcia_device *pdev);
static void prism2_cs_remove(struct pcmcia_device *pdev);
static int prism2_cs_probe(struct pcmcia_device *pdev);
#else
dev_link_t *prism2sta_attach(void);
static void prism2sta_detach(dev_link_t *link);
static void prism2sta_config(dev_link_t *link);
static void prism2sta_release(u_long arg);
static int prism2sta_event (event_t event, int priority, event_callback_args_t *args);
static dev_link_t *dev_list = NULL; /* head of instance list */
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,68))
/*----------------------------------------------------------------
* cs_error
*
* Utility function to print card services error messages.
*
* Arguments:
* handle client handle identifying this CS client
* func CS function number that generated the error
* ret CS function return code
*
* Returns:
* nothing
* Side effects:
*
* Call context:
* process thread
* interrupt
----------------------------------------------------------------*/
static void cs_error(client_handle_t handle, int func, int ret)
{
#if (defined(CS_RELEASE_CODE) && (CS_RELEASE_CODE < 0x2911))
CardServices(ReportError, dev_info, (void *)func, (void *)ret);
#else
error_info_t err = { func, ret };
pcmcia_report_error(handle, &err);
#endif
}
#else // kernel_version
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,12)
static struct pcmcia_device_id prism2_cs_ids[] = {
PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18), // Intersil PRISM2 Reference Design 11Mb/s 802.11b WLAN Card
PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), // Compaq WL100/200 11Mb/s 802.11b WLAN Card
PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), // Compaq iPaq HNW-100 11Mb/s 802.11b WLAN Card
PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), // Samsung SWL2000-N 11Mb/s 802.11b WLAN Card
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), // Z-Com XI300 11Mb/s 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee), // ZoomAir 4100 11Mb/s 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0), // Linksys WPC11 11Mbps 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID123("Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02", 0xe6ec52ce, 0x8649af2, 0x4b74baa0), // Addtron AWP-100 11Mbps 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID123("D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02", 0x71b18589, 0xb6f1b0ab, 0x4b74baa0), // D-Link DWL-650 11Mbps 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID123("SMC", "SMC2632W", "Version 01.02", 0xc4f8b18b, 0x474a1f2a, 0x4b74baa0), // SMC 2632W 11Mbps 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID1234("Intersil", "PRISM 2_5 PCMCIA ADAPTER", "ISL37300P", "Eval-RevA", 0x4b801a17, 0x6345a0bf, 0xc9049a39, 0xc23adc0e), // BroMax Freeport 11Mbps 802.11b WLAN Card (Prism 2.5)
PCMCIA_DEVICE_PROD_ID123("U.S. Robotics", "IEEE 802.11b PC-CARD", "Version 01.02", 0xc7b8df9d, 0x1700d087, 0x4b74baa0), // U.S. Robotics IEEE 802.11b PC-CARD
PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146), // Level-One WPC-0100
PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), // Bromax OEM 11Mbps 802.11b WLAN Card (Prism 2.5)
PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), // Bromax OEM 11Mbps 802.11b WLAN Card (Prism 3)
PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584), // corega K.K. Wireless LAN PCC-11
PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9), // corega K.K. Wireless LAN PCCA-11
PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0008), // CONTEC FLEXSCAN/FX-DDS110-PCC
PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178), // PLANEX GeoWave/GW-NS110
PCMCIA_DEVICE_PROD_ID123("OEM", "PRISM2 IEEE 802.11 PC-Card", "Version 01.02", 0xfea54c90, 0x48f2bdd6, 0x4b74baa0), // Ambicom WL1100 11Mbps 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID123("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", "Version 01.02", 0x7e3b326a, 0x49893e92, 0x4b74baa0), // LeArtery SYNCBYAIR 11Mbps 802.11b WLAN Card
PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), // Intermec MobileLAN 11Mbps 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID123("NETGEAR MA401 Wireless PC", "Card", "Version 01.00", 0xa37434e9, 0x9762e8f1, 0xa57adb8c), // NETGEAR MA401 11Mbps 802.11 WLAN Card
PCMCIA_DEVICE_PROD_ID1234("Intersil", "PRISM Freedom PCMCIA Adapter", "ISL37100P", "Eval-RevA", 0x4b801a17, 0xf222ec2d, 0x630d52b2, 0xc23adc0e), // Intersil PRISM Freedom 11mbps 802.11 WLAN Card
PCMCIA_DEVICE_PROD_ID123("OTC", "Wireless AirEZY 2411-PCC WLAN Card", "Version 01.02", 0x4ac44287, 0x235a6bed, 0x4b74baa0), // OTC Wireless AirEZY 2411-PCC 11Mbps 802.11 WLAN Card
PCMCIA_DEVICE_PROD_ID1234("802.11", "11Mbps Wireless LAN Card", "v08C1", "" , 0xb67a610e, 0x655aa7b7, 0x264b451a, 0x0), // Dynalink L11HDT 11Mbps 802.11 WLAN Card
PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), // Dynalink L11HDT 11Mbps 802.11 WLAN Card
PCMCIA_DEVICE_PROD_ID12("PROXIM", "RangeLAN-DS/LAN PC CARD", 0xc6536a5e, 0x3f35797d), // PROXIM RangeLAN-DS/LAN PC CARD
PCMCIA_DEVICE_PROD_ID1234("ACTIONTEC", "PRISM Wireless LAN PC Card", "0381", "RevA", 0x393089da, 0xa71e69d5, 0x90471fa9, 0x57a66194), // ACTIONTEC PRISM Wireless LAN PC Card
PCMCIA_DEVICE_MANF_CARD(0x1668, 0x0101), // ACTIONTEC PRISM Wireless LAN PC Card
PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3), // 3Com AirConnect 3CRWE737A
PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE777A AirConnect Wireless LAN PCI Card" , 0x41240e5b, 0xafc7c33e), // 3Com AirConnect 3CRWE777A
PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842), // ASUS WL-100 802.11b WLAN PC Card
PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e), // ASUS WL-110 802.11b WLAN CF Card
PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18), // BUFFALO WLI-CF-S11G 802.11b WLAN Card
PCMCIA_DEVICE_PROD_ID1234("The Linksys Group, Inc.", "Wireless Network CF Card", "ISL37300P", "RevA", 0xa5f472c2, 0x9c05598d, 0xc9049a39, 0x57a66194), // Linksys WCF11 11Mbps 802.11b WLAN Card (Prism 2.5)
PCMCIA_DEVICE_PROD_ID1234("Linksys", "Wireless CompactFlash Card", "", "", 0x733cc81, 0xc52f395, 0x0, 0x0), // Linksys WCF12 11Mbps 802.11b WLAN Card (Prism 3)
PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), // Linksys WCF12 11Mbps 802.11b WLAN Card (Prism 3)
PCMCIA_DEVICE_PROD_ID1234("NETGEAR MA401RA Wireless PC", "Card", "ISL37300P", "Eval-RevA", 0x306467f, 0x9762e8f1, 0xc9049a39, 0xc23adc0e), // NETGEAR MA401RA 11Mbps 802.11 WLAN Card
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), // D-Link DCF-660W 11Mbps 802.11b WLAN Card
PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001), // Microsoft Wireless Notebook Adapter MN-520
PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), // AnyPoint(TM) Wireless II PC Card
PCMCIA_DEVICE_PROD_ID1234("D", "Link DRC-650 11Mbps WLAN Card", "Version 01.02", "" , 0x71b18589, 0xf144e3ac, 0x4b74baa0, 0x0), // D-Link DRC-650 802.11b WLAN Card
PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), // Adaptec AWN-8030
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7110), // D-Link DWL-650 rev P 802.11b WLAN card
// PCMCIA_DEVICE_PROD_ID1234("D-Link", "DWL-650 Wireless PC Card RevP", "ISL37101P-10", "A3", 0x1a424a1c, 0x6ea57632, 0xdd97a26b, 0x56b21f52), // D-Link DWL-650 rev P 802.11b WLAN card
PCMCIA_DEVICE_PROD_ID123("INTERSIL", "I-GATE 11M PC Card / PC Card plus", "Version 01.02", 0x74c5e40d, 0x8304ff77, 0x4b74baa0), // I-Gate 11M PC Card
PCMCIA_DEVICE_PROD_ID1234("BENQ", "AWL100 PCMCIA ADAPTER", "ISL37300P", "Eval-RevA", 0x35dadc74, 0x1f7fedb, 0xc9049a39, 0xc23adc0e), // benQ AWL100 802.11b WLAN Card
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), // benQ AWL100 802.11b WLAN Card
// PCMCIA_DEVICE_PROD_ID1("INTERSIL", 0x74c5e40d), // Intersil Prism 2 card
// PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), // Intersil Prism 2 card
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, prism2_cs_ids);
#endif
static struct pcmcia_driver prism2_cs_driver = {
.drv = {
.name = "prism2_cs",
},
.owner = THIS_MODULE,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)
.suspend = prism2_cs_suspend,
.resume = prism2_cs_resume,
.remove = prism2_cs_remove,
.probe = prism2_cs_probe,
.id_table = prism2_cs_ids,
#else
.attach = prism2sta_attach,
.detach = prism2sta_detach,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,12)
.id_table = prism2_cs_ids,
.event = prism2sta_event,
#endif // > 2.6.12
#endif // <= 2.6.15
};
#endif /* kernel_version */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
#define CFG_CHECK(fn, retf) \
do { int ret = (retf); \
if (ret != 0) { \
WLAN_LOG_DEBUG(1, "CardServices(" #fn ") returned %d\n", ret); \
cs_error(pdev, fn, ret); \
goto next_entry; \
} \
} while (0)
static void prism2_cs_remove(struct pcmcia_device *pdev)
{
struct wlandevice *wlandev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
dev_link_t *link = dev_to_instance(pdev);
#endif
DBFENTER;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
wlandev = pdev->priv;
#else
wlandev = link->priv;
#endif
if (wlandev) {
p80211netdev_hwremoved(wlandev);
unregister_wlandev(wlandev);
wlan_unsetup(wlandev);
if (wlandev->priv) {
hfa384x_t *hw = wlandev->priv;
wlandev->priv = NULL;
if (hw) {
hfa384x_destroy(hw);
kfree(hw);
}
}
kfree(wlandev);
}
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->priv = NULL;
pcmcia_disable_device(pdev);
#else
if (link->state & DEV_CONFIG) {
if (link->win)
pcmcia_release_window(link->win);
pcmcia_release_configuration(link->handle);
if (link->io.NumPorts1)
pcmcia_release_io(link->handle, &link->io);
if (link->irq.AssignedIRQ)
pcmcia_release_irq(link->handle, &link->irq);
link->state &= ~DEV_CONFIG;
}
link->priv = NULL;
kfree(link);
#endif
DBFEXIT;
return;
}
static int prism2_cs_suspend(struct pcmcia_device *pdev)
{
struct wlandevice *wlandev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
dev_link_t *link = dev_to_instance(pdev);
#endif
DBFENTER;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
wlandev = pdev->priv;
prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
#else
wlandev = link->priv;
link->state |= DEV_SUSPEND;
if (link->state & DEV_CONFIG) {
prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
pcmcia_release_configuration(link->handle);
}
#endif
DBFEXIT;
return 0;
}
static int prism2_cs_resume(struct pcmcia_device *pdev)
{
struct wlandevice *wlandev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
dev_link_t *link = dev_to_instance(pdev);
#endif
DBFENTER;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
wlandev = pdev->priv;
// XXX do something here?
#else
wlandev = link->priv;
link->state &= ~DEV_SUSPEND;
if (link->state & DEV_CONFIG) {
pcmcia_request_configuration(link->handle, &link->conf);
// XXX do something here?
}
#endif
DBFEXIT;
return 0;
}
static int prism2_cs_probe(struct pcmcia_device *pdev)
{
int rval = 0;
struct wlandevice *wlandev = NULL;
hfa384x_t *hw = NULL;
config_info_t socketconf;
cisparse_t *parse = NULL;
tuple_t tuple;
uint8_t buf[64];
int last_fn, last_ret;
cistpl_cftable_entry_t dflt = { 0 };
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
dev_link_t *link;
#endif
DBFENTER;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
/* Set up interrupt type */
pdev->conf.IntType = INT_MEMORY_AND_IO;
#else
link = kmalloc(sizeof(dev_link_t), GFP_KERNEL);
if (link == NULL)
return -ENOMEM;
memset(link, 0, sizeof(dev_link_t));
link->conf.Vcc = 33;
link->conf.IntType = INT_MEMORY_AND_IO;
link->handle = pdev;
pdev->instance = link;
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
#endif
// VCC crap?
parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
wlandev = create_wlan();
if (!wlandev || !parse) {
WLAN_LOG_ERROR("%s: Memory allocation failure.\n", dev_info);
rval = -EIO;
goto failed;
}
hw = wlandev->priv;
if ( wlan_setup(wlandev) != 0 ) {
WLAN_LOG_ERROR("%s: wlan_setup() failed.\n", dev_info);
rval = -EIO;
goto failed;
}
/* Initialize the hw struct for now */
hfa384x_create(hw, 0, 0, NULL);
hw->wlandev = wlandev;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
hw->pdev = pdev;
pdev->priv = wlandev;
#else
hw->link = link;
link->priv = wlandev;
#endif
tuple.DesiredTuple = CISTPL_CONFIG;
tuple.Attributes = 0;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
tuple.TupleOffset = 0;
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(pdev, &tuple));
CS_CHECK(GetTupleData, pcmcia_get_tuple_data(pdev, &tuple));
CS_CHECK(ParseTuple, pcmcia_parse_tuple(pdev, &tuple, parse));
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->conf.ConfigBase = parse->config.base;
pdev->conf.Present = parse->config.rmask[0];
#else
link->conf.ConfigBase = parse->config.base;
link->conf.Present = parse->config.rmask[0];
link->conf.Vcc = socketconf.Vcc;
#endif
CS_CHECK(GetConfigurationInfo,
pcmcia_get_configuration_info(pdev, &socketconf));
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(pdev, &tuple));
for (;;) {
cistpl_cftable_entry_t *cfg = &(parse->cftable_entry);
CFG_CHECK(GetTupleData,
pcmcia_get_tuple_data(pdev, &tuple));
CFG_CHECK(ParseTuple,
pcmcia_parse_tuple(pdev, &tuple, parse));
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
dflt = *cfg;
if (cfg->index == 0)
goto next_entry;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->conf.ConfigIndex = cfg->index;
#else
link->conf.ConfigIndex = cfg->index;
#endif
/* Does this card need audio output? */
if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->conf.Attributes |= CONF_ENABLE_SPKR;
pdev->conf.Status = CCSR_AUDIO_ENA;
#else
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
#endif
}
/* Use power settings for Vcc and Vpp if present */
/* Note that the CIS values need to be rescaled */
if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
if (socketconf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] /
10000 && !prism2_ignorevcc) {
WLAN_LOG_DEBUG(1, " Vcc mismatch - skipping"
" this entry\n");
goto next_entry;
}
} else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
if (socketconf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] /
10000 && !prism2_ignorevcc) {
WLAN_LOG_DEBUG(1, " Vcc (default) mismatch "
"- skipping this entry\n");
goto next_entry;
}
}
if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->conf.Vpp =
cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
#else
link->conf.Vpp1 = link->conf.Vpp2 =
cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
#endif
} else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->conf.Vpp =
dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
#else
link->conf.Vpp1 = link->conf.Vpp2 =
dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
#endif
}
/* Do we need to allocate an interrupt? */
/* HACK: due to a bad CIS....we ALWAYS need an interrupt */
/* if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->conf.Attributes |= CONF_ENABLE_IRQ;
#else
link->conf.Attributes |= CONF_ENABLE_IRQ;
#endif
/* IO window settings */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->io.NumPorts1 = pdev->io.NumPorts2 = 0;
if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
pdev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
if (!(io->flags & CISTPL_IO_8BIT))
pdev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
if (!(io->flags & CISTPL_IO_16BIT))
pdev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
pdev->io.BasePort1 = io->win[0].base;
if ( pdev->io.BasePort1 != 0 ) {
WLAN_LOG_WARNING(
"Brain damaged CIS: hard coded iobase="
"0x%x, try letting pcmcia_cs decide...\n",
pdev->io.BasePort1 );
pdev->io.BasePort1 = 0;
}
pdev->io.NumPorts1 = io->win[0].len;
if (io->nwin > 1) {
pdev->io.Attributes2 = pdev->io.Attributes1;
pdev->io.BasePort2 = io->win[1].base;
pdev->io.NumPorts2 = io->win[1].len;
}
}
/* This reserves IO space but doesn't actually enable it */
CFG_CHECK(RequestIO, pcmcia_request_io(pdev, &pdev->io));
#else
link->io.NumPorts1 = link->io.NumPorts2 = 0;
if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
if (!(io->flags & CISTPL_IO_8BIT))
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
if (!(io->flags & CISTPL_IO_16BIT))
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.BasePort1 = io->win[0].base;
if ( link->io.BasePort1 != 0 ) {
WLAN_LOG_WARNING(
"Brain damaged CIS: hard coded iobase="
"0x%x, try letting pcmcia_cs decide...\n",
link->io.BasePort1 );
link->io.BasePort1 = 0;
}
link->io.NumPorts1 = io->win[0].len;
if (io->nwin > 1) {
link->io.Attributes2 = link->io.Attributes1;
link->io.BasePort2 = io->win[1].base;
link->io.NumPorts2 = io->win[1].len;
}
}
/* This reserves IO space but doesn't actually enable it */
CFG_CHECK(RequestIO, pcmcia_request_io(pdev, &link->io));
#endif
/* If we got this far, we're cool! */
break;
next_entry:
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
dflt = *cfg;
CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(pdev, &tuple));
}
/* Let pcmcia know the device name */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->dev_node = &hw->node;
#else
link->dev = &hw->node;
#endif
/* Register the network device and get assigned a name */
SET_MODULE_OWNER(wlandev->netdev);
SET_NETDEV_DEV(wlandev->netdev, &handle_to_dev(pdev));
if (register_wlandev(wlandev) != 0) {
WLAN_LOG_NOTICE("prism2sta_cs: register_wlandev() failed.\n");
goto failed;
}
strcpy(hw->node.dev_name, wlandev->name);
/* Allocate an interrupt line. Note that this does not assign a */
/* handler to the interrupt, unless the 'Handler' member of the */
/* irq structure is initialized. */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
if (pdev->conf.Attributes & CONF_ENABLE_IRQ) {
pdev->irq.IRQInfo1 = IRQ_LEVEL_ID;
pdev->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
pdev->irq.Handler = hfa384x_interrupt;
pdev->irq.Instance = wlandev;
CS_CHECK(RequestIRQ, pcmcia_request_irq(pdev, &pdev->irq));
}
#else
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
link->irq.Handler = hfa384x_interrupt;
link->irq.Instance = wlandev;
CS_CHECK(RequestIRQ, pcmcia_request_irq(pdev, &link->irq));
}
#endif
/* This actually configures the PCMCIA socket -- setting up */
/* the I/O windows and the interrupt mapping, and putting the */
/* card and host interface into "Memory and IO" mode. */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(pdev, &pdev->conf));
#else
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(pdev, &link->conf));
#endif
/* Fill the netdevice with this info */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
wlandev->netdev->irq = pdev->irq.AssignedIRQ;
wlandev->netdev->base_addr = pdev->io.BasePort1;
#else
wlandev->netdev->irq = link->irq.AssignedIRQ;
wlandev->netdev->base_addr = link->io.BasePort1;
#endif
/* And the rest of the hw structure */
hw->irq = wlandev->netdev->irq;
hw->iobase = wlandev->netdev->base_addr;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
link->state |= DEV_CONFIG;
link->state &= ~DEV_CONFIG_PENDING;
#endif
/* And now we're done! */
wlandev->msdstate = WLAN_MSD_HWPRESENT;
goto done;
cs_failed:
cs_error(pdev, last_fn, last_ret);
failed:
// wlandev, hw, etc etc..
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
pdev->priv = NULL;
#else
pdev->instance = NULL;
if (link) {
link->priv = NULL;
kfree(link);
}
#endif
if (wlandev) {
wlan_unsetup(wlandev);
if (wlandev->priv) {
hw = wlandev->priv;
wlandev->priv = NULL;
if (hw) {
hfa384x_destroy(hw);
kfree(hw);
}
}
kfree(wlandev);
}
done:
if (parse) kfree(parse);
DBFEXIT;
return rval;
}
#else // <= 2.6.15
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
#define CFG_CHECK(fn, retf) \
do { int ret = (retf); \
if (ret != 0) { \
WLAN_LOG_DEBUG(1, "CardServices(" #fn ") returned %d\n", ret); \
cs_error(link->handle, fn, ret); \
goto next_entry; \
} \
} while (0)
/*----------------------------------------------------------------
* prism2sta_attach
*
* Half of the attach/detach pair. Creates and registers a device
* instance with Card Services. In this case, it also creates the
* wlandev structure and device private structure. These are
* linked to the device instance via its priv member.
*
* Arguments:
* none
*
* Returns:
* A valid ptr to dev_link_t on success, NULL otherwise
*
* Side effects:
*
*
* Call context:
* process thread (insmod/init_module/register_pccard_driver)
----------------------------------------------------------------*/
dev_link_t *prism2sta_attach(void)
{
client_reg_t client_reg;
int result;
dev_link_t *link = NULL;
wlandevice_t *wlandev = NULL;
hfa384x_t *hw = NULL;
DBFENTER;
/* Alloc our structures */
link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
if (!link || ((wlandev = create_wlan()) == NULL)) {
WLAN_LOG_ERROR("%s: Memory allocation failure.\n", dev_info);
result = -EIO;
goto failed;
}
hw = wlandev->priv;
/* Clear all the structs */
memset(link, 0, sizeof(struct dev_link_t));
if ( wlan_setup(wlandev) != 0 ) {
WLAN_LOG_ERROR("%s: wlan_setup() failed.\n", dev_info);
result = -EIO;
goto failed;
}
/* Initialize the hw struct for now */
hfa384x_create(hw, 0, 0, NULL);
hw->wlandev = wlandev;
/* Initialize the PC card device object. */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
init_timer(&link->release);
link->release.function = &prism2sta_release;
link->release.data = (u_long)link;
#endif
link->conf.IntType = INT_MEMORY_AND_IO;
link->priv = wlandev;
#if (defined(CS_RELEASE_CODE) && (CS_RELEASE_CODE < 0x2911))
link->irq.Instance = wlandev;
#endif
/* Link in to the list of devices managed by this driver */
link->next = dev_list;
dev_list = link;
/* Register with Card Services */
client_reg.dev_info = &dev_info;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) )
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) )
client_reg.EventMask =
CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
CS_EVENT_RESET_REQUEST |
CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
client_reg.event_handler = &prism2sta_event;
#endif
client_reg.Version = 0x0210;
client_reg.event_callback_args.client_data = link;
result = pcmcia_register_client(&link->handle, &client_reg);
if (result != 0) {
cs_error(link->handle, RegisterClient, result);
prism2sta_detach(link);
return NULL;
}
goto done;
failed:
if (link) kfree(link);
if (wlandev) kfree(wlandev);
if (hw) kfree(hw);
link = NULL;
done:
DBFEXIT;
return link;
}
/*----------------------------------------------------------------
* prism2sta_detach
*
* Remove one of the device instances managed by this driver.
* Search the list for the given instance,
* check our flags for a waiting timer'd release call
* call release
* Deregister the instance with Card Services
* (netdevice) unregister the network device.
* unlink the instance from the list
* free the link, priv, and priv->priv memory
* Note: the dev_list variable is a driver scoped static used to
* maintain a list of device instances managed by this
* driver.
*
* Arguments:
* link ptr to the instance to detach
*
* Returns:
* nothing
*
* Side effects:
* the link structure is gone, the netdevice is gone
*
* Call context:
* Might be interrupt, don't block.
----------------------------------------------------------------*/
void prism2sta_detach(dev_link_t *link)
{
dev_link_t **linkp;
wlandevice_t *wlandev;
hfa384x_t *hw;
DBFENTER;
/* Locate prev device structure */
for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) {
if (*linkp == link) break;
}
if (*linkp != NULL) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0))
unsigned long flags;
/* Get rid of any timer'd release call */
save_flags(flags);
cli();
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
if (link->state & DEV_RELEASE_PENDING) {
del_timer_sync(&link->release);
link->state &= ~DEV_RELEASE_PENDING;
}
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0))
restore_flags(flags);
#endif
/* If link says we're still config'd, call release */
if (link->state & DEV_CONFIG) {
prism2sta_release((u_long)link);
if (link->state & DEV_STALE_CONFIG) {
link->state |= DEV_STALE_LINK;
return;
}
}
/* Tell Card Services we're not around any more */
if (link->handle) {
pcmcia_deregister_client(link->handle);
}
/* Unlink device structure, free bits */
*linkp = link->next;
if ( link->priv != NULL ) {
wlandev = (wlandevice_t*)link->priv;
p80211netdev_hwremoved(wlandev);
if (link->dev != NULL) {
unregister_wlandev(wlandev);
}
wlan_unsetup(wlandev);
if (wlandev->priv) {
hw = wlandev->priv;
wlandev->priv = NULL;
if (hw) {
hfa384x_destroy(hw);
kfree(hw);
}
}
link->priv = NULL;
kfree(wlandev);
}
kfree(link);
}
DBFEXIT;
return;
}
/*----------------------------------------------------------------
* prism2sta_config
*
* Half of the config/release pair. Usually called in response to
* a card insertion event. At this point, we _know_ there's some
* physical device present. That means we can start poking around
* at the CIS and at any device specific config data we want.
*
* Note the gotos and the macros. I recoded this once without
* them, and it got incredibly ugly. It's actually simpler with
* them.
*
* Arguments:
* link the dev_link_t structure created in attach that
* represents this device instance.
*
* Returns:
* nothing
*
* Side effects:
* Resources (irq, io, mem) are allocated
* The pcmcia dev_link->node->name is set
* (For netcards) The device structure is finished and,
* most importantly, registered. This means that there
* is now a _named_ device that can be configured from
* userland.
*
* Call context:
* May be called from a timer. Don't block!
----------------------------------------------------------------*/
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
#define CFG_CHECK(fn, retf) \
do { int ret = (retf); \
if (ret != 0) { \
WLAN_LOG_DEBUG(1, "CardServices(" #fn ") returned %d\n", ret); \
cs_error(link->handle, fn, ret); \
goto next_entry; \
} \
} while (0)
void prism2sta_config(dev_link_t *link)
{
client_handle_t handle;
wlandevice_t *wlandev;
hfa384x_t *hw;
int last_fn;
int last_ret;
tuple_t tuple;
cisparse_t parse;
config_info_t socketconf;
UINT8 buf[64];
int minVcc = 0;
int maxVcc = 0;
cistpl_cftable_entry_t dflt = { 0 };
DBFENTER;
handle = link->handle;
wlandev = (wlandevice_t*)link->priv;
hw = wlandev->priv;
/* Collect the config register info */
tuple.DesiredTuple = CISTPL_CONFIG;
tuple.Attributes = 0;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
tuple.TupleOffset = 0;
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
link->conf.ConfigBase = parse.config.base;
link->conf.Present = parse.config.rmask[0];
/* Configure card */
link->state |= DEV_CONFIG;
/* Acquire the current socket config (need Vcc setting) */
CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &socketconf));
/* Loop through the config table entries until we find one that works */
/* Assumes a complete and valid CIS */
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
while (1) {
cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
CFG_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
CFG_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
if (cfg->index == 0) goto next_entry;
link->conf.ConfigIndex = cfg->index;
/* Lets print out the Vcc that the controller+pcmcia-cs set
* for us, cause that's what we're going to use.
*/
WLAN_LOG_DEBUG(1,"Initial Vcc=%d/10v\n", socketconf.Vcc);
if (prism2_ignorevcc) {
link->conf.Vcc = socketconf.Vcc;
goto skipvcc;
}
/* Use power settings for Vcc and Vpp if present */
/* Note that the CIS values need to be rescaled */
if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
WLAN_LOG_DEBUG(1, "Vcc obtained from curtupl.VNOM\n");
minVcc = maxVcc =
cfg->vcc.param[CISTPL_POWER_VNOM]/10000;
} else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
WLAN_LOG_DEBUG(1, "Vcc set from dflt.VNOM\n");
minVcc = maxVcc =
dflt.vcc.param[CISTPL_POWER_VNOM]/10000;
} else if ((cfg->vcc.present & (1<<CISTPL_POWER_VMAX)) &&
(cfg->vcc.present & (1<<CISTPL_POWER_VMIN)) ) {
WLAN_LOG_DEBUG(1, "Vcc set from curtupl(VMIN,VMAX)\n"); minVcc = cfg->vcc.param[CISTPL_POWER_VMIN]/10000;
maxVcc = cfg->vcc.param[CISTPL_POWER_VMAX]/10000;
} else if ((dflt.vcc.present & (1<<CISTPL_POWER_VMAX)) &&
(dflt.vcc.present & (1<<CISTPL_POWER_VMIN)) ) {
WLAN_LOG_DEBUG(1, "Vcc set from dflt(VMIN,VMAX)\n");
minVcc = dflt.vcc.param[CISTPL_POWER_VMIN]/10000;
maxVcc = dflt.vcc.param[CISTPL_POWER_VMAX]/10000;
}
if ( socketconf.Vcc >= minVcc && socketconf.Vcc <= maxVcc) {
link->conf.Vcc = socketconf.Vcc;
} else {
/* [MSM]: Note that I've given up trying to change
* the Vcc if a change is indicated. It seems the
* system&socketcontroller&card vendors can't seem
* to get it right, so I'm tired of trying to hack
* my way around it. pcmcia-cs does its best using
* the voltage sense pins but sometimes the controller
* lies. Then, even if we have a good read on the VS
* pins, some system designs will silently ignore our
* requests to set the voltage. Additionally, some
* vendors have 3.3v indicated on their sense pins,
* but 5v specified in the CIS or vice-versa. I've
* had it. My only recommendation is "let the buyer
* beware". Your system might supply 5v to a 3v card
* (possibly causing damage) or a 3v capable system
* might supply 5v to a 3v capable card (wasting
* precious battery life).
* My only recommendation (if you care) is to get
* yourself an extender card (I don't know where, I
* have only one myself) and a meter and test it for
* yourself.
*/
goto next_entry;
}
skipvcc:
WLAN_LOG_DEBUG(1, "link->conf.Vcc=%d\n", link->conf.Vcc);
/* Do we need to allocate an interrupt? */
/* HACK: due to a bad CIS....we ALWAYS need an interrupt */
/* if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) */
link->conf.Attributes |= CONF_ENABLE_IRQ;
/* IO window settings */
link->io.NumPorts1 = link->io.NumPorts2 = 0;
if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
if (!(io->flags & CISTPL_IO_8BIT))
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
if (!(io->flags & CISTPL_IO_16BIT))
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.BasePort1 = io->win[0].base;
if ( link->io.BasePort1 != 0 ) {
WLAN_LOG_WARNING(
"Brain damaged CIS: hard coded iobase="
"0x%x, try letting pcmcia_cs decide...\n",
link->io.BasePort1 );
link->io.BasePort1 = 0;
}
link->io.NumPorts1 = io->win[0].len;
if (io->nwin > 1) {
link->io.Attributes2 = link->io.Attributes1;
link->io.BasePort2 = io->win[1].base;
link->io.NumPorts2 = io->win[1].len;
}
}
/* This reserves IO space but doesn't actually enable it */
CFG_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io));
/* If we got this far, we're cool! */
break;
next_entry:
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
dflt = *cfg;
CS_CHECK(GetNextTuple,
pcmcia_get_next_tuple(handle, &tuple));
}
/* Allocate an interrupt line. Note that this does not assign a */
/* handler to the interrupt, unless the 'Handler' member of the */
/* irq structure is initialized. */
if (link->conf.Attributes & CONF_ENABLE_IRQ)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) )
int i;
link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
if (irq_list[0] == -1)
link->irq.IRQInfo2 = irq_mask;
else
for (i=0; i<4; i++)
link->irq.IRQInfo2 |= 1 << irq_list[i];
#else
link->irq.IRQInfo1 = IRQ_LEVEL_ID;
#endif
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
link->irq.Handler = hfa384x_interrupt;
link->irq.Instance = wlandev;
CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
}
/* This actually configures the PCMCIA socket -- setting up */
/* the I/O windows and the interrupt mapping, and putting the */
/* card and host interface into "Memory and IO" mode. */
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
/* Fill the netdevice with this info */
wlandev->netdev->irq = link->irq.AssignedIRQ;
wlandev->netdev->base_addr = link->io.BasePort1;
/* Report what we've done */
WLAN_LOG_INFO("%s: index 0x%02x: Vcc %d.%d",
dev_info, link->conf.ConfigIndex,
link->conf.Vcc/10, link->conf.Vcc%10);
if (link->conf.Vpp1)
printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
if (link->conf.Attributes & CONF_ENABLE_IRQ)
printk(", irq %d", link->irq.AssignedIRQ);
if (link->io.NumPorts1)
printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1);
if (link->io.NumPorts2)
printk(" & 0x%04x-0x%04x", link->io.BasePort2, link->io.BasePort2+link->io.NumPorts2-1);
printk("\n");
link->state &= ~DEV_CONFIG_PENDING;
/* Let pcmcia know the device name */
link->dev = &hw->node;
/* Register the network device and get assigned a name */
SET_MODULE_OWNER(wlandev->netdev);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) )
SET_NETDEV_DEV(wlandev->netdev, &handle_to_dev(link->handle));
#endif
if (register_wlandev(wlandev) != 0) {
WLAN_LOG_NOTICE("prism2sta_cs: register_wlandev() failed.\n");
goto failed;
}
strcpy(hw->node.dev_name, wlandev->name);
/* Any device custom config/query stuff should be done here */
/* For a netdevice, we should at least grab the mac address */
return;
cs_failed:
cs_error(link->handle, last_fn, last_ret);
WLAN_LOG_ERROR("NextTuple failure? It's probably a Vcc mismatch.\n");
failed:
prism2sta_release((u_long)link);
return;
}
/*----------------------------------------------------------------
* prism2sta_release
*
* Half of the config/release pair. Usually called in response to
* a card ejection event. Checks to make sure no higher layers
* are still (or think they are) using the card via the link->open
* field.
*
* NOTE: Don't forget to increment the link->open variable in the
* device_open method, and decrement it in the device_close
* method.
*
* Arguments:
* arg a generic 32 bit variable. It's the value that
* we assigned to link->release.data in sta_attach().
*
* Returns:
* nothing
*
* Side effects:
* All resources should be released after this function
* executes and finds the device !open.
*
* Call context:
* Possibly in a timer context. Don't do anything that'll
* block.
----------------------------------------------------------------*/
void prism2sta_release(u_long arg)
{
dev_link_t *link = (dev_link_t *)arg;
DBFENTER;
/* First thing we should do is get the MSD back to the
* HWPRESENT state. I.e. everything quiescent.
*/
prism2sta_ifstate(link->priv, P80211ENUM_ifstate_disable);
if (link->open) {
/* TODO: I don't think we're even using this bit of code
* and I don't think it's hurting us at the moment.
*/
WLAN_LOG_DEBUG(1,
"prism2sta_cs: release postponed, '%s' still open\n",
link->dev->dev_name);
link->state |= DEV_STALE_CONFIG;
return;
}
pcmcia_release_configuration(link->handle);
pcmcia_release_io(link->handle, &link->io);
pcmcia_release_irq(link->handle, &link->irq);
link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
DBFEXIT;
}
/*----------------------------------------------------------------
* prism2sta_event
*
* Handler for card services events.
*
* Arguments:
* event The event code
* priority hi/low - REMOVAL is the only hi
* args ptr to card services struct containing info about
* pcmcia status
*
* Returns:
* Zero on success, non-zero otherwise
*
* Side effects:
*
*
* Call context:
* Both interrupt and process thread, depends on the event.
----------------------------------------------------------------*/
static int
prism2sta_event (
event_t event,
int priority,
event_callback_args_t *args)
{
int result = 0;
dev_link_t *link = (dev_link_t *) args->client_data;
wlandevice_t *wlandev = (wlandevice_t*)link->priv;
hfa384x_t *hw = NULL;
DBFENTER;
if (wlandev) hw = wlandev->priv;
switch (event)
{
case CS_EVENT_CARD_INSERTION:
WLAN_LOG_DEBUG(5,"event is INSERTION\n");
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
prism2sta_config(link);
if (!(link->state & DEV_CONFIG)) {
wlandev->netdev->irq = 0;
WLAN_LOG_ERROR(
"%s: Initialization failed!\n", dev_info);
wlandev->msdstate = WLAN_MSD_HWFAIL;
break;
}
/* Fill in the rest of the hw struct */
hw->irq = wlandev->netdev->irq;
hw->iobase = wlandev->netdev->base_addr;
hw->link = link;
if (prism2_doreset) {
result = hfa384x_corereset(hw,
prism2_reset_holdtime,
prism2_reset_settletime, 0);
if ( result ) {
WLAN_LOG_ERROR(
"corereset() failed, result=%d.\n",
result);
wlandev->msdstate = WLAN_MSD_HWFAIL;
break;
}
}
#if 0
/*
* TODO: test_hostif() not implemented yet.
*/
result = hfa384x_test_hostif(hw);
if (result) {
WLAN_LOG_ERROR(
"test_hostif() failed, result=%d.\n", result);
wlandev->msdstate = WLAN_MSD_HWFAIL;
break;
}
#endif
wlandev->msdstate = WLAN_MSD_HWPRESENT;
break;
case CS_EVENT_CARD_REMOVAL:
WLAN_LOG_DEBUG(5,"event is REMOVAL\n");
link->state &= ~DEV_PRESENT;
if (wlandev) {
p80211netdev_hwremoved(wlandev);
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
if (link->state & DEV_CONFIG)
{
link->release.expires = jiffies + (HZ/20);
add_timer(&link->release);
}
#endif
break;
case CS_EVENT_RESET_REQUEST:
WLAN_LOG_DEBUG(5,"event is RESET_REQUEST\n");
WLAN_LOG_NOTICE(
"prism2 card reset not supported "
"due to post-reset user mode configuration "
"requirements.\n");
WLAN_LOG_NOTICE(
" From user mode, use "
"'cardctl suspend;cardctl resume' "
"instead.\n");
break;
case CS_EVENT_RESET_PHYSICAL:
case CS_EVENT_CARD_RESET:
WLAN_LOG_WARNING("Rx'd CS_EVENT_RESET_xxx, should not "
"be possible since RESET_REQUEST was denied.\n");
break;
case CS_EVENT_PM_SUSPEND:
WLAN_LOG_DEBUG(5,"event is SUSPEND\n");
link->state |= DEV_SUSPEND;
if (link->state & DEV_CONFIG)
{
prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
pcmcia_release_configuration(link->handle);
}
break;
case CS_EVENT_PM_RESUME:
WLAN_LOG_DEBUG(5,"event is RESUME\n");
link->state &= ~DEV_SUSPEND;
if (link->state & DEV_CONFIG) {
pcmcia_request_configuration(link->handle, &link->conf);
}
break;
}
DBFEXIT;
return 0; /* noone else does anthing with the return value */
}
#endif // <= 2.6.15
int hfa384x_corereset(hfa384x_t *hw, int holdtime, int settletime, int genesis)
{
int result = 0;
conf_reg_t reg;
UINT8 corsave;
DBFENTER;
WLAN_LOG_DEBUG(3, "Doing reset via CardServices().\n");
/* Collect COR */
reg.Function = 0;
reg.Action = CS_READ;
reg.Offset = CISREG_COR;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
result = pcmcia_access_configuration_register(hw->pdev, &reg);
#else
result = pcmcia_access_configuration_register(
hw->link->handle,
&reg);
#endif
if (result != CS_SUCCESS ) {
WLAN_LOG_ERROR(
":0: AccessConfigurationRegister(CS_READ) failed,"
"result=%d.\n", result);
result = -EIO;
}
corsave = reg.Value;
/* Write reset bit (BIT7) */
reg.Value |= BIT7;
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
result = pcmcia_access_configuration_register(hw->pdev, &reg);
#else
result = pcmcia_access_configuration_register(
hw->link->handle,
&reg);
#endif
if (result != CS_SUCCESS ) {
WLAN_LOG_ERROR(
":1: AccessConfigurationRegister(CS_WRITE) failed,"
"result=%d.\n", result);
result = -EIO;
}
/* Hold for holdtime */
mdelay(holdtime);
if (genesis) {
reg.Value = genesis;
reg.Action = CS_WRITE;
reg.Offset = CISREG_CCSR;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
result = pcmcia_access_configuration_register(hw->pdev, &reg);
#else
result = pcmcia_access_configuration_register(
hw->link->handle,
&reg);
#endif
if (result != CS_SUCCESS ) {
WLAN_LOG_ERROR(
":1: AccessConfigurationRegister(CS_WRITE) failed,"
"result=%d.\n", result);
result = -EIO;
}
}
/* Hold for holdtime */
mdelay(holdtime);
/* Clear reset bit */
reg.Value &= ~BIT7;
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
result = pcmcia_access_configuration_register(hw->pdev, &reg);
#else
result = pcmcia_access_configuration_register(
hw->link->handle,
&reg);
#endif
if (result != CS_SUCCESS ) {
WLAN_LOG_ERROR(
":2: AccessConfigurationRegister(CS_WRITE) failed,"
"result=%d.\n", result);
result = -EIO;
goto done;
}
/* Wait for settletime */
mdelay(settletime);
/* Set non-reset bits back what they were */
reg.Value = corsave;
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
result = pcmcia_access_configuration_register(hw->pdev, &reg);
#else
result = pcmcia_access_configuration_register(
hw->link->handle,
&reg);
#endif
if (result != CS_SUCCESS ) {
WLAN_LOG_ERROR(
":2: AccessConfigurationRegister(CS_WRITE) failed,"
"result=%d.\n", result);
result = -EIO;
goto done;
}
done:
DBFEXIT;
return result;
}
#ifdef MODULE
static int __init prism2cs_init(void)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,68))
servinfo_t serv;
#endif
DBFENTER;
WLAN_LOG_NOTICE("%s Loaded\n", version);
WLAN_LOG_NOTICE("dev_info is: %s\n", dev_info);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,68))
pcmcia_get_card_services_info(&serv);
if ( serv.Revision != CS_RELEASE_CODE )
{
printk(KERN_NOTICE"%s: CardServices release does not match!\n", dev_info);
return -1;
}
/* This call will result in a call to prism2sta_attach */
/* and eventually prism2sta_detach */
register_pccard_driver( &dev_info, &prism2sta_attach, &prism2sta_detach);
#else
pcmcia_register_driver(&prism2_cs_driver);
#endif
DBFEXIT;
return 0;
}
static void __exit prism2cs_cleanup(void)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,68))
dev_link_t *link = dev_list;
dev_link_t *nlink;
DBFENTER;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) )
for (link=dev_list; link != NULL; link = nlink) {
nlink = link->next;
if ( link->state & DEV_CONFIG ) {
prism2sta_release((u_long)link);
}
prism2sta_detach(link); /* remember detach() frees link */
}
#endif
unregister_pccard_driver( &dev_info);
#else
pcmcia_unregister_driver(&prism2_cs_driver);
#endif
printk(KERN_NOTICE "%s Unloaded\n", version);
DBFEXIT;
return;
}
module_init(prism2cs_init);
module_exit(prism2cs_cleanup);
#endif // MODULE
#define WLAN_HOSTIF WLAN_PCI
#include "hfa384x.c"
#include "prism2mgmt.c"
#include "prism2mib.c"
#include "prism2sta.c"
#define PCI_SIZE 0x1000 /* Memory size - 4K bytes */
/* ISL3874A 11Mb/s WLAN controller */
#define PCIVENDOR_INTERSIL 0x1260UL
#define PCIDEVICE_ISL3874 0x3873UL /* [MSM] yeah I know...the ID says
3873. Trust me, it's a 3874. */
/* Samsung SWL-2210P 11Mb/s WLAN controller (uses ISL3874A) */
#define PCIVENDOR_SAMSUNG 0x167dUL
#define PCIDEVICE_SWL_2210P 0xa000UL
#define PCIVENDOR_NETGEAR 0x1385UL /* for MA311 */
/* PCI Class & Sub-Class code, Network-'Other controller' */
#define PCI_CLASS_NETWORK_OTHERS 0x280
/*----------------------------------------------------------------
* prism2sta_probe_pci
*
* Probe routine called when a PCI device w/ matching ID is found.
* The ISL3874 implementation uses the following map:
* BAR0: Prism2.x registers memory mapped, size=4k
* Here's the sequence:
* - Allocate the PCI resources.
* - Read the PCMCIA attribute memory to make sure we have a WLAN card
* - Reset the MAC
* - Initialize the netdev and wlan data
* - Initialize the MAC
*
* Arguments:
* pdev ptr to pci device structure containing info about
* pci configuration.
* id ptr to the device id entry that matched this device.
*
* Returns:
* zero - success
* negative - failed
*
* Side effects:
*
*
* Call context:
* process thread
*
----------------------------------------------------------------*/
static int __devinit
prism2sta_probe_pci(
struct pci_dev *pdev,
const struct pci_device_id *id)
{
int result;
phys_t phymem = 0;
void __iomem *mem = NULL;
wlandevice_t *wlandev = NULL;
hfa384x_t *hw = NULL;
DBFENTER;
/* Enable the pci device */
if (pci_enable_device(pdev)) {
WLAN_LOG_ERROR("%s: pci_enable_device() failed.\n", dev_info);
result = -EIO;
goto fail;
}
/* Figure out our resources */
phymem = pci_resource_start(pdev, 0);
if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) {
printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n");
result = -EIO;
goto fail;
}
mem = ioremap(phymem, PCI_SIZE);
if ( mem == 0 ) {
WLAN_LOG_ERROR("%s: ioremap() failed.\n", dev_info);
result = -EIO;
goto fail;
}
/* Log the device */
WLAN_LOG_INFO("A Prism2.5 PCI device found, "
"phymem:0x%llx, irq:%d, mem:0x%p\n",
(unsigned long long)phymem, pdev->irq, mem);
if ((wlandev = create_wlan()) == NULL) {
WLAN_LOG_ERROR("%s: Memory allocation failure.\n", dev_info);
result = -EIO;
goto fail;
}
hw = wlandev->priv;
if ( wlan_setup(wlandev) != 0 ) {
WLAN_LOG_ERROR("%s: wlan_setup() failed.\n", dev_info);
result = -EIO;
goto fail;
}
/* Setup netdevice's ability to report resources
* Note: the netdevice was allocated by wlan_setup()
*/
wlandev->netdev->irq = pdev->irq;
wlandev->netdev->mem_start = (unsigned long) mem;
wlandev->netdev->mem_end = wlandev->netdev->mem_start +
pci_resource_len(pdev, 0);
/* Initialize the hw data */
hfa384x_create(hw, wlandev->netdev->irq, 0, mem);
hw->wlandev = wlandev;
/* Register the wlandev, this gets us a name and registers the
* linux netdevice.
*/
SET_MODULE_OWNER(wlandev->netdev);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
SET_NETDEV_DEV(wlandev->netdev, &(pdev->dev));
#endif
if ( register_wlandev(wlandev) != 0 ) {
WLAN_LOG_ERROR("%s: register_wlandev() failed.\n", dev_info);
result = -EIO;
goto fail;
}
#if 0
/* TODO: Move this and an irq test into an hfa384x_testif() routine.
*/
outw(PRISM2STA_MAGIC, HFA384x_SWSUPPORT(wlandev->netdev->base_addr));
reg=inw( HFA384x_SWSUPPORT(wlandev->netdev->base_addr));
if ( reg != PRISM2STA_MAGIC ) {
WLAN_LOG_ERROR("MAC register access test failed!\n");
result = -EIO;
goto fail;
}
#endif
/* Do a chip-level reset on the MAC */
if (prism2_doreset) {
result = hfa384x_corereset(hw,
prism2_reset_holdtime,
prism2_reset_settletime, 0);
if (result != 0) {
WLAN_LOG_ERROR(
"%s: hfa384x_corereset() failed.\n",
dev_info);
unregister_wlandev(wlandev);
hfa384x_destroy(hw);
result = -EIO;
goto fail;
}
}
pci_set_drvdata(pdev, wlandev);
/* Shouldn't actually hook up the IRQ until we
* _know_ things are alright. A test routine would help.
*/
request_irq(wlandev->netdev->irq, hfa384x_interrupt,
SA_SHIRQ, wlandev->name, wlandev);
wlandev->msdstate = WLAN_MSD_HWPRESENT;
result = 0;
goto done;
fail:
pci_set_drvdata(pdev, NULL);
if (wlandev) kfree(wlandev);
if (hw) kfree(hw);
if (mem) iounmap(mem);
pci_release_regions(pdev);
pci_disable_device(pdev);
done:
DBFEXIT;
return result;
}
static void __devexit prism2sta_remove_pci(struct pci_dev *pdev)
{
wlandevice_t *wlandev;
hfa384x_t *hw;
wlandev = (wlandevice_t *) pci_get_drvdata(pdev);
hw = wlandev->priv;
p80211netdev_hwremoved(wlandev);
/* reset hardware */
prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
if (pdev->irq)
free_irq(pdev->irq, wlandev);
unregister_wlandev(wlandev);
/* free local stuff */
if (hw) {
hfa384x_destroy(hw);
kfree(hw);
}
iounmap((void __iomem *)wlandev->netdev->mem_start);
wlan_unsetup(wlandev);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
kfree(wlandev);
}
static struct pci_device_id pci_id_tbl[] = {
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3874,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller"
},
{
PCIVENDOR_INTERSIL, 0x3872,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Intersil Prism2.5 ISL3872 11Mb/s WLAN Controller"
},
{
PCIVENDOR_SAMSUNG, PCIDEVICE_SWL_2210P,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Samsung MagicLAN SWL-2210P 11Mb/s WLAN Controller"
},
{ /* for NetGear MA311 */
PCIVENDOR_NETGEAR, 0x3872,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Netgear MA311 WLAN Controller"
},
{
0, 0, 0, 0, 0, 0, 0
}
};
MODULE_DEVICE_TABLE(pci, pci_id_tbl);
/* Function declared here because of ptr reference below */
static int __devinit prism2sta_probe_pci(struct pci_dev *pdev,
const struct pci_device_id *id);
static void __devexit prism2sta_remove_pci(struct pci_dev *pdev);
static struct pci_driver prism2_pci_drv_id = {
.name = "prism2_pci",
.id_table = pci_id_tbl,
.probe = prism2sta_probe_pci,
.remove = prism2sta_remove_pci,
#ifdef CONFIG_PM
.suspend = prism2sta_suspend_pci,
.resume = prism2sta_resume_pci,
#endif
};
#ifdef MODULE
static int __init prism2pci_init(void)
{
WLAN_LOG_NOTICE("%s Loaded\n", version);
return pci_module_init(&prism2_pci_drv_id);
};
static void __exit prism2pci_cleanup(void)
{
pci_unregister_driver(&prism2_pci_drv_id);
};
module_init(prism2pci_init);
module_exit(prism2pci_cleanup);
#endif
int hfa384x_corereset(hfa384x_t *hw, int holdtime, int settletime, int genesis)
{
int result = 0;
unsigned long timeout;
UINT16 reg;
DBFENTER;
/* Assert reset and wait awhile
* (note: these delays are _really_ long, but they appear to be
* necessary.)
*/
hfa384x_setreg(hw, 0xc5, HFA384x_PCICOR);
timeout = jiffies + HZ/4;
while(time_before(jiffies, timeout)) udelay(5);
if (genesis) {
hfa384x_setreg(hw, genesis, HFA384x_PCIHCR);
timeout = jiffies + HZ/4;
while(time_before(jiffies, timeout)) udelay(5);
}
/* Clear the reset and wait some more
*/
hfa384x_setreg(hw, 0x45, HFA384x_PCICOR);
timeout = jiffies + HZ/2;
while(time_before(jiffies, timeout)) udelay(5);
/* Wait for f/w to complete initialization (CMD:BUSY == 0)
*/
timeout = jiffies + 2*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("corereset: Timed out waiting for cmd register.\n");
result=1;
}
DBFEXIT;
return result;
}
#define WLAN_HOSTIF WLAN_PLX
#include "hfa384x.c"
#include "prism2mgmt.c"
#include "prism2mib.c"
#include "prism2sta.c"
#define PLX_ATTR_SIZE 0x1000 /* Attribute memory size - 4K bytes */
#define COR_OFFSET 0x3e0 /* COR attribute offset of Prism2 PC card */
#define COR_VALUE 0x41 /* Enable PC card with irq in level trigger */
#define PLX_INTCSR 0x4c /* Interrupt Control and Status Register */
#define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */
#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */
/* 3Com 3CRW777A (PLX) board ID */
#define PCIVENDOR_3COM 0x10B7
#define PCIDEVICE_AIRCONNECT 0x7770
/* Eumitcom PCI WL11000 PCI Adapter (PLX) board device+vendor ID */
#define PCIVENDOR_EUMITCOM 0x1638UL
#define PCIDEVICE_WL11000 0x1100UL
/* Global Sun Tech GL24110P PCI Adapter (PLX) board device+vendor ID */
#define PCIVENDOR_GLOBALSUN 0x16abUL
#define PCIDEVICE_GL24110P 0x1101UL
#define PCIDEVICE_GL24110P_ALT 0x1102UL
/* Netgear MA301 PCI Adapter (PLX) board device+vendor ID */
#define PCIVENDOR_NETGEAR 0x1385UL
#define PCIDEVICE_MA301 0x4100UL
/* US Robotics USR2410 PCI Adapter (PLX) board device+vendor ID */
#define PCIVENDOR_USROBOTICS 0x16ecUL
#define PCIDEVICE_USR2410 0x3685UL
/* Linksys WPC11 card with the WDT11 adapter (PLX) board device+vendor ID */
#define PCIVENDOR_Linksys 0x16abUL
#define PCIDEVICE_Wpc11Wdt11 0x1102UL
/* National Datacomm Corp SOHOware Netblaster II PCI */
#define PCIVENDOR_NDC 0x15e8UL
#define PCIDEVICE_NCP130_PLX 0x0130UL
#define PCIDEVICE_NCP130_ASIC 0x0131UL
/* NDC NCP130_PLX is also sold by Corega. Their name is CGWLPCIA11 */
#define PCIVENDOR_COREGA PCIVENDOR_NDC
#define PCIDEVICE_CGWLPCIA11 PCIDEVICE_NCP130_PLX
/* PCI Class & Sub-Class code, Network-'Other controller' */
#define PCI_CLASS_NETWORK_OTHERS 0x280
/*----------------------------------------------------------------
* prism2sta_probe_plx
*
* Probe routine called when a PCI device w/ matching ID is found.
* This PLX implementation uses the following map:
* BAR0: Unused
* BAR1: ????
* BAR2: PCMCIA attribute memory
* BAR3: PCMCIA i/o space
* Here's the sequence:
* - Allocate the PCI resources.
* - Read the PCMCIA attribute memory to make sure we have a WLAN card
* - Reset the MAC using the PCMCIA COR
* - Initialize the netdev and wlan data
* - Initialize the MAC
*
* Arguments:
* pdev ptr to pci device structure containing info about
* pci configuration.
* id ptr to the device id entry that matched this device.
*
* Returns:
* zero - success
* negative - failed
*
* Side effects:
*
*
* Call context:
* process thread
*
----------------------------------------------------------------*/
static int __devinit
prism2sta_probe_plx(
struct pci_dev *pdev,
const struct pci_device_id *id)
{
int result;
phys_t pccard_ioaddr;
phys_t pccard_attr_mem;
unsigned int pccard_attr_len;
void __iomem *attr_mem = NULL;
UINT32 plx_addr;
wlandevice_t *wlandev = NULL;
hfa384x_t *hw = NULL;
int reg;
u32 regic;
if (pci_enable_device(pdev))
return -EIO;
/* TMC7160 boards are special */
if ((pdev->vendor == PCIVENDOR_NDC) &&
(pdev->device == PCIDEVICE_NCP130_ASIC)) {
unsigned long delay;
pccard_attr_mem = 0;
pccard_ioaddr = pci_resource_start(pdev, 1);
outb(0x45, pccard_ioaddr);
delay = jiffies + 1*HZ;
while (time_before(jiffies, delay));
if (inb(pccard_ioaddr) != 0x45) {
WLAN_LOG_ERROR("Initialize the TMC7160 failed. (0x%x)\n", inb(pccard_ioaddr));
return -EIO;
}
pccard_ioaddr = pci_resource_start(pdev, 2);
prism2_doreset = 0;
WLAN_LOG_INFO("NDC NCP130 with TMC716(ASIC) PCI interface device found at io:0x%x, irq:%d\n", pccard_ioaddr, pdev->irq);
goto init;
}
/* Collect the resource requirements */
pccard_attr_mem = pci_resource_start(pdev, 2);
pccard_attr_len = pci_resource_len(pdev, 2);
if (pccard_attr_len < PLX_MIN_ATTR_LEN)
return -EIO;
pccard_ioaddr = pci_resource_start(pdev, 3);
/* bjoern: We need to tell the card to enable interrupts, in
* case the serial eprom didn't do this already. See the
* PLX9052 data book, p8-1 and 8-24 for reference.
* [MSM]: This bit of code came from the orinoco_cs driver.
*/
plx_addr = pci_resource_start(pdev, 1);
regic = 0;
regic = inl(plx_addr+PLX_INTCSR);
if(regic & PLX_INTCSR_INTEN) {
WLAN_LOG_DEBUG(1,
"%s: Local Interrupt already enabled\n", dev_info);
} else {
regic |= PLX_INTCSR_INTEN;
outl(regic, plx_addr+PLX_INTCSR);
regic = inl(plx_addr+PLX_INTCSR);
if(!(regic & PLX_INTCSR_INTEN)) {
WLAN_LOG_ERROR(
"%s: Couldn't enable Local Interrupts\n",
dev_info);
return -EIO;
}
}
/* These assignments are here in case of future mappings for
* io space and irq that might be similar to ioremap
*/
if (!request_mem_region(pccard_attr_mem, pci_resource_len(pdev, 2), "Prism2")) {
WLAN_LOG_ERROR("%s: Couldn't reserve PCI memory region\n", dev_info);
return -EIO;
}
attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
WLAN_LOG_INFO("A PLX PCI/PCMCIA interface device found, "
"phymem:0x%llx, phyio=0x%x, irq:%d, "
"mem: 0x%lx\n",
(unsigned long long)pccard_attr_mem, pccard_ioaddr, pdev->irq,
(unsigned long)attr_mem);
/* Verify whether PC card is present.
* [MSM] This needs improvement, the right thing to do is
* probably to walk the CIS looking for the vendor and product
* IDs. It would be nice if this could be tied in with the
* etc/pcmcia/wlan-ng.conf file. Any volunteers? ;-)
*/
if (
readb(attr_mem + 0) != 0x01 || readb(attr_mem + 2) != 0x03 ||
readb(attr_mem + 4) != 0x00 || readb(attr_mem + 6) != 0x00 ||
readb(attr_mem + 8) != 0xFF || readb(attr_mem + 10) != 0x17 ||
readb(attr_mem + 12) != 0x04 || readb(attr_mem + 14) != 0x67) {
WLAN_LOG_ERROR("Prism2 PC card CIS is invalid.\n");
return -EIO;
}
WLAN_LOG_INFO("A PCMCIA WLAN adapter was found.\n");
/* Write COR to enable PC card */
writeb(COR_VALUE, attr_mem + COR_OFFSET);
reg = readb(attr_mem + COR_OFFSET);
init:
/*
* Now do everything the same as a PCI device
* [MSM] TODO: We could probably factor this out of pcmcia/pci/plx
* and perhaps usb. Perhaps a task for another day.......
*/
if ((wlandev = create_wlan()) == NULL) {
WLAN_LOG_ERROR("%s: Memory allocation failure.\n", dev_info);
result = -EIO;
goto failed;
}
hw = wlandev->priv;
if ( wlan_setup(wlandev) != 0 ) {
WLAN_LOG_ERROR("%s: wlan_setup() failed.\n", dev_info);
result = -EIO;
goto failed;
}
/* Setup netdevice's ability to report resources
* Note: the netdevice was allocated by wlan_setup()
*/
wlandev->netdev->irq = pdev->irq;
wlandev->netdev->base_addr = pccard_ioaddr;
wlandev->netdev->mem_start = (unsigned long)attr_mem;
wlandev->netdev->mem_end = (unsigned long)attr_mem + pci_resource_len(pdev, 0);
/* Initialize the hw data */
hfa384x_create(hw, wlandev->netdev->irq, pccard_ioaddr, attr_mem);
hw->wlandev = wlandev;
/* Register the wlandev, this gets us a name and registers the
* linux netdevice.
*/
SET_MODULE_OWNER(wlandev->netdev);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
SET_NETDEV_DEV(wlandev->netdev, &(pdev->dev));
#endif
if ( register_wlandev(wlandev) != 0 ) {
WLAN_LOG_ERROR("%s: register_wlandev() failed.\n", dev_info);
result = -EIO;
goto failed;
}
#if 0
/* TODO: Move this and an irq test into an hfa384x_testif() routine.
*/
outw(PRISM2STA_MAGIC, HFA384x_SWSUPPORT(wlandev->netdev->base_addr));
reg=inw( HFA384x_SWSUPPORT(wlandev->netdev->base_addr));
if ( reg != PRISM2STA_MAGIC ) {
WLAN_LOG_ERROR("MAC register access test failed!\n");
result = -EIO;
goto failed;
}
#endif
/* Do a chip-level reset on the MAC */
if (prism2_doreset) {
result = hfa384x_corereset(hw,
prism2_reset_holdtime,
prism2_reset_settletime, 0);
if (result != 0) {
unregister_wlandev(wlandev);
hfa384x_destroy(hw);
WLAN_LOG_ERROR(
"%s: hfa384x_corereset() failed.\n",
dev_info);
result = -EIO;
goto failed;
}
}
pci_set_drvdata(pdev, wlandev);
/* Shouldn't actually hook up the IRQ until we
* _know_ things are alright. A test routine would help.
*/
request_irq(wlandev->netdev->irq, hfa384x_interrupt,
SA_SHIRQ, wlandev->name, wlandev);
wlandev->msdstate = WLAN_MSD_HWPRESENT;
result = 0;
goto done;
failed:
pci_set_drvdata(pdev, NULL);
if (wlandev) kfree(wlandev);
if (hw) kfree(hw);
if (attr_mem) iounmap(attr_mem);
pci_release_regions(pdev);
pci_disable_device(pdev);
done:
DBFEXIT;
return result;
}
static void __devexit prism2sta_remove_plx(struct pci_dev *pdev)
{
wlandevice_t *wlandev;
hfa384x_t *hw;
wlandev = (wlandevice_t *) pci_get_drvdata(pdev);
hw = wlandev->priv;
p80211netdev_hwremoved(wlandev);
/* reset hardware */
prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
if (pdev->irq)
free_irq(pdev->irq, wlandev);
unregister_wlandev(wlandev);
/* free local stuff */
if (hw) {
hfa384x_destroy(hw);
kfree(hw);
}
iounmap((void __iomem *)wlandev->netdev->mem_start);
wlan_unsetup(wlandev);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
kfree(wlandev);
}
static struct pci_device_id plx_id_tbl[] = {
{
PCIVENDOR_EUMITCOM, PCIDEVICE_WL11000,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Eumitcom WL11000 PCI(PLX) card"
},
{
PCIVENDOR_GLOBALSUN, PCIDEVICE_GL24110P,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Global Sun Tech GL24110P PCI(PLX) card"
},
{
PCIVENDOR_GLOBALSUN, PCIDEVICE_GL24110P_ALT,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Global Sun Tech GL24110P PCI(PLX) card"
},
{
PCIVENDOR_NETGEAR, PCIDEVICE_MA301,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Global Sun Tech GL24110P PCI(PLX) card"
},
{
PCIVENDOR_USROBOTICS, PCIDEVICE_USR2410,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"US Robotics USR2410 PCI(PLX) card"
},
{
PCIVENDOR_Linksys, PCIDEVICE_Wpc11Wdt11,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"Linksys WPC11 with WDT11 PCI(PLX) adapter"
},
{
PCIVENDOR_NDC, PCIDEVICE_NCP130_PLX,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"NDC Netblaster II PCI(PLX)"
},
{
PCIVENDOR_NDC, PCIDEVICE_NCP130_ASIC,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"NDC Netblaster II PCI(TMC7160)"
},
{
PCIVENDOR_3COM, PCIDEVICE_AIRCONNECT,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
/* Driver data, we just put the name here */
(unsigned long)"3Com AirConnect PCI 802.11b 11Mb/s WLAN Controller"
},
{
0, 0, 0, 0, 0, 0, 0
}
};
MODULE_DEVICE_TABLE(pci, plx_id_tbl);
/* Function declared here because of ptr reference below */
static int __devinit prism2sta_probe_plx(struct pci_dev *pdev,
const struct pci_device_id *);
static void __devexit prism2sta_remove_plx(struct pci_dev *pdev);
static struct pci_driver prism2_plx_drv_id = {
.name = "prism2_plx",
.id_table = plx_id_tbl,
.probe = prism2sta_probe_plx,
.remove = prism2sta_remove_plx,
#ifdef CONFIG_PM
.suspend = prism2sta_suspend_pci,
.resume = prism2sta_resume_pci,
#endif
};
#ifdef MODULE
static int __init prism2plx_init(void)
{
WLAN_LOG_NOTICE("%s Loaded\n", version);
return pci_module_init(&prism2_plx_drv_id);
};
static void __exit prism2plx_cleanup(void)
{
pci_unregister_driver(&prism2_plx_drv_id);
};
module_init(prism2plx_init);
module_exit(prism2plx_cleanup);
#endif // MODULE
int hfa384x_corereset(hfa384x_t *hw, int holdtime, int settletime, int genesis)
{
int result = 0;
#define COR_OFFSET 0x3e0 /* COR attribute offset of Prism2 PC card */
#define COR_VALUE 0x41 /* Enable PC card with irq in level trigger */
#define HCR_OFFSET 0x3e2 /* HCR attribute offset of Prism2 PC card */
UINT8 corsave;
DBFENTER;
WLAN_LOG_DEBUG(3, "Doing reset via direct COR access.\n");
/* Collect COR */
corsave = readb(hw->membase + COR_OFFSET);
/* Write reset bit (BIT7) */
writeb(corsave | BIT7, hw->membase + COR_OFFSET);
/* Hold for holdtime */
mdelay(holdtime);
if (genesis) {
writeb(genesis, hw->membase + HCR_OFFSET);
/* Hold for holdtime */
mdelay(holdtime);
}
/* Clear reset bit */
writeb(corsave & ~BIT7, hw->membase + COR_OFFSET);
/* Wait for settletime */
mdelay(settletime);
/* Set non-reset bits back what they were */
writeb(corsave, hw->membase + COR_OFFSET);
DBFEXIT;
return result;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册