/******************************************************************************
Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
802.11 status code portion of this file from ethereal-0.10.6:
Copyright 2000, Axis Communications AB
Ethereal - Network traffic analyzer
By Gerald Combs <
[email protected]>
Copyright 1998 Gerald Combs
This program is free software; you can redistribute it and/or modify it
under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
The full GNU General Public License is included in this distribution in the
file called LICENSE.
Contact Information:
James P. Ketrenos <
[email protected]>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
******************************************************************************/
/*
*
* About the threading and lock model of the driver...
*
* There are three paths of execution through the driver.
*
* 1. ioctl based (wireless extensions, netdev, etc.)
* 2. interrupt based
* 3. scheduled work queue items
*
* As soon as an interrupt comes in, it schedules a tasklet. That tasklet,
* when run, does any HW checks, pulls any data from the read queue,
* and schedules other layers to do the actual work.
*
* About scanning
*
* There are several states related to scanning:
* !STATUS_SCANNING -- no scan requests, no active scan
*
* The states can transition as follows:
*
* !STATUS_SCANNING => (SCANNING_DIRECT | SCANNING_INDIRECT)
* SCANNING_DIRECT => (SCANNING_INDIRECT | !STATUS_SCANNING)
* SCANNING_INDIRECT => !STATUS_SCANNING
*
* Each band requires a seperate scan request. This means that in a
* configuration where the user has specified an ESSID in an ABG network,
* we need to execute 4 scan passes -- one for each band (2.4 or 5.2) and
* then again with each band one directed and one indirect.
*
*/
#include <net/ieee80211.h>
#include <net/ieee80211_radiotap.h>
#include <asm/div64.h>
#include "ipw3945.h"
#ifndef IPW3945_COMPAT
#error Incomplete build system. If you are building as part of your kernel build
#error process, ensure that you used 'make patch_kernel' to install into your kernel.
#endif
#ifdef CONFIG_IPW3945_DEBUG
#define VD "d"
#else
#define VD
#endif
#ifdef CONFIG_IPW3945_MONITOR
#define VM "m"
#else
#define VM
#endif
#ifdef CONFIG_IPW3945_PROMISCUOUS
#define VP "p"
#else
#define VP
#endif
#ifdef CONFIG_IEEE80211_RADIOTAP
#define VR "r"
#else
#define VR
#endif
#ifdef CONFIG_IPW3945_QOS
#define VQ "q"
#else
#define VQ
#endif
#define IPW3945_VERSION "1.2.0" VD VM VP VR VQ
#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 3945 Network Connection driver for Linux"
#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
#define DRV_VERSION IPW3945_VERSION
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
#ifdef CONFIG_IPW3945_DEBUG
static int debug = 0;
#endif
static int channel = 0;
static int mode = 0;
static u32 ipw_debug_level;
static int associate = 0; /* def: don't assoc until user sets assoc parms */
static int auto_create = 1; /* def: create new adhoc network if needed */
static int led = 1; /* def: use LEDs */
static int disable = 0; /* def: enable radio */
static int antenna = 0; /* def: 0 = both antennas (use diversity) */
static const char ipw_modes[] = {
'a', 'b', 'g', '?'
};
#ifdef CONFIG_IPW3945_PROMISCUOUS
static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */
#endif
#define LD_TIME_LINK_ON 300
static int from_priority_to_tx_queue[] = {
IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2,
IPW_TX_QUEUE_1,
IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
};
static int ipw_rate_scale_init_handle(struct ipw_priv *priv, s32 window_size);
static int ipw_update_power_cmd(struct ipw_priv *priv,
struct ipw_powertable_cmd *cmd, u32 mode);
static int ipw_power_init_handle(struct ipw_priv *priv);
static int ipw_queue_tx_hcmd(struct ipw_priv *priv, struct ipw_host_cmd *cmd);
static int ipw_update_rate_scaling(struct ipw_priv *priv, u8 mode);
static int ipw_rate_scale_tx_resp_handle(struct ipw_priv *priv,
struct ipw_tx_resp *tx_resp);
static int ipw_rate_scale_rxon_handle(struct ipw_priv *priv, s32 sta_id);
static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid);
static void ipw_clear_stations_table(struct ipw_priv *priv);
static int ipw_rx_queue_update_write_ptr(struct ipw_priv *priv,
struct ipw_rx_queue *q);
static int ipw_tx_queue_update_write_ptr(struct ipw_priv *priv,
struct ipw_tx_queue *txq, int tx_id);
#define SLP PMC_TCMD_FLAG_DRIVER_ALLOW_SLEEP_MSK
#define MSEC_TO_USEC 1024
/* default power management (not Tx power) table values */
static struct ipw_power_vec_entry range_0[IPW_POWER_AC] = { // for tim 0-10
{{0, 0 * MSEC_TO_USEC, 0 * MSEC_TO_USEC, {0, 0, 0, 0, 0}}, 0},
{{SLP, 200 * MSEC_TO_USEC, 500 * MSEC_TO_USEC,
{1, 2, 3, 4, 4}}, 0},
{{SLP, 200 * MSEC_TO_USEC, 300 * MSEC_TO_USEC,
{2, 4, 6, 7, 7}}, 0},
{{SLP, 50 * MSEC_TO_USEC, 100 * MSEC_TO_USEC,
{2, 6, 9, 9, 10}}, 0},
{{SLP, 50 * MSEC_TO_USEC, 25 * MSEC_TO_USEC,
{2, 7, 9, 9, 10}}, 1},
{{SLP, 25 * MSEC_TO_USEC, 25 * MSEC_TO_USEC,
{4, 7, 10, 10, 10}}, 1}
};
static struct ipw_power_vec_entry range_1[IPW_POWER_AC] = { // fot tim > 10
{{0, 0 * MSEC_TO_USEC, 0 * MSEC_TO_USEC, {0, 0, 0, 0, 0}}, 0},
{{SLP, 200 * MSEC_TO_USEC, 500 * MSEC_TO_USEC,
{1, 2, 3, 4, 0xFF}}, 0},
{{SLP, 200 * MSEC_TO_USEC, 300 * MSEC_TO_USEC,
{2, 4, 6, 7, 0xFF}}, 0},
{{SLP, 50 * MSEC_TO_USEC, 100 * MSEC_TO_USEC,
{2, 6, 9, 9, 0xFF}}, 0},
{{SLP, 50 * MSEC_TO_USEC, 25 * MSEC_TO_USEC,
{2, 7, 9, 9, 0xFF}}, 0},
{{SLP, 25 * MSEC_TO_USEC, 25 * MSEC_TO_USEC,
{4, 7, 10, 10, 0xFF}}, 0}
};
/************************************************/
static void ipw_remove_current_network(struct ipw_priv *priv);
static void ipw_rx_handle(struct ipw_priv *priv);
static int ipw_queue_tx_reclaim(struct ipw_priv *priv, int qindex, int index);
static int ipw_queue_reset(struct ipw_priv *priv);
static void ipw_tx_queue_free(struct ipw_priv *);
static int ipw_stop_tx_queue(struct ipw_priv *priv);
static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
static void ipw_rx_queue_replenish(struct ipw_priv *);
static int ipw_up(struct ipw_priv *);
static void ipw_bg_up(struct work_struct *work);
static void ipw_down(struct ipw_priv *);
static void ipw_bg_down(struct work_struct *work);
static int ipw_card_show_info(struct ipw_priv *priv);
static int ipw_query_eeprom(struct ipw_priv *priv, u32 offset,
u32 len, u8 * buf);
static void ipw_bg_alive_start(struct work_struct *work);
static int ipw_send_card_state(struct ipw_priv *priv, u32 flags, u8 meta_flag);
static void ipw_link_down(struct ipw_priv *priv);
static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid,
int is_ap, u8 flags);
static u8 ipw_remove_station(struct ipw_priv *priv, u8 * bssid, int is_ap);
static int ipw_card_bss_active_changed_notify(struct ipw_priv *priv, struct ieee80211_network
*network);
static int snprint_line(char *buf, size_t count,
const u8 * data, u32 len, u32 ofs)
{
int out, i, j, l;
char c;
out = snprintf(buf, count, "%08X", ofs);
for (l = 0, i = 0; i < 2; i++) {
out += snprintf(buf + out, count - out, " ");
for (j = 0; j < 8 && l < len; j++, l++)
out +=
s