| /* 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*)(®))[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; |
| } |