blob: fd7daf52f8b251869256b858382f98d0d0bf282a [file] [log] [blame]
/***********************************************************************
** Copyright (C) 2003 ACX100 Open Source Project
**
** 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 ACX100 Open Source Project can be
** made directly to:
**
** acx100-users@lists.sf.net
** http://acx100.sf.net
** ---------------------------------------------------------------------
*/
#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/delay.h>
#include <linux/proc_fs.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include <linux/pm.h>
#include <linux/vmalloc.h>
#include <linux/firmware.h> /* request_firmware() */
#include <net/iw_handler.h>
#include "acx.h"
/***********************************************************************
*/
#if ACX_DEBUG
unsigned int acx_debug /* will add __read_mostly later */ = ACX_DEFAULT_MSG;
EXPORT_SYMBOL_GPL(acx_debug);
/* parameter is 'debug', corresponding var is acx_debug */
module_param_named(debug, acx_debug, uint, 0);
MODULE_PARM_DESC(debug, "Debug level mask (see L_xxx constants)");
#endif
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual MPL/GPL");
#endif
/* USB had this: MODULE_AUTHOR("Martin Wawro <martin.wawro AT uni-dortmund.de>"); */
MODULE_AUTHOR("ACX100 Open Source Driver development team");
MODULE_DESCRIPTION("Driver for TI ACX1xx based wireless cards (common)");
/***********************************************************************
*/
/* Probably a number of acx's intermediate buffers for USB transfers,
** not to be confused with number of descriptors in tx/rx rings
** (which are not directly accessible to host in USB devices) */
#define USB_RX_CNT 10
#define USB_TX_CNT 10
/***********************************************************************
*/
/* minutes to wait until next radio recalibration: */
#define RECALIB_PAUSE 5
/* Please keep acx_reg_domain_ids_len in sync... */
const u8 acx_reg_domain_ids[acx_reg_domain_ids_len] =
{ 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x51 };
static const u16 reg_domain_channel_masks[acx_reg_domain_ids_len] =
{ 0x07ff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc };
const char * const
acx_reg_domain_strings[] = {
/* 0 */ " 1-11 FCC (USA)",
/* 1 */ " 1-11 DOC/IC (Canada)",
/* BTW: WLAN use in ETSI is regulated by ETSI standard EN 300 328-2 V1.1.2 */
/* 2 */ " 1-13 ETSI (Europe)",
/* 3 */ "10-11 Spain",
/* 4 */ "10-13 France",
/* 5 */ " 14 MKK (Japan)",
/* 6 */ " 1-14 MKK1",
/* 7 */ " 3-9 Israel (not all firmware versions)",
NULL /* needs to remain as last entry */
};
/***********************************************************************
** Debugging support
*/
#ifdef PARANOID_LOCKING
static unsigned max_lock_time;
static unsigned max_sem_time;
void
acx_lock_unhold() { max_lock_time = 0; }
void
acx_sem_unhold() { max_sem_time = 0; }
static inline const char*
sanitize_str(const char *s)
{
const char* t = strrchr(s, '/');
if (t) return t + 1;
return s;
}
void
acx_lock_debug(acx_device_t *adev, const char* where)
{
unsigned int count = 100*1000*1000;
where = sanitize_str(where);
while (--count) {
if (!spin_is_locked(&adev->lock)) break;
cpu_relax();
}
if (!count) {
printk(KERN_EMERG "LOCKUP: already taken at %s!\n", adev->last_lock);
BUG();
}
adev->last_lock = where;
rdtscl(adev->lock_time);
}
void
acx_unlock_debug(acx_device_t *adev, const char* where)
{
#ifdef SMP
if (!spin_is_locked(&adev->lock)) {
where = sanitize_str(where);
printk(KERN_EMERG "STRAY UNLOCK at %s!\n", where);
BUG();
}
#endif
if (acx_debug & L_LOCK) {
unsigned long diff;
rdtscl(diff);
diff -= adev->lock_time;
if (diff > max_lock_time) {
where = sanitize_str(where);
printk("max lock hold time %ld CPU ticks from %s "
"to %s\n", diff, adev->last_lock, where);
max_lock_time = diff;
}
}
}
void
acx_down_debug(acx_device_t *adev, const char* where)
{
int sem_count;
unsigned long timeout = jiffies + 5*HZ;
where = sanitize_str(where);
for (;;) {
sem_count = atomic_read(&adev->sem.count);
if (sem_count) break;
if (time_after(jiffies, timeout))
break;
msleep(5);
}
if (!sem_count) {
printk(KERN_EMERG "D STATE at %s! last sem at %s\n",
where, adev->last_sem);
dump_stack();
}
adev->last_sem = where;
adev->sem_time = jiffies;
down(&adev->sem);
if (acx_debug & L_LOCK) {
printk("%s: sem_down %d -> %d\n",
where, sem_count, atomic_read(&adev->sem.count));
}
}
void
acx_up_debug(acx_device_t *adev, const char* where)
{
int sem_count = atomic_read(&adev->sem.count);
if (sem_count) {
where = sanitize_str(where);
printk(KERN_EMERG "STRAY UP at %s! sem.count=%d\n", where, sem_count);
dump_stack();
}
if (acx_debug & L_LOCK) {
unsigned long diff = jiffies - adev->sem_time;
if (diff > max_sem_time) {
where = sanitize_str(where);
printk("max sem hold time %ld jiffies from %s "
"to %s\n", diff, adev->last_sem, where);
max_sem_time = diff;
}
}
up(&adev->sem);
if (acx_debug & L_LOCK) {
where = sanitize_str(where);
printk("%s: sem_up %d -> %d\n",
where, sem_count, atomic_read(&adev->sem.count));
}
}
#endif /* PARANOID_LOCKING */
/***********************************************************************
*/
#if ACX_DEBUG > 1
static int acx_debug_func_indent;
#define DEBUG_TSC 0
#define FUNC_INDENT_INCREMENT 2
#if DEBUG_TSC
#define TIMESTAMP(d) unsigned long d; rdtscl(d)
#else
#define TIMESTAMP(d) unsigned long d = jiffies
#endif
static const char
spaces[] = " " " "; /* Nx10 spaces */
void
acx_log_fn_enter(const char *funcname)
{
int indent;
TIMESTAMP(d);
indent = acx_debug_func_indent;
if (indent >= sizeof(spaces))
indent = sizeof(spaces)-1;
printk("%08ld %s==> %s\n",
d % 100000000,
spaces + (sizeof(spaces)-1) - indent,
funcname
);
acx_debug_func_indent += FUNC_INDENT_INCREMENT;
}
EXPORT_SYMBOL_GPL(acx_log_fn_enter);
void
acx_log_fn_exit(const char *funcname)
{
int indent;
TIMESTAMP(d);
acx_debug_func_indent -= FUNC_INDENT_INCREMENT;
indent = acx_debug_func_indent;
if (indent >= sizeof(spaces))
indent = sizeof(spaces)-1;
printk("%08ld %s<== %s\n",
d % 100000000,
spaces + (sizeof(spaces)-1) - indent,
funcname
);
}
EXPORT_SYMBOL_GPL(acx_log_fn_exit);
void
acx_log_fn_exit_v(const char *funcname, int v)
{
int indent;
TIMESTAMP(d);
acx_debug_func_indent -= FUNC_INDENT_INCREMENT;
indent = acx_debug_func_indent;
if (indent >= sizeof(spaces))
indent = sizeof(spaces)-1;
printk("%08ld %s<== %s: %08X\n",
d % 100000000,
spaces + (sizeof(spaces)-1) - indent,
funcname,
v
);
}
EXPORT_SYMBOL_GPL(acx_log_fn_exit_v);
#endif /* ACX_DEBUG > 1 */
/***********************************************************************
** Basically a msleep with logging
*/
void
acx_s_msleep(int ms)
{
FN_ENTER;
msleep(ms);
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_s_msleep);
/***********************************************************************
** Not inlined: it's larger than it seems
*/
void
acx_print_mac(const char *head, const u8 *mac, const char *tail)
{
printk("%s"MACSTR"%s", head, MAC(mac), tail);
}
/***********************************************************************
** acx_get_status_name
*/
static const char*
acx_get_status_name(u16 status)
{
static const char * const str[] = {
"STOPPED", "SCANNING", "WAIT_AUTH",
"AUTHENTICATED", "ASSOCIATED", "INVALID??"
};
if (status > VEC_SIZE(str)-1)
status = VEC_SIZE(str)-1;
return str[status];
}
/***********************************************************************
** acx_get_packet_type_string
*/
#if ACX_DEBUG
const char*
acx_get_packet_type_string(u16 fc)
{
static const char * const mgmt_arr[] = {
"MGMT/AssocReq", "MGMT/AssocResp", "MGMT/ReassocReq",
"MGMT/ReassocResp", "MGMT/ProbeReq", "MGMT/ProbeResp",
"MGMT/UNKNOWN", "MGMT/UNKNOWN", "MGMT/Beacon", "MGMT/ATIM",
"MGMT/Disassoc", "MGMT/Authen", "MGMT/Deauthen"
};
static const char * const ctl_arr[] = {
"CTL/PSPoll", "CTL/RTS", "CTL/CTS", "CTL/Ack", "CTL/CFEnd",
"CTL/CFEndCFAck"
};
static const char * const data_arr[] = {
"DATA/DataOnly", "DATA/Data CFAck", "DATA/Data CFPoll",
"DATA/Data CFAck/CFPoll", "DATA/Null", "DATA/CFAck",
"DATA/CFPoll", "DATA/CFAck/CFPoll"
};
const char *str;
u8 fstype = (WF_FC_FSTYPE & fc) >> 4;
u8 ctl;
switch (WF_FC_FTYPE & fc) {
case WF_FTYPE_MGMT:
if (fstype < VEC_SIZE(mgmt_arr))
str = mgmt_arr[fstype];
else
str = "MGMT/UNKNOWN";
break;
case WF_FTYPE_CTL:
ctl = fstype - 0x0a;
if (ctl < VEC_SIZE(ctl_arr))
str = ctl_arr[ctl];
else
str = "CTL/UNKNOWN";
break;
case WF_FTYPE_DATA:
if (fstype < VEC_SIZE(data_arr))
str = data_arr[fstype];
else
str = "DATA/UNKNOWN";
break;
default:
str = "UNKNOWN";
break;
}
return str;
}
EXPORT_SYMBOL_GPL(acx_get_packet_type_string);
#endif
/***********************************************************************
** acx_wlan_reason_str
*/
static inline const char*
acx_wlan_reason_str(u16 reason)
{
static const char* const reason_str[] = {
/* 0 */ "?",
/* 1 */ "unspecified",
/* 2 */ "prev auth is not valid",
/* 3 */ "leaving BBS",
/* 4 */ "due to inactivity",
/* 5 */ "AP is busy",
/* 6 */ "got class 2 frame from non-auth'ed STA",
/* 7 */ "got class 3 frame from non-assoc'ed STA",
/* 8 */ "STA has left BSS",
/* 9 */ "assoc without auth is not allowed",
/* 10 */ "bad power setting (802.11h)",
/* 11 */ "bad channel (802.11i)",
/* 12 */ "?",
/* 13 */ "invalid IE",
/* 14 */ "MIC failure",
/* 15 */ "four-way handshake timeout",
/* 16 */ "group key handshake timeout",
/* 17 */ "IE is different",
/* 18 */ "invalid group cipher",
/* 19 */ "invalid pairwise cipher",
/* 20 */ "invalid AKMP",
/* 21 */ "unsupported RSN version",
/* 22 */ "invalid RSN IE cap",
/* 23 */ "802.1x failed",
/* 24 */ "cipher suite rejected"
};
return reason < VEC_SIZE(reason_str) ? reason_str[reason] : "?";
}
/***********************************************************************
** acx_cmd_status_str
*/
const char*
acx_cmd_status_str(unsigned int state)
{
static const char * const cmd_error_strings[] = {
"Idle",
"Success",
"Unknown Command",
"Invalid Information Element",
"Channel rejected",
"Channel invalid in current regulatory domain",
"MAC invalid",
"Command rejected (read-only information element)",
"Command rejected",
"Already asleep",
"TX in progress",
"Already awake",
"Write only",
"RX in progress",
"Invalid parameter",
"Scan in progress",
"Failed"
};
return state < VEC_SIZE(cmd_error_strings) ?
cmd_error_strings[state] : "?";
}
EXPORT_SYMBOL_GPL(acx_cmd_status_str);
/***********************************************************************
** get_status_string
*/
static inline const char*
get_status_string(unsigned int status)
{
/* A bit shortened, but hopefully still understandable */
static const char * const status_str[] = {
/* 0 */ "Successful",
/* 1 */ "Unspecified failure",
/* 2 */ "reserved",
/* 3 */ "reserved",
/* 4 */ "reserved",
/* 5 */ "reserved",
/* 6 */ "reserved",
/* 7 */ "reserved",
/* 8 */ "reserved",
/* 9 */ "reserved",
/*10 */ "Cannot support all requested capabilities in Capability Information field",
/*11 */ "Reassoc denied (reason outside of 802.11b scope)",
/*12 */ "Assoc denied (reason outside of 802.11b scope), maybe MAC filtering by peer?",
/*13 */ "Responding station doesnt support specified auth algorithm",
/*14 */ "Auth rejected: wrong transaction sequence number",
/*15 */ "Auth rejected: challenge failure",
/*16 */ "Auth rejected: timeout for next frame in sequence",
/*17 */ "Assoc denied: too many STAs on this AP",
/*18 */ "Assoc denied: requesting STA doesnt support all data rates in basic set",
/*19 */ "Assoc denied: requesting STA doesnt support Short Preamble",
/*20 */ "Assoc denied: requesting STA doesnt support PBCC Modulation",
/*21 */ "Assoc denied: requesting STA doesnt support Channel Agility"
/*22 */ "reserved",
/*23 */ "reserved",
/*24 */ "reserved",
/*25 */ "Assoc denied: requesting STA doesnt support Short Slot Time",
/*26 */ "Assoc denied: requesting STA doesnt support DSSS-OFDM"
};
return status_str[status < VEC_SIZE(status_str) ? status : 2];
}
/***********************************************************************
*/
#if ACX_DEBUG
void
acx_dump_bytes(const void *data, int num)
{
const u8* ptr = (const u8*)data;
if (num <= 0) {
printk("\n");
return;
}
while (num >= 16) {
printk( "%02X %02X %02X %02X %02X %02X %02X %02X "
"%02X %02X %02X %02X %02X %02X %02X %02X\n",
ptr[0], ptr[1], ptr[2], ptr[3],
ptr[4], ptr[5], ptr[6], ptr[7],
ptr[8], ptr[9], ptr[10], ptr[11],
ptr[12], ptr[13], ptr[14], ptr[15]);
num -= 16;
ptr += 16;
}
if (num > 0) {
while (--num > 0)
printk("%02X ", *ptr++);
printk("%02X\n", *ptr);
}
}
EXPORT_SYMBOL_GPL(acx_dump_bytes);
#endif
/***********************************************************************
** acx_s_get_firmware_version
*/
void
acx_s_get_firmware_version(acx_device_t *adev)
{
fw_ver_t fw;
u8 hexarr[4] = { 0, 0, 0, 0 };
int hexidx = 0, val = 0;
const char *num;
char c;
FN_ENTER;
memset(fw.fw_id, 'E', FW_ID_SIZE);
acx_s_interrogate(adev, &fw, ACX1xx_IE_FWREV);
memcpy(adev->firmware_version, fw.fw_id, FW_ID_SIZE);
adev->firmware_version[FW_ID_SIZE] = '\0';
log(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n",
adev->firmware_version, fw.hw_id);
if (strncmp(fw.fw_id, "Rev ", 4) != 0) {
printk("acx: strange firmware version string "
"'%s', please report\n", adev->firmware_version);
adev->firmware_numver = 0x01090407; /* assume 1.9.4.7 */
} else {
num = &fw.fw_id[4];
while (1) {
c = *num++;
if ((c == '.') || (c == '\0')) {
hexarr[hexidx++] = val;
if ((hexidx > 3) || (c == '\0')) /* end? */
break;
val = 0;
continue;
}
if ((c >= '0') && (c <= '9'))
c -= '0';
else
c = c - 'a' + (char)10;
val = val*16 + c;
}
adev->firmware_numver = (u32)(
(hexarr[0] << 24) + (hexarr[1] << 16)
+ (hexarr[2] << 8) + hexarr[3]);
log(L_DEBUG, "firmware_numver 0x%08X\n", adev->firmware_numver);
}
if (IS_ACX111(adev)) {
if (adev->firmware_numver == 0x00010011) {
/* This one does not survive floodpinging */
printk("acx: firmware '%s' is known to be buggy, "
"please upgrade\n", adev->firmware_version);
}
}
adev->firmware_id = le32_to_cpu(fw.hw_id);
/* we're able to find out more detailed chip names now */
switch (adev->firmware_id & 0xffff0000) {
case 0x01010000:
case 0x01020000:
adev->chip_name = "TNETW1100A";
break;
case 0x01030000:
adev->chip_name = "TNETW1100B";
break;
case 0x03000000:
case 0x03010000:
adev->chip_name = "TNETW1130";
break;
case 0x04030000: /* 0x04030101 is TNETW1450 */
adev->chip_name = "TNETW1450";
break;
default:
printk("acx: unknown chip ID 0x%08X, "
"please report\n", adev->firmware_id);
break;
}
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_s_get_firmware_version);
/***********************************************************************
** acx_display_hardware_details
**
** Displays hw/fw version, radio type etc...
*/
void
acx_display_hardware_details(acx_device_t *adev)
{
const char *radio_str, *form_str;
FN_ENTER;
switch (adev->radio_type) {
case RADIO_MAXIM_0D:
radio_str = "Maxim";
break;
case RADIO_RFMD_11:
radio_str = "RFMD";
break;
case RADIO_RALINK_15:
radio_str = "Ralink";
break;
case RADIO_RADIA_16:
radio_str = "Radia";
break;
case RADIO_UNKNOWN_17:
/* TI seems to have a radio which is
* additionally 802.11a capable, too */
radio_str = "802.11a/b/g radio?! Please report";
break;
case RADIO_UNKNOWN_19:
radio_str = "A radio used by Safecom cards?! Please report";
break;
case RADIO_UNKNOWN_1B:
radio_str = "An unknown radio used by TNETW1450 USB adapters";
break;
default:
radio_str = "UNKNOWN, please report radio type name!";
break;
}
switch (adev->form_factor) {
case 0x00:
form_str = "unspecified";
break;
case 0x01:
form_str = "(mini-)PCI / CardBus";
break;
case 0x02:
form_str = "USB";
break;
case 0x03:
form_str = "Compact Flash";
break;
default:
form_str = "UNKNOWN, please report";
break;
}
printk("acx: chipset %s, radio type 0x%02X (%s), "
"form factor 0x%02X (%s), EEPROM version 0x%02X: "
"uploaded firmware '%s'\n",
adev->chip_name, adev->radio_type, radio_str,
adev->form_factor, form_str, adev->eeprom_version,
adev->firmware_version);
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_display_hardware_details);
/***********************************************************************
*/
int
acx_e_change_mtu(struct net_device *ndev, int mtu)
{
enum {
MIN_MTU = 256,
MAX_MTU = WLAN_DATA_MAXLEN - (ETH_HLEN)
};
if (mtu < MIN_MTU || mtu > MAX_MTU)
return -EINVAL;
ndev->mtu = mtu;
return 0;
}
EXPORT_SYMBOL_GPL(acx_e_change_mtu);
/***********************************************************************
** acx_e_get_stats, acx_e_get_wireless_stats
*/
struct net_device_stats*
acx_e_get_stats(struct net_device *ndev)
{
acx_device_t *adev = ndev2adev(ndev);
return &adev->stats;
}
EXPORT_SYMBOL_GPL(acx_e_get_stats);
struct iw_statistics*
acx_e_get_wireless_stats(struct net_device *ndev)
{
acx_device_t *adev = ndev2adev(ndev);
return &adev->wstats;
}
/***********************************************************************
** maps acx111 tx descr rate field to acx100 one
*/
const u8
acx_bitpos2rate100[] = {
RATE100_1 ,/* 0 */
RATE100_2 ,/* 1 */
RATE100_5 ,/* 2 */
RATE100_2 ,/* 3, should not happen */
RATE100_2 ,/* 4, should not happen */
RATE100_11 ,/* 5 */
RATE100_2 ,/* 6, should not happen */
RATE100_2 ,/* 7, should not happen */
RATE100_22 ,/* 8 */
RATE100_2 ,/* 9, should not happen */
RATE100_2 ,/* 10, should not happen */
RATE100_2 ,/* 11, should not happen */
RATE100_2 ,/* 12, should not happen */
RATE100_2 ,/* 13, should not happen */
RATE100_2 ,/* 14, should not happen */
RATE100_2 ,/* 15, should not happen */
};
u8
acx_rate111to100(u16 r) {
return acx_bitpos2rate100[highest_bit(r)];
}
/***********************************************************************
** Calculate level like the feb 2003 windows driver seems to do
*/
static u8
acx_signal_to_winlevel(u8 rawlevel)
{
/* u8 winlevel = (u8) (0.5 + 0.625 * rawlevel); */
u8 winlevel = ((4 + (rawlevel * 5)) / 8);
if (winlevel > 100)
winlevel = 100;
return winlevel;
}
u8
acx_signal_determine_quality(u8 signal, u8 noise)
{
int qual;
qual = (((signal - 30) * 100 / 70) + (100 - noise * 4)) / 2;
if (qual > 100)
return 100;
if (qual < 0)
return 0;
return qual;
}
EXPORT_SYMBOL_GPL(acx_signal_determine_quality);
/***********************************************************************
** Interrogate/configure commands
*/
/* FIXME: the lengths given here probably aren't always correct.
* They should be gradually replaced by proper "sizeof(acx1XX_ie_XXXX)-4",
* unless the firmware actually expects a different length than the struct length */
static const u16
acx100_ie_len[] = {
0,
ACX100_IE_ACX_TIMER_LEN,
sizeof(acx100_ie_powersave_t)-4, /* is that 6 or 8??? */
ACX1xx_IE_QUEUE_CONFIG_LEN,
ACX100_IE_BLOCK_SIZE_LEN,
ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN,
ACX1xx_IE_RATE_FALLBACK_LEN,
ACX100_IE_WEP_OPTIONS_LEN,
ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */
0,
ACX1xx_IE_ASSOC_ID_LEN,
0,
ACX111_IE_CONFIG_OPTIONS_LEN,
ACX1xx_IE_FWREV_LEN,
ACX1xx_IE_FCS_ERROR_COUNT_LEN,
ACX1xx_IE_MEDIUM_USAGE_LEN,
ACX1xx_IE_RXCONFIG_LEN,
0,
0,
sizeof(fw_stats_t)-4,
0,
ACX1xx_IE_FEATURE_CONFIG_LEN,
ACX111_IE_KEY_CHOOSE_LEN,
ACX1FF_IE_MISC_CONFIG_TABLE_LEN,
ACX1FF_IE_WONE_CONFIG_LEN,
0,
ACX1FF_IE_TID_CONFIG_LEN,
0,
0,
0,
ACX1FF_IE_CALIB_ASSESSMENT_LEN,
ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN,
ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN,
ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN,
0,
ACX1FF_IE_PACKET_DETECT_THRESH_LEN,
ACX1FF_IE_TX_CONFIG_OPTIONS_LEN,
ACX1FF_IE_CCA_THRESHOLD_LEN,
ACX1FF_IE_EVENT_MASK_LEN,
ACX1FF_IE_DTIM_PERIOD_LEN,
0,
ACX1FF_IE_ACI_CONFIG_SET_LEN,
0,
0,
0,
0,
0,
0,
ACX1FF_IE_EEPROM_VER_LEN,
};
static const u16
acx100_ie_len_dot11[] = {
0,
ACX1xx_IE_DOT11_STATION_ID_LEN,
0,
ACX100_IE_DOT11_BEACON_PERIOD_LEN,
ACX1xx_IE_DOT11_DTIM_PERIOD_LEN,
ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN,
ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN,
ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN,
ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN,
0,
ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN,
ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN,
0,
ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN,
ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN,
ACX100_IE_DOT11_ED_THRESHOLD_LEN,
ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN,
0,
0,
0,
};
static const u16
acx111_ie_len[] = {
0,
ACX100_IE_ACX_TIMER_LEN,
sizeof(acx111_ie_powersave_t)-4,
ACX1xx_IE_QUEUE_CONFIG_LEN,
ACX100_IE_BLOCK_SIZE_LEN,
ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN,
ACX1xx_IE_RATE_FALLBACK_LEN,
ACX100_IE_WEP_OPTIONS_LEN,
ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */
0,
ACX1xx_IE_ASSOC_ID_LEN,
0,
ACX111_IE_CONFIG_OPTIONS_LEN,
ACX1xx_IE_FWREV_LEN,
ACX1xx_IE_FCS_ERROR_COUNT_LEN,
ACX1xx_IE_MEDIUM_USAGE_LEN,
ACX1xx_IE_RXCONFIG_LEN,
0,
0,
sizeof(fw_stats_t)-4,
0,
ACX1xx_IE_FEATURE_CONFIG_LEN,
ACX111_IE_KEY_CHOOSE_LEN,
ACX1FF_IE_MISC_CONFIG_TABLE_LEN,
ACX1FF_IE_WONE_CONFIG_LEN,
0,
ACX1FF_IE_TID_CONFIG_LEN,
0,
0,
0,
ACX1FF_IE_CALIB_ASSESSMENT_LEN,
ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN,
ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN,
ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN,
0,
ACX1FF_IE_PACKET_DETECT_THRESH_LEN,
ACX1FF_IE_TX_CONFIG_OPTIONS_LEN,
ACX1FF_IE_CCA_THRESHOLD_LEN,
ACX1FF_IE_EVENT_MASK_LEN,
ACX1FF_IE_DTIM_PERIOD_LEN,
0,
ACX1FF_IE_ACI_CONFIG_SET_LEN,
0,
0,
0,
0,
0,
0,
ACX1FF_IE_EEPROM_VER_LEN,
};
static const u16
acx111_ie_len_dot11[] = {
0,
ACX1xx_IE_DOT11_STATION_ID_LEN,
0,
ACX100_IE_DOT11_BEACON_PERIOD_LEN,
ACX1xx_IE_DOT11_DTIM_PERIOD_LEN,
ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN,
ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN,
ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN,
ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN,
0,
ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN,
ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN,
0,
ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN,
ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN,
ACX100_IE_DOT11_ED_THRESHOLD_LEN,
ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN,
0,
0,
0,
};
#undef FUNC
#define FUNC "configure"
#if !ACX_DEBUG
int
acx_s_configure(acx_device_t *adev, void *pdr, int type)
#else
int
acx_s_configure_debug(acx_device_t *adev, void *pdr, int type, const char* typestr)
#endif
{
u16 len;
int res;
if (type < 0x1000)
len = adev->ie_len[type];
else
len = adev->ie_len_dot11[type - 0x1000];
log(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len);
if (unlikely(!len)) {
log(L_DEBUG, "zero-length type %s?!\n", typestr);
}
((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type);
((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len);
res = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIGURE, pdr, len + 4);
if (unlikely(OK != res)) {
#if ACX_DEBUG
printk("%s: "FUNC"(type:%s) FAILED\n", adev->ndev->name, typestr);
#else
printk("%s: "FUNC"(type:0x%X) FAILED\n", adev->ndev->name, type);
#endif
/* dump_stack() is already done in issue_cmd() */
}
return res;
}
#undef FUNC
#define FUNC "interrogate"
#if !ACX_DEBUG
int
acx_s_interrogate(acx_device_t *adev, void *pdr, int type)
#else
int
acx_s_interrogate_debug(acx_device_t *adev, void *pdr, int type,
const char* typestr)
#endif
{
u16 len;
int res;
/* FIXME: no check whether this exceeds the array yet.
* We should probably remember the number of entries... */
if (type < 0x1000)
len = adev->ie_len[type];
else
len = adev->ie_len_dot11[type-0x1000];
log(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len);
((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type);
((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len);
res = acx_s_issue_cmd(adev, ACX1xx_CMD_INTERROGATE, pdr, len + 4);
if (unlikely(OK != res)) {
#if ACX_DEBUG
printk("%s: "FUNC"(type:%s) FAILED\n", adev->ndev->name, typestr);
#else
printk("%s: "FUNC"(type:0x%X) FAILED\n", adev->ndev->name, type);
#endif
/* dump_stack() is already done in issue_cmd() */
}
return res;
}
#if ACX_DEBUG
EXPORT_SYMBOL_GPL(acx_s_interrogate_debug);
#else
EXPORT_SYMBOL_GPL(acx_s_interrogate);
#endif /* ACX_DEBUG */
#if CMD_DISCOVERY
void
great_inquisitor(acx_device_t *adev)
{
static struct {
u16 type;
u16 len;
/* 0x200 was too large here: */
u8 data[0x100 - 4];
} ACX_PACKED ie;
u16 type;
FN_ENTER;
/* 0..0x20, 0x1000..0x1020 */
for (type = 0; type <= 0x1020; type++) {
if (type == 0x21)
type = 0x1000;
ie.type = cpu_to_le16(type);
ie.len = cpu_to_le16(sizeof(ie) - 4);
acx_s_issue_cmd(adev, ACX1xx_CMD_INTERROGATE, &ie, sizeof(ie));
}
FN_EXIT0;
}
#endif
#ifdef CONFIG_PROC_FS
/***********************************************************************
** /proc files
*/
/***********************************************************************
** acx_l_proc_output
** Generate content for our /proc entry
**
** Arguments:
** buf is a pointer to write output to
** adev is the usual pointer to our private struct acx_device
** Returns:
** number of bytes actually written to buf
** Side effects:
** none
*/
static int
acx_l_proc_output(char *buf, acx_device_t *adev)
{
char *p = buf;
int i;
FN_ENTER;
p += sprintf(p,
"acx driver version:\t\t" ACX_RELEASE "\n"
"Wireless extension version:\t" STRING(WIRELESS_EXT) "\n"
"chip name:\t\t\t%s (0x%08X)\n"
"radio type:\t\t\t0x%02X\n"
"form factor:\t\t\t0x%02X\n"
"EEPROM version:\t\t\t0x%02X\n"
"firmware version:\t\t%s (0x%08X)\n",
adev->chip_name, adev->firmware_id,
adev->radio_type,
adev->form_factor,
adev->eeprom_version,
adev->firmware_version, adev->firmware_numver);
for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
struct client *bss = &adev->sta_list[i];
if (!bss->used) continue;
p += sprintf(p, "BSS %u BSSID "MACSTR" ESSID %s channel %u "
"Cap 0x%X SIR %u SNR %u\n",
i, MAC(bss->bssid), (char*)bss->essid, bss->channel,
bss->cap_info, bss->sir, bss->snr);
}
p += sprintf(p, "status:\t\t\t%u (%s)\n",
adev->status, acx_get_status_name(adev->status));
FN_EXIT1(p - buf);
return p - buf;
}
/***********************************************************************
*/
static int
acx_s_proc_diag_output(char *buf, acx_device_t *adev)
{
char *p = buf;
unsigned long flags;
unsigned int len = 0, partlen;
u32 temp1, temp2;
u8 *st, *st_end;
#ifdef __BIG_ENDIAN
u8 *st2;
#endif
fw_stats_t *fw_stats;
char *part_str = NULL;
fw_stats_tx_t *tx = NULL;
fw_stats_rx_t *rx = NULL;
fw_stats_dma_t *dma = NULL;
fw_stats_irq_t *irq = NULL;
fw_stats_wep_t *wep = NULL;
fw_stats_pwr_t *pwr = NULL;
fw_stats_mic_t *mic = NULL;
fw_stats_aes_t *aes = NULL;
fw_stats_event_t *evt = NULL;
FN_ENTER;
acx_lock(adev, flags);
if (IS_PCI(adev))
p = acxpci_s_proc_diag_output(p, adev);
p += sprintf(p,
"\n"
"** network status **\n"
"dev_state_mask 0x%04X\n"
"status %u (%s), "
"mode %u, channel %u, "
"reg_dom_id 0x%02X, reg_dom_chanmask 0x%04X, ",
adev->dev_state_mask,
adev->status, acx_get_status_name(adev->status),
adev->mode, adev->channel,
adev->reg_dom_id, adev->reg_dom_chanmask
);
p += sprintf(p,
"ESSID \"%s\", essid_active %d, essid_len %d, "
"essid_for_assoc \"%s\", nick \"%s\"\n"
"WEP ena %d, restricted %d, idx %d\n",
adev->essid, adev->essid_active, (int)adev->essid_len,
adev->essid_for_assoc, adev->nick,
adev->ieee->sec.enabled, adev->ieee->sec.auth_mode,
adev->ieee->sec.active_key);
p += sprintf(p, "dev_addr "MACSTR"\n", MAC(adev->dev_addr));
p += sprintf(p, "bssid "MACSTR"\n", MAC(adev->bssid));
p += sprintf(p, "ap_filter "MACSTR"\n", MAC(adev->ap));
p += sprintf(p,
"\n"
"** PHY status **\n"
"tx_disabled %d, tx_level_dbm %d\n" /* "tx_level_val %d, tx_level_auto %d\n" */
"sensitivity %d, antenna 0x%02X, ed_threshold %d, cca %d, preamble_mode %d\n"
"rts_threshold %d, frag_threshold %d, short_retry %d, long_retry %d\n"
"msdu_lifetime %d, listen_interval %d, beacon_interval %d\n",
adev->tx_disabled, adev->tx_level_dbm, /* adev->tx_level_val, adev->tx_level_auto, */
adev->sensitivity, adev->antenna, adev->ed_threshold, adev->cca, adev->preamble_mode,
adev->rts_threshold, adev->frag_threshold, adev->short_retry, adev->long_retry,
adev->msdu_lifetime, adev->listen_interval, adev->beacon_interval);
acx_unlock(adev, flags);
p += sprintf(p,
"\n"
"** Firmware **\n"
"NOTE: version dependent statistics layout, "
"please report if you suspect wrong parsing!\n"
"\n"
"version \"%s\"\n", adev->firmware_version);
/* TODO: may replace kmalloc/memset with kzalloc once
* Linux 2.6.14 is widespread */
fw_stats = kmalloc(sizeof(*fw_stats), GFP_KERNEL);
if (!fw_stats) {
FN_EXIT1(0);
return 0;
}
memset(fw_stats, 0, sizeof(*fw_stats));
st = (u8 *)fw_stats;
part_str = "statistics query command";
if (OK != acx_s_interrogate(adev, st, ACX1xx_IE_FIRMWARE_STATISTICS))
goto fw_stats_end;
st += sizeof(u16);
len = *(u16 *)st;
if (len > sizeof(*fw_stats)) {
p += sprintf(p,
"firmware version with bigger fw_stats struct detected\n"
"(%u vs. %u), please report\n", len, sizeof(fw_stats_t));
if (len > sizeof(*fw_stats)) {
p += sprintf(p, "struct size exceeded allocation!\n");
len = sizeof(*fw_stats);
}
}
st += sizeof(u16);
st_end = st - 2*sizeof(u16) + len;
#ifdef __BIG_ENDIAN
/* let's make one bold assumption here:
* (hopefully!) *all* statistics fields are u32 only,
* thus if we need to make endianness corrections
* we can simply do them in one go, in advance */
st2 = (u8 *)fw_stats;
for (temp1 = 0; temp1 < len; temp1 += 4, st2 += 4)
*(u32 *)st2 = le32_to_cpu(*(u32 *)st2);
#endif
part_str = "Rx/Tx";
/* directly at end of a struct part? --> no error! */
if (st == st_end)
goto fw_stats_end;
tx = (fw_stats_tx_t *)st;
st += sizeof(fw_stats_tx_t);
rx = (fw_stats_rx_t *)st;
st += sizeof(fw_stats_rx_t);
partlen = sizeof(fw_stats_tx_t) + sizeof(fw_stats_rx_t);
if (IS_ACX100(adev)) {
/* at least ACX100 PCI F/W 1.9.8.b
* and ACX100 USB F/W 1.0.7-USB
* don't have those two fields... */
st -= 2*sizeof(u32);
/* our parsing doesn't quite match this firmware yet,
* log failure */
if (st > st_end)
goto fw_stats_fail;
temp1 = temp2 = 999999999;
} else {
if (st > st_end)
goto fw_stats_fail;
temp1 = rx->rx_aci_events;
temp2 = rx->rx_aci_resets;
}
p += sprintf(p,
"%s:\n"
" tx_desc_overfl %u\n"
" rx_OutOfMem %u, rx_hdr_overfl %u, rx_hw_stuck %u\n"
" rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u\n"
" rx_aci_events %u, rx_aci_resets %u\n",
part_str,
tx->tx_desc_of,
rx->rx_oom,
rx->rx_hdr_of,
rx->rx_hw_stuck,
rx->rx_dropped_frame,
rx->rx_frame_ptr_err,
rx->rx_xfr_hint_trig,
temp1,
temp2);
part_str = "DMA";
if (st == st_end)
goto fw_stats_end;
dma = (fw_stats_dma_t *)st;
partlen = sizeof(fw_stats_dma_t);
st += partlen;
if (st > st_end)
goto fw_stats_fail;
p += sprintf(p,
"%s:\n"
" rx_dma_req %u, rx_dma_err %u, tx_dma_req %u, tx_dma_err %u\n",
part_str,
dma->rx_dma_req,
dma->rx_dma_err,
dma->tx_dma_req,
dma->tx_dma_err);
part_str = "IRQ";
if (st == st_end)
goto fw_stats_end;
irq = (fw_stats_irq_t *)st;
partlen = sizeof(fw_stats_irq_t);
st += partlen;
if (st > st_end)
goto fw_stats_fail;
p += sprintf(p,
"%s:\n"
" cmd_cplt %u, fiq %u\n"
" rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u\n"
" irqs %u, tx_procs %u, decrypt_done %u\n"
" dma_0_done %u, dma_1_done %u, tx_exch_complet %u\n"
" commands %u, rx_procs %u, hw_pm_mode_changes %u\n"
" host_acks %u, pci_pm %u, acm_wakeups %u\n",
part_str,
irq->cmd_cplt,
irq->fiq,
irq->rx_hdrs,
irq->rx_cmplt,
irq->rx_mem_of,
irq->rx_rdys,
irq->irqs,
irq->tx_procs,
irq->decrypt_done,
irq->dma_0_done,
irq->dma_1_done,
irq->tx_exch_complet,
irq->commands,
irq->rx_procs,
irq->hw_pm_mode_changes,
irq->host_acks,
irq->pci_pm,
irq->acm_wakeups);
part_str = "WEP";
if (st == st_end)
goto fw_stats_end;
wep = (fw_stats_wep_t *)st;
partlen = sizeof(fw_stats_wep_t);
st += partlen;
if (
(IS_PCI(adev) && IS_ACX100(adev))
|| (IS_USB(adev) && IS_ACX100(adev))
) {
/* at least ACX100 PCI F/W 1.9.8.b
* and ACX100 USB F/W 1.0.7-USB
* don't have those two fields... */
st -= 2*sizeof(u32);
if (st > st_end)
goto fw_stats_fail;
temp1 = temp2 = 999999999;
} else {
if (st > st_end)
goto fw_stats_fail;
temp1 = wep->wep_pkt_decrypt;
temp2 = wep->wep_decrypt_irqs;
}
p += sprintf(p,
"%s:\n"
" wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n"
" wep_key_not_found %u, wep_decrypt_fail %u\n"
" wep_pkt_decrypt %u, wep_decrypt_irqs %u\n",
part_str,
wep->wep_key_count,
wep->wep_default_key_count,
wep->dot11_def_key_mib,
wep->wep_key_not_found,
wep->wep_decrypt_fail,
temp1,
temp2);
part_str = "power";
if (st == st_end)
goto fw_stats_end;
pwr = (fw_stats_pwr_t *)st;
partlen = sizeof(fw_stats_pwr_t);
st += partlen;
if (st > st_end)
goto fw_stats_fail;
p += sprintf(p,
"%s:\n"
" tx_start_ctr %u, no_ps_tx_too_short %u\n"
" rx_start_ctr %u, no_ps_rx_too_short %u\n"
" lppd_started %u\n"
" no_lppd_too_noisy %u, no_lppd_too_short %u, no_lppd_matching_frame %u\n",
part_str,
pwr->tx_start_ctr,
pwr->no_ps_tx_too_short,
pwr->rx_start_ctr,
pwr->no_ps_rx_too_short,
pwr->lppd_started,
pwr->no_lppd_too_noisy,
pwr->no_lppd_too_short,
pwr->no_lppd_matching_frame);
part_str = "MIC";
if (st == st_end)
goto fw_stats_end;
mic = (fw_stats_mic_t *)st;
partlen = sizeof(fw_stats_mic_t);
st += partlen;
if (st > st_end)
goto fw_stats_fail;
p += sprintf(p,
"%s:\n"
" mic_rx_pkts %u, mic_calc_fail %u\n",
part_str,
mic->mic_rx_pkts,
mic->mic_calc_fail);
part_str = "AES";
if (st == st_end)
goto fw_stats_end;
aes = (fw_stats_aes_t *)st;
partlen = sizeof(fw_stats_aes_t);
st += partlen;
if (st > st_end)
goto fw_stats_fail;
p += sprintf(p,
"%s:\n"
" aes_enc_fail %u, aes_dec_fail %u\n"
" aes_enc_pkts %u, aes_dec_pkts %u\n"
" aes_enc_irq %u, aes_dec_irq %u\n",
part_str,
aes->aes_enc_fail,
aes->aes_dec_fail,
aes->aes_enc_pkts,
aes->aes_dec_pkts,
aes->aes_enc_irq,
aes->aes_dec_irq);
part_str = "event";
if (st == st_end)
goto fw_stats_end;
evt = (fw_stats_event_t *)st;
partlen = sizeof(fw_stats_event_t);
st += partlen;
if (st > st_end)
goto fw_stats_fail;
p += sprintf(p,
"%s:\n"
" heartbeat %u, calibration %u\n"
" rx_mismatch %u, rx_mem_empty %u, rx_pool %u\n"
" oom_late %u\n"
" phy_tx_err %u, tx_stuck %u\n",
part_str,
evt->heartbeat,
evt->calibration,
evt->rx_mismatch,
evt->rx_mem_empty,
evt->rx_pool,
evt->oom_late,
evt->phy_tx_err,
evt->tx_stuck);
if (st < st_end)
goto fw_stats_bigger;
goto fw_stats_end;
fw_stats_fail:
st -= partlen;
p += sprintf(p,
"failed at %s part (size %u), offset %u (struct size %u), "
"please report\n", part_str, partlen,
(int)st - (int)fw_stats, len);
fw_stats_bigger:
for (; st < st_end; st += 4)
p += sprintf(p,
"UNKN%3d: %u\n", (int)st - (int)fw_stats, *(u32 *)st);
fw_stats_end:
kfree(fw_stats);
FN_EXIT1(p - buf);
return p - buf;
}
/***********************************************************************
*/
static int
acx_s_proc_phy_output(char *buf, acx_device_t *adev)
{
char *p = buf;
int i;
FN_ENTER;
/*
if (RADIO_RFMD_11 != adev->radio_type) {
printk("sorry, not yet adapted for radio types "
"other than RFMD, please verify "
"PHY size etc. first!\n");
goto end;
}
*/
/* The PHY area is only 0x80 bytes long; further pages after that
* only have some page number registers with altered value,
* all other registers remain the same. */
for (i = 0; i < 0x80; i++) {
acx_s_read_phy_reg(adev, i, p++);
}
FN_EXIT1(p - buf);
return p - buf;
}
/***********************************************************************
** acx_e_read_proc_XXXX
** Handle our /proc entry
**
** Arguments:
** standard kernel read_proc interface
** Returns:
** number of bytes written to buf
** Side effects:
** none
*/
static int
acx_e_read_proc(char *buf, char **start, off_t offset, int count,
int *eof, void *data)
{
acx_device_t *adev = (acx_device_t*)data;
unsigned long flags;
int length;
FN_ENTER;
acx_sem_lock(adev);
acx_lock(adev, flags);
/* fill buf */
length = acx_l_proc_output(buf, adev);
acx_unlock(adev, flags);
acx_sem_unlock(adev);
/* housekeeping */
if (length <= offset + count)
*eof = 1;
*start = buf + offset;
length -= offset;
if (length > count)
length = count;
if (length < 0)
length = 0;
FN_EXIT1(length);
return length;
}
static int
acx_e_read_proc_diag(char *buf, char **start, off_t offset, int count,
int *eof, void *data)
{
acx_device_t *adev = (acx_device_t*)data;
int length;
FN_ENTER;
acx_sem_lock(adev);
/* fill buf */
length = acx_s_proc_diag_output(buf, adev);
acx_sem_unlock(adev);
/* housekeeping */
if (length <= offset + count)
*eof = 1;
*start = buf + offset;
length -= offset;
if (length > count)
length = count;
if (length < 0)
length = 0;
FN_EXIT1(length);
return length;
}
static int
acx_e_read_proc_eeprom(char *buf, char **start, off_t offset, int count,
int *eof, void *data)
{
acx_device_t *adev = (acx_device_t*)data;
int length;
FN_ENTER;
/* fill buf */
length = 0;
if (IS_PCI(adev)) {
acx_sem_lock(adev);
length = acxpci_proc_eeprom_output(buf, adev);
acx_sem_unlock(adev);
}
/* housekeeping */
if (length <= offset + count)
*eof = 1;
*start = buf + offset;
length -= offset;
if (length > count)
length = count;
if (length < 0)
length = 0;
FN_EXIT1(length);
return length;
}
static int
acx_e_read_proc_phy(char *buf, char **start, off_t offset, int count,
int *eof, void *data)
{
acx_device_t *adev = (acx_device_t*)data;
int length;
FN_ENTER;
acx_sem_lock(adev);
/* fill buf */
length = acx_s_proc_phy_output(buf, adev);
acx_sem_unlock(adev);
/* housekeeping */
if (length <= offset + count)
*eof = 1;
*start = buf + offset;
length -= offset;
if (length > count)
length = count;
if (length < 0)
length = 0;
FN_EXIT1(length);
return length;
}
/***********************************************************************
** /proc files registration
*/
static const char * const
proc_files[] = { "", "_diag", "_eeprom", "_phy" };
static read_proc_t * const
proc_funcs[] = {
acx_e_read_proc,
acx_e_read_proc_diag,
acx_e_read_proc_eeprom,
acx_e_read_proc_phy
};
static int
manage_proc_entries(const struct net_device *ndev, int remove)
{
acx_device_t *adev = ndev2adev((struct net_device *)ndev);
char procbuf[80];
int i;
for (i = 0; i < VEC_SIZE(proc_files); i++) {
snprintf(procbuf, sizeof(procbuf),
"driver/acx_%s%s", ndev->name, proc_files[i]);
log(L_INIT, "%sing /proc entry %s\n",
remove ? "remov" : "creat", procbuf);
if (!remove) {
if (!create_proc_read_entry(procbuf, 0, NULL, proc_funcs[i], adev)) {
printk("acx: cannot register /proc entry %s\n", procbuf);
return NOT_OK;
}
} else {
remove_proc_entry(procbuf, NULL);
}
}
return OK;
}
int
acx_proc_register_entries(const struct net_device *ndev)
{
return manage_proc_entries(ndev, 0);
}
EXPORT_SYMBOL_GPL(acx_proc_register_entries);
int
acx_proc_unregister_entries(const struct net_device *ndev)
{
return manage_proc_entries(ndev, 1);
}
EXPORT_SYMBOL_GPL(acx_proc_unregister_entries);
#endif /* CONFIG_PROC_FS */
/***********************************************************************
** acx_cmd_join_bssid
**
** Common code for both acx100 and acx111.
*/
/* NB: does NOT match RATE100_nn but matches ACX[111]_SCAN_RATE_n */
static const u8
bitpos2genframe_txrate[] = {
10, /* 0. 1 Mbit/s */
20, /* 1. 2 Mbit/s */
55, /* 2. 5.5 Mbit/s */
0x0B, /* 3. 6 Mbit/s */
0x0F, /* 4. 9 Mbit/s */
110, /* 5. 11 Mbit/s */
0x0A, /* 6. 12 Mbit/s */
0x0E, /* 7. 18 Mbit/s */
220, /* 8. 22 Mbit/s */
0x09, /* 9. 24 Mbit/s */
0x0D, /* 10. 36 Mbit/s */
0x08, /* 11. 48 Mbit/s */
0x0C, /* 12. 54 Mbit/s */
10, /* 13. 1 Mbit/s, should never happen */
10, /* 14. 1 Mbit/s, should never happen */
10, /* 15. 1 Mbit/s, should never happen */
};
/* Looks scary, eh?
** Actually, each one compiled into one AND and one SHIFT,
** 31 bytes in x86 asm (more if uints are replaced by u16/u8) */
static inline unsigned int
rate111to5bits(unsigned int rate)
{
return (rate & 0x7)
| ( (rate & RATE111_11) / (RATE111_11/JOINBSS_RATES_11) )
| ( (rate & RATE111_22) / (RATE111_22/JOINBSS_RATES_22) )
;
}
static void
acx_s_cmd_join_bssid(acx_device_t *adev, const u8 *bssid)
{
acx_joinbss_t tmp;
int dtim_interval;
int i;
if (mac_is_zero(bssid))
return;
FN_ENTER;
dtim_interval = (ACX_MODE_0_ADHOC == adev->mode) ?
1 : adev->dtim_interval;
memset(&tmp, 0, sizeof(tmp));
for (i = 0; i < ETH_ALEN; i++) {
tmp.bssid[i] = bssid[ETH_ALEN-1 - i];
}
tmp.beacon_interval = cpu_to_le16(adev->beacon_interval);
/* Basic rate set. Control frame responses (such as ACK or CTS frames)
** are sent with one of these rates */
if (IS_ACX111(adev)) {
/* It was experimentally determined that rates_basic
** can take 11g rates as well, not only rates
** defined with JOINBSS_RATES_BASIC111_nnn.
** Just use RATE111_nnn constants... */
tmp.u.acx111.dtim_interval = dtim_interval;
tmp.u.acx111.rates_basic = cpu_to_le16(adev->rate_basic);
log(L_ASSOC, "rates_basic:%04X, rates_supported:%04X\n",
adev->rate_basic, adev->rate_oper);
} else {
tmp.u.acx100.dtim_interval = dtim_interval;
tmp.u.acx100.rates_basic = rate111to5bits(adev->rate_basic);
tmp.u.acx100.rates_supported = rate111to5bits(adev->rate_oper);
log(L_ASSOC, "rates_basic:%04X->%02X, "
"rates_supported:%04X->%02X\n",
adev->rate_basic, tmp.u.acx100.rates_basic,
adev->rate_oper, tmp.u.acx100.rates_supported);
}
/* Setting up how Beacon, Probe Response, RTS, and PS-Poll frames
** will be sent (rate/modulation/preamble) */
tmp.genfrm_txrate = bitpos2genframe_txrate[lowest_bit(adev->rate_basic)];
tmp.genfrm_mod_pre = 0; /* FIXME: was = adev->capab_short (which was always 0); */
/* we can use short pre *if* all peers can understand it */
/* FIXME #2: we need to correctly set PBCC/OFDM bits here too */
/* we switch fw to STA mode in MONITOR mode, it seems to be
** the only mode where fw does not emit beacons by itself
** but allows us to send anything (we really want to retain
** ability to tx arbitrary frames in MONITOR mode)
*/
tmp.macmode = (adev->mode != ACX_MODE_MONITOR ? adev->mode : ACX_MODE_2_STA);
tmp.channel = adev->channel;
tmp.essid_len = adev->essid_len;
/* NOTE: the code memcpy'd essid_len + 1 before, which is WRONG! */
memcpy(tmp.essid, adev->essid, tmp.essid_len);
acx_s_issue_cmd(adev, ACX1xx_CMD_JOIN, &tmp, tmp.essid_len + 0x11);
log(L_ASSOC|L_DEBUG, "BSS_Type = %u\n", tmp.macmode);
acxlog_mac(L_ASSOC|L_DEBUG, "JoinBSSID MAC:", adev->bssid, "\n");
acx_update_capabilities(adev);
FN_EXIT0;
}
/***********************************************************************
** acx_s_cmd_start_scan
**
** Issue scan command to the hardware
**
** unified function for both ACX111 and ACX100
*/
static void
acx_s_scan_chan(acx_device_t *adev)
{
union {
acx111_scan_t acx111;
acx100_scan_t acx100;
} s;
FN_ENTER;
memset(&s, 0, sizeof(s));
/* first common positions... */
s.acx111.count = cpu_to_le16(adev->scan_count);
s.acx111.rate = adev->scan_rate;
s.acx111.options = adev->scan_mode;
s.acx111.chan_duration = cpu_to_le16(adev->scan_duration);
s.acx111.max_probe_delay = cpu_to_le16(adev->scan_probe_delay);
/* ...then differences */
if (IS_ACX111(adev)) {
s.acx111.channel_list_select = 0; /* scan every allowed channel */
/*s.acx111.channel_list_select = 1;*/ /* scan given channels */
/*s.acx111.modulation = 0x40;*/ /* long preamble? OFDM? -> only for active scan */
s.acx111.modulation = 0;
/*s.acx111.channel_list[0] = 6;
s.acx111.channel_list[1] = 4;*/
} else {
s.acx100.start_chan = cpu_to_le16(1);
s.acx100.flags = cpu_to_le16(0x8000);
}
acx_s_issue_cmd(adev, ACX1xx_CMD_SCAN, &s, sizeof(s));
FN_EXIT0;
}
void
acx_s_cmd_start_scan(acx_device_t *adev)
{
/* time_before check is 'just in case' thing */
if (!(adev->irq_status & HOST_INT_SCAN_COMPLETE)
&& time_before(jiffies, adev->scan_start + 10*HZ)
) {
log(L_INIT, "start_scan: seems like previous scan "
"is still running. Not starting anew. Please report\n");
return;
}
log(L_INIT, "starting radio scan\n");
/* remember that fw is commanded to do scan */
adev->scan_start = jiffies;
CLEAR_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE);
/* issue it */
acx_s_scan_chan(adev);
}
/***********************************************************************
** acx111 feature config
*/
static int
acx111_s_get_feature_config(acx_device_t *adev,
u32 *feature_options, u32 *data_flow_options)
{
struct acx111_ie_feature_config feat;
if (!IS_ACX111(adev)) {
return NOT_OK;
}
memset(&feat, 0, sizeof(feat));
if (OK != acx_s_interrogate(adev, &feat, ACX1xx_IE_FEATURE_CONFIG)) {
return NOT_OK;
}
log(L_DEBUG,
"got Feature option:0x%X, DataFlow option: 0x%X\n",
feat.feature_options,
feat.data_flow_options);
if (feature_options)
*feature_options = le32_to_cpu(feat.feature_options);
if (data_flow_options)
*data_flow_options = le32_to_cpu(feat.data_flow_options);
return OK;
}
static int
acx111_s_set_feature_config(acx_device_t *adev,
u32 feature_options, u32 data_flow_options,
unsigned int mode /* 0 == remove, 1 == add, 2 == set */)
{
struct acx111_ie_feature_config feat;
if (!IS_ACX111(adev)) {
return NOT_OK;
}
if ((mode < 0) || (mode > 2))
return NOT_OK;
if (mode != 2)
/* need to modify old data */
acx111_s_get_feature_config(adev, &feat.feature_options, &feat.data_flow_options);
else {
/* need to set a completely new value */
feat.feature_options = 0;
feat.data_flow_options = 0;
}
if (mode == 0) { /* remove */
CLEAR_BIT(feat.feature_options, cpu_to_le32(feature_options));
CLEAR_BIT(feat.data_flow_options, cpu_to_le32(data_flow_options));
} else { /* add or set */
SET_BIT(feat.feature_options, cpu_to_le32(feature_options));
SET_BIT(feat.data_flow_options, cpu_to_le32(data_flow_options));
}
log(L_DEBUG,
"old: feature 0x%08X dataflow 0x%08X. mode: %u\n"
"new: feature 0x%08X dataflow 0x%08X\n",
feature_options, data_flow_options, mode,
le32_to_cpu(feat.feature_options),
le32_to_cpu(feat.data_flow_options));
if (OK != acx_s_configure(adev, &feat, ACX1xx_IE_FEATURE_CONFIG)) {
return NOT_OK;
}
return OK;
}
static inline int
acx111_s_feature_off(acx_device_t *adev, u32 f, u32 d)
{
return acx111_s_set_feature_config(adev, f, d, 0);
}
static inline int
acx111_s_feature_on(acx_device_t *adev, u32 f, u32 d)
{
return acx111_s_set_feature_config(adev, f, d, 1);
}
static inline int
acx111_s_feature_set(acx_device_t *adev, u32 f, u32 d)
{
return acx111_s_set_feature_config(adev, f, d, 2);
}
/***********************************************************************
** acx100_s_init_memory_pools
*/
static int
acx100_s_init_memory_pools(acx_device_t *adev, const acx_ie_memmap_t *mmt)
{
acx100_ie_memblocksize_t MemoryBlockSize;
acx100_ie_memconfigoption_t MemoryConfigOption;
int TotalMemoryBlocks;
int RxBlockNum;
int TotalRxBlockSize;
int TxBlockNum;
int TotalTxBlockSize;
FN_ENTER;
/* Let's see if we can follow this:
first we select our memory block size (which I think is
completely arbitrary) */
MemoryBlockSize.size = cpu_to_le16(adev->memblocksize);
/* Then we alert the card to our decision of block size */
if (OK != acx_s_configure(adev, &MemoryBlockSize, ACX100_IE_BLOCK_SIZE)) {
goto bad;
}
/* We figure out how many total blocks we can create, using
the block size we chose, and the beginning and ending
memory pointers, i.e.: end-start/size */
TotalMemoryBlocks = (le32_to_cpu(mmt->PoolEnd) - le32_to_cpu(mmt->PoolStart)) / adev->memblocksize;
log(L_DEBUG, "TotalMemoryBlocks=%u (%u bytes)\n",
TotalMemoryBlocks, TotalMemoryBlocks*adev->memblocksize);
/* MemoryConfigOption.DMA_config bitmask:
access to ACX memory is to be done:
0x00080000 using PCI conf space?!
0x00040000 using IO instructions?
0x00000000 using memory access instructions
0x00020000 using local memory block linked list (else what?)
0x00010000 using host indirect descriptors (else host must access ACX memory?)
*/
if (IS_PCI(adev)) {
MemoryConfigOption.DMA_config = cpu_to_le32(0x30000);
/* Declare start of the Rx host pool */
MemoryConfigOption.pRxHostDesc = cpu2acx(adev->rxhostdesc_startphy);
log(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n",
acx2cpu(MemoryConfigOption.pRxHostDesc),
(long)adev->rxhostdesc_startphy);
} else {
MemoryConfigOption.DMA_config = cpu_to_le32(0x20000);
}
/* 50% of the allotment of memory blocks go to tx descriptors */
TxBlockNum = TotalMemoryBlocks / 2;
MemoryConfigOption.TxBlockNum = cpu_to_le16(TxBlockNum);
/* and 50% go to the rx descriptors */
RxBlockNum = TotalMemoryBlocks - TxBlockNum;
MemoryConfigOption.RxBlockNum = cpu_to_le16(RxBlockNum);
/* size of the tx and rx descriptor queues */
TotalTxBlockSize = TxBlockNum * adev->memblocksize;
TotalRxBlockSize = RxBlockNum * adev->memblocksize;
log(L_DEBUG, "TxBlockNum %u RxBlockNum %u TotalTxBlockSize %u "
"TotalTxBlockSize %u\n", TxBlockNum, RxBlockNum,
TotalTxBlockSize, TotalRxBlockSize);
/* align the tx descriptor queue to an alignment of 0x20 (32 bytes) */
MemoryConfigOption.rx_mem =
cpu_to_le32((le32_to_cpu(mmt->PoolStart) + 0x1f) & ~0x1f);
/* align the rx descriptor queue to units of 0x20
* and offset it by the tx descriptor queue */
MemoryConfigOption.tx_mem =
cpu_to_le32((le32_to_cpu(mmt->PoolStart) + TotalRxBlockSize + 0x1f) & ~0x1f);
log(L_DEBUG, "rx_mem %08X rx_mem %08X\n",
MemoryConfigOption.tx_mem, MemoryConfigOption.rx_mem);
/* alert the device to our decision */
if (OK != acx_s_configure(adev, &MemoryConfigOption, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) {
goto bad;
}
/* and tell the device to kick it into gear */
if (OK != acx_s_issue_cmd(adev, ACX100_CMD_INIT_MEMORY, NULL, 0)) {
goto bad;
}
FN_EXIT1(OK);
return OK;
bad:
FN_EXIT1(NOT_OK);
return NOT_OK;
}
/***********************************************************************
** acx100_s_create_dma_regions
**
** Note that this fn messes up heavily with hardware, but we cannot
** lock it (we need to sleep). Not a problem since IRQs can't happen
*/
int
acx100_s_create_dma_regions(acx_device_t *adev)
{
acx100_ie_queueconfig_t queueconf;
acx_ie_memmap_t memmap;
int res = NOT_OK;
u32 tx_queue_start, rx_queue_start;
FN_ENTER;
/* read out the acx100 physical start address for the queues */
if (OK != acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) {
goto fail;
}
tx_queue_start = le32_to_cpu(memmap.QueueStart);
rx_queue_start = tx_queue_start + TX_CNT * sizeof(txdesc_t);
log(L_DEBUG, "initializing Queue Indicator\n");
memset(&queueconf, 0, sizeof(queueconf));
/* Not needed for PCI, so we can avoid setting them altogether */
if (IS_USB(adev)) {
queueconf.NumTxDesc = USB_TX_CNT;
queueconf.NumRxDesc = USB_RX_CNT;
}
/* calculate size of queues */
queueconf.AreaSize = cpu_to_le32(
TX_CNT * sizeof(txdesc_t) +
RX_CNT * sizeof(rxdesc_t) + 8
);
queueconf.NumTxQueues = 1; /* number of tx queues */
/* sets the beginning of the tx descriptor queue */
queueconf.TxQueueStart = memmap.QueueStart;
/* done by memset: queueconf.TxQueuePri = 0; */
queueconf.RxQueueStart = cpu_to_le32(rx_queue_start);
queueconf.QueueOptions = 1; /* auto reset descriptor */
/* sets the end of the rx descriptor queue */
queueconf.QueueEnd = cpu_to_le32(
rx_queue_start + RX_CNT * sizeof(rxdesc_t)
);
/* sets the beginning of the next queue */
queueconf.HostQueueEnd = cpu_to_le32(le32_to_cpu(queueconf.QueueEnd) + 8);
if (OK != acx_s_configure(adev, &queueconf, ACX1xx_IE_QUEUE_CONFIG)) {
goto fail;
}
if (IS_PCI(adev)) {
/* sets the beginning of the rx descriptor queue, after the tx descrs */
if (OK != acxpci_s_create_hostdesc_queues(adev))
goto fail;
acxpci_create_desc_queues(adev, tx_queue_start, rx_queue_start);
}
if (OK != acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) {
goto fail;
}
memmap.PoolStart = cpu_to_le32(
(le32_to_cpu(memmap.QueueEnd) + 4 + 0x1f) & ~0x1f
);
if (OK != acx_s_configure(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) {
goto fail;
}
if (OK != acx100_s_init_memory_pools(adev, &memmap)) {
goto fail;
}
res = OK;
goto end;
fail:
acx_s_msleep(1000); /* ? */
if (IS_PCI(adev))
acxpci_free_desc_queues(adev);
end:
FN_EXIT1(res);
return res;
}
EXPORT_SYMBOL_GPL(acx100_s_create_dma_regions);
/***********************************************************************
** acx111_s_create_dma_regions
**
** Note that this fn messes heavily with hardware, but we cannot
** lock it (we need to sleep). Not a problem since IRQs can't happen
*/
#define ACX111_PERCENT(percent) ((percent)/5)
int
acx111_s_create_dma_regions(acx_device_t *adev)
{
struct acx111_ie_memoryconfig memconf;
struct acx111_ie_queueconfig queueconf;
u32 tx_queue_start, rx_queue_start;
FN_ENTER;
/* Calculate memory positions and queue sizes */
/* Set up our host descriptor pool + data pool */
if (IS_PCI(adev)) {
if (OK != acxpci_s_create_hostdesc_queues(adev))
goto fail;
}
memset(&memconf, 0, sizeof(memconf));
/* the number of STAs (STA contexts) to support
** NB: was set to 1 and everything seemed to work nevertheless... */
memconf.no_of_stations = cpu_to_le16(VEC_SIZE(adev->sta_list));
/* specify the memory block size. Default is 256 */
memconf.memory_block_size = cpu_to_le16(adev->memblocksize);
/* let's use 50%/50% for tx/rx (specify percentage, units of 5%) */
memconf.tx_rx_memory_block_allocation = ACX111_PERCENT(50);
/* set the count of our queues
** NB: struct acx111_ie_memoryconfig shall be modified
** if we ever will switch to more than one rx and/or tx queue */
memconf.count_rx_queues = 1;
memconf.count_tx_queues = 1;
/* 0 == Busmaster Indirect Memory Organization, which is what we want
* (using linked host descs with their allocated mem).
* 2 == Generic Bus Slave */
/* done by memset: memconf.options = 0; */
/* let's use 25% for fragmentations and 75% for frame transfers
* (specified in units of 5%) */
memconf.fragmentation = ACX111_PERCENT(75);
/* Rx descriptor queue config */
memconf.rx_queue1_count_descs = RX_CNT;
memconf.rx_queue1_type = 7; /* must be set to 7 */
/* done by memset: memconf.rx_queue1_prio = 0; low prio */
if (IS_PCI(adev)) {
memconf.rx_queue1_host_rx_start = cpu2acx(adev->rxhostdesc_startphy);
}
/* Tx descriptor queue config */
memconf.tx_queue1_count_descs = TX_CNT;
/* done by memset: memconf.tx_queue1_attributes = 0; lowest priority */
/* NB1: this looks wrong: (memconf,ACX1xx_IE_QUEUE_CONFIG),
** (queueconf,ACX1xx_IE_MEMORY_CONFIG_OPTIONS) look swapped, eh?
** But it is actually correct wrt IE numbers.
** NB2: sizeof(memconf) == 28 == 0x1c but configure(ACX1xx_IE_QUEUE_CONFIG)
** writes 0x20 bytes (because same IE for acx100 uses struct acx100_ie_queueconfig
** which is 4 bytes larger. what a mess. TODO: clean it up) */
if (OK != acx_s_configure(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG)) {
goto fail;
}
acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS);
tx_queue_start = le32_to_cpu(queueconf.tx1_queue_address);
rx_queue_start = le32_to_cpu(queueconf.rx1_queue_address);
log(L_INIT, "dump queue head (from card):\n"
"len: %u\n"
"tx_memory_block_address: %X\n"
"rx_memory_block_address: %X\n"
"tx1_queue address: %X\n"
"rx1_queue address: %X\n",
le16_to_cpu(queueconf.len),
le32_to_cpu(queueconf.tx_memory_block_address),
le32_to_cpu(queueconf.rx_memory_block_address),
tx_queue_start,
rx_queue_start);
if (IS_PCI(adev))
acxpci_create_desc_queues(adev, tx_queue_start, rx_queue_start);
FN_EXIT1(OK);
return OK;
fail:
if (IS_PCI(adev))
acxpci_free_desc_queues(adev);
FN_EXIT1(NOT_OK);
return NOT_OK;
}
EXPORT_SYMBOL_GPL(acx111_s_create_dma_regions);
/***********************************************************************
*/
static void
acx_s_initialize_rx_config(acx_device_t *adev)
{
struct {
u16 id;
u16 len;
u16 rx_cfg1;
u16 rx_cfg2;
} ACX_PACKED cfg;
switch (adev->mode) {
case ACX_MODE_OFF:
adev->rx_config_1 = (u16) (0
/* | RX_CFG1_INCLUDE_RXBUF_HDR */
/* | RX_CFG1_FILTER_SSID */
/* | RX_CFG1_FILTER_BCAST */
/* | RX_CFG1_RCV_MC_ADDR1 */
/* | RX_CFG1_RCV_MC_ADDR0 */
/* | RX_CFG1_FILTER_ALL_MULTI */
/* | RX_CFG1_FILTER_BSSID */
/* | RX_CFG1_FILTER_MAC */
/* | RX_CFG1_RCV_PROMISCUOUS */
/* | RX_CFG1_INCLUDE_FCS */
/* | RX_CFG1_INCLUDE_PHY_HDR */
);
adev->rx_config_2 = (u16) (0
/*| RX_CFG2_RCV_ASSOC_REQ */
/*| RX_CFG2_RCV_AUTH_FRAMES */
/*| RX_CFG2_RCV_BEACON_FRAMES */
/*| RX_CFG2_RCV_CONTENTION_FREE */
/*| RX_CFG2_RCV_CTRL_FRAMES */
/*| RX_CFG2_RCV_DATA_FRAMES */
/*| RX_CFG2_RCV_BROKEN_FRAMES */
/*| RX_CFG2_RCV_MGMT_FRAMES */
/*| RX_CFG2_RCV_PROBE_REQ */
/*| RX_CFG2_RCV_PROBE_RESP */
/*| RX_CFG2_RCV_ACK_FRAMES */
/*| RX_CFG2_RCV_OTHER */
);
break;
case ACX_MODE_MONITOR:
adev->rx_config_1 = (u16) (0
/* | RX_CFG1_INCLUDE_RXBUF_HDR */
/* | RX_CFG1_FILTER_SSID */
/* | RX_CFG1_FILTER_BCAST */
/* | RX_CFG1_RCV_MC_ADDR1 */
/* | RX_CFG1_RCV_MC_ADDR0 */
/* | RX_CFG1_FILTER_ALL_MULTI */
/* | RX_CFG1_FILTER_BSSID */
/* | RX_CFG1_FILTER_MAC */
| RX_CFG1_RCV_PROMISCUOUS
/* | RX_CFG1_INCLUDE_FCS */
/* | RX_CFG1_INCLUDE_PHY_HDR */
);
adev->rx_config_2 = (u16) (0
| RX_CFG2_RCV_ASSOC_REQ
| RX_CFG2_RCV_AUTH_FRAMES
| RX_CFG2_RCV_BEACON_FRAMES
| RX_CFG2_RCV_CONTENTION_FREE
| RX_CFG2_RCV_CTRL_FRAMES
| RX_CFG2_RCV_DATA_FRAMES
| RX_CFG2_RCV_BROKEN_FRAMES
| RX_CFG2_RCV_MGMT_FRAMES
| RX_CFG2_RCV_PROBE_REQ
| RX_CFG2_RCV_PROBE_RESP
| RX_CFG2_RCV_ACK_FRAMES
| RX_CFG2_RCV_OTHER
);
break;
default:
adev->rx_config_1 = (u16) (0
/* | RX_CFG1_INCLUDE_RXBUF_HDR */
/* | RX_CFG1_FILTER_SSID */
/* | RX_CFG1_FILTER_BCAST */
/* | RX_CFG1_RCV_MC_ADDR1 */
/* | RX_CFG1_RCV_MC_ADDR0 */
/* | RX_CFG1_FILTER_ALL_MULTI */
/* | RX_CFG1_FILTER_BSSID */
| RX_CFG1_FILTER_MAC
/* | RX_CFG1_RCV_PROMISCUOUS */
/* | RX_CFG1_INCLUDE_FCS */
/* | RX_CFG1_INCLUDE_PHY_HDR */
);
adev->rx_config_2 = (u16) (0
| RX_CFG2_RCV_ASSOC_REQ
| RX_CFG2_RCV_AUTH_FRAMES
| RX_CFG2_RCV_BEACON_FRAMES
| RX_CFG2_RCV_CONTENTION_FREE
| RX_CFG2_RCV_CTRL_FRAMES
| RX_CFG2_RCV_DATA_FRAMES
/*| RX_CFG2_RCV_BROKEN_FRAMES */
| RX_CFG2_RCV_MGMT_FRAMES
| RX_CFG2_RCV_PROBE_REQ
| RX_CFG2_RCV_PROBE_RESP
/*| RX_CFG2_RCV_ACK_FRAMES */
| RX_CFG2_RCV_OTHER
);
break;
}
adev->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR;
if ((adev->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR)
|| (adev->firmware_numver >= 0x02000000))
adev->phy_header_len = IS_ACX111(adev) ? 8 : 4;
else
adev->phy_header_len = 0;
log(L_INIT, "setting RXconfig to %04X:%04X\n",
adev->rx_config_1, adev->rx_config_2);
cfg.rx_cfg1 = cpu_to_le16(adev->rx_config_1);
cfg.rx_cfg2 = cpu_to_le16(adev->rx_config_2);
acx_s_configure(adev, &cfg, ACX1xx_IE_RXCONFIG);
}
/***********************************************************************
** acx_s_set_defaults
*/
void
acx_s_set_defaults(acx_device_t *adev)
{
unsigned long flags;
FN_ENTER;
/* do it before getting settings, prevent bogus channel 0 warning */
adev->channel = 1;
/* query some settings from the card.
* NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial
* query is REQUIRED, otherwise the card won't work correctly! */
adev->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN;
/* Only ACX100 supports ED and CCA */
if (IS_ACX100(adev))
adev->get_mask |= GETSET_CCA|GETSET_ED_THRESH;
acx_s_update_card_settings(adev);
acx_lock(adev, flags);
/* set our global interrupt mask */
if (IS_PCI(adev))
acxpci_set_interrupt_mask(adev);
adev->led_power = 1; /* LED is active on startup */
adev->brange_max_quality = 60; /* LED blink max quality is 60 */
adev->brange_time_last_state_change = jiffies;
/* copy the MAC address we just got from the card
* into our MAC address used during current 802.11 session */
MAC_COPY(adev->dev_addr, adev->ndev->dev_addr);
MAC_BCAST(adev->ap);
adev->essid_len =
snprintf(adev->essid, sizeof(adev->essid), "STA%02X%02X%02X",
adev->dev_addr[3], adev->dev_addr[4], adev->dev_addr[5]);
adev->essid_active = 1;
/* we have a nick field to waste, so why not abuse it
* to announce the driver version? ;-) */
strncpy(adev->nick, "acx " ACX_RELEASE, IW_ESSID_MAX_SIZE);
if (IS_PCI(adev)) { /* FIXME: this should be made to apply to USB, too! */
/* first regulatory domain entry in EEPROM == default reg. domain */
adev->reg_dom_id = adev->cfgopt_domains.list[0];
}
/* 0xffff would be better, but then we won't get a "scan complete"
* interrupt, so our current infrastructure will fail: */
adev->scan_count = 1;
adev->scan_mode = ACX_SCAN_OPT_ACTIVE;
adev->scan_duration = 100;
adev->scan_probe_delay = 200;
/* reported to break scanning: adev->scan_probe_delay = adev->cfgopt_probe_delay; */
adev->scan_rate = ACX_SCAN_RATE_1;
adev->mode = ACX_MODE_2_STA;
adev->ieee->sec.auth_mode = WLAN_AUTH_OPEN;
adev->listen_interval = 100;
adev->beacon_interval = DEFAULT_BEACON_INTERVAL;
adev->dtim_interval = DEFAULT_DTIM_INTERVAL;
adev->msdu_lifetime = DEFAULT_MSDU_LIFETIME;
adev->rts_threshold = DEFAULT_RTS_THRESHOLD;
adev->frag_threshold = 2346;
/* use standard default values for retry limits */
adev->short_retry = 7; /* max. retries for (short) non-RTS packets */
adev->long_retry = 4; /* max. retries for long (RTS) packets */
adev->preamble_mode = 2; /* auto */
adev->fallback_threshold = 3;
adev->stepup_threshold = 10;
adev->rate_bcast = RATE111_1;
adev->rate_bcast100 = RATE100_1;
adev->rate_basic = RATE111_1 | RATE111_2;
adev->rate_auto = 1;
if (IS_ACX111(adev)) {
adev->rate_oper = RATE111_ALL;
} else {
adev->rate_oper = RATE111_ACX100_COMPAT;
}
/* Supported Rates element - the rates here are given in units of
* 500 kbit/s, plus 0x80 added. See 802.11-1999.pdf item 7.3.2.2 */
acx_l_update_ratevector(adev);
/* set some more defaults */
if (IS_ACX111(adev)) {
/* 30mW (15dBm) is default, at least in my acx111 card: */
adev->tx_level_dbm = 15;
} else {
/* don't use max. level, since it might be dangerous
* (e.g. WRT54G people experience
* excessive Tx power damage!) */
adev->tx_level_dbm = 18;
}
/* adev->tx_level_auto = 1; */
if (IS_ACX111(adev)) {
/* start with sensitivity level 1 out of 3: */
adev->sensitivity = 1;
}
/* #define ENABLE_POWER_SAVE */
#ifdef ENABLE_POWER_SAVE
adev->ps_wakeup_cfg = PS_CFG_ENABLE | PS_CFG_WAKEUP_ALL_BEAC;
adev->ps_listen_interval = 1;
adev->ps_options = PS_OPT_ENA_ENHANCED_PS | PS_OPT_TX_PSPOLL | PS_OPT_STILL_RCV_BCASTS;
adev->ps_hangover_period = 30;
adev->ps_enhanced_transition_time = 0;
#else
adev->ps_wakeup_cfg = 0;
adev->ps_listen_interval = 0;
adev->ps_options = 0;
adev->ps_hangover_period = 0;
adev->ps_enhanced_transition_time = 0;
#endif
/* These settings will be set in fw on ifup */
adev->set_mask = 0
| GETSET_RETRY
| SET_MSDU_LIFETIME
/* configure card to do rate fallback when in auto rate mode */
| SET_RATE_FALLBACK
| SET_RXCONFIG
| GETSET_TXPOWER
/* better re-init the antenna value we got above */
| GETSET_ANTENNA
#if POWER_SAVE_80211
| GETSET_POWER_80211
#endif
;
acx_unlock(adev, flags);
acx_lock_unhold(); /* hold time 844814 CPU ticks @2GHz */
acx_s_initialize_rx_config(adev);
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_s_set_defaults);
/***********************************************************************
** FIXME: this should be solved in a general way for all radio types
** by decoding the radio firmware module,
** since it probably has some standard structure describing how to
** set the power level of the radio module which it controls.
** Or maybe not, since the radio module probably has a function interface
** instead which then manages Tx level programming :-\
*/
static int
acx111_s_set_tx_level(acx_device_t *adev, u8 level_dbm)
{
struct acx111_ie_tx_level tx_level;
/* my acx111 card has two power levels in its configoptions (== EEPROM):
* 1 (30mW) [15dBm]
* 2 (10mW) [10dBm]
* For now, just assume all other acx111 cards have the same.
* FIXME: Ideally we would query it here, but we first need a
* standard way to query individual configoptions easily.
* Well, now we have proper cfgopt txpower variables, but this still
* hasn't been done yet, since it also requires dBm <-> mW conversion here... */
if (level_dbm <= 12) {
tx_level.level = 2; /* 10 dBm */
adev->tx_level_dbm = 10;
} else {
tx_level.level = 1; /* 15 dBm */
adev->tx_level_dbm = 15;
}
if (level_dbm != adev->tx_level_dbm)
log(L_INIT, "acx111 firmware has specific "
"power levels only: adjusted %d dBm to %d dBm!\n",
level_dbm, adev->tx_level_dbm);
return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL);
}
static int
acx_s_set_tx_level(acx_device_t *adev, u8 level_dbm)
{
if (IS_ACX111(adev)) {
return acx111_s_set_tx_level(adev, level_dbm);
}
if (IS_PCI(adev)) {
return acx100pci_s_set_tx_level(adev, level_dbm);
}
return OK;
}
/***********************************************************************
*/
//SM: have no dev specific code, should be moved to sm layer
/* Filter out unrelated packets, call ieee80211_rx[_mgt] */
static int
_TODO_ieee80211_rx_any(struct ieee80211_device *ieee,
struct sk_buff *skb, struct ieee80211_rx_stats *stats)
{
struct ieee80211_hdr_4addr *hdr;
int is_packet_for_us;
u16 fc;
if (ieee->iw_mode == IW_MODE_MONITOR)
return ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL;
hdr = (struct ieee80211_hdr_4addr *)skb->data;
fc = le16_to_cpu(hdr->frame_ctl);
if ((fc & IEEE80211_FCTL_VERS) != 0)
return -EINVAL;
switch (fc & IEEE80211_FCTL_FTYPE) {
case IEEE80211_FTYPE_MGMT:
ieee80211_rx_mgt(ieee, hdr, stats);
return 0;
case IEEE80211_FTYPE_DATA:
break;
case IEEE80211_FTYPE_CTL:
return 0;
default:
return -EINVAL;
}
is_packet_for_us = 0;
switch (ieee->iw_mode) {
case IW_MODE_ADHOC:
/* our BSS and not from/to DS */
if (memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) == 0)
if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) {
/* promisc: get all */
if (ieee->dev->flags & IFF_PROMISC)
is_packet_for_us = 1;
/* to us */
else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
is_packet_for_us = 1;
/* mcast */
else if (is_multicast_ether_addr(hdr->addr1))
is_packet_for_us = 1;
}
break;
case IW_MODE_INFRA:
/* our BSS (== from our AP) and from DS */
if (memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) == 0)
if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) {
/* promisc: get all */
if (ieee->dev->flags & IFF_PROMISC)
is_packet_for_us = 1;
/* to us */
else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
is_packet_for_us = 1;
/* mcast */
else if (is_multicast_ether_addr(hdr->addr1)) {
/* not our own packet bcasted from AP */
if (memcmp(hdr->addr3, ieee->dev->dev_addr, ETH_ALEN))
is_packet_for_us = 1;
}
}
break;
default:
/* ? */
break;
}
if (is_packet_for_us)
return (ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL);
return 0;
}
//SM
void
acx_l_softmac_process_rxbuf(acx_device_t *adev, rxbuffer_t *rxbuf)
{
struct ieee80211_rx_stats stats;
struct ieee80211_hdr_3addr *hdr;
struct sk_buff *skb;
int skb_len;
u16 fc;
FN_ENTER;
skb_len = RXBUF_BYTES_RCVD(adev, rxbuf);
hdr = acx_get_wlan_hdr(adev, rxbuf);
fc = le16_to_cpu(hdr->frame_ctl);
if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON)
|| (acx_debug & L_XFER_BEACON)
) {
log(L_XFER|L_DATA, "rx: %s "
"time:%u len:%u signal:%u SNR:%u macstat:%02X "
"phystat:%02X phyrate:%u status:%u\n",
acx_get_packet_type_string(fc),
le32_to_cpu(rxbuf->time),
skb_len,
acx_signal_to_winlevel(rxbuf->phy_level),
acx_signal_to_winlevel(rxbuf->phy_snr),
rxbuf->mac_status,
rxbuf->phy_stat_baseband,
rxbuf->phy_plcp_signal,
adev->status);
}
if (unlikely(acx_debug & L_DATA)) {
printk("rx: 802.11 buf[%u]: ", skb_len);
acx_dump_bytes(hdr, skb_len);
}
/* FIXME: should check for Rx errors (rxbuf->mac_status?
* discard broken packets - but NOT for monitor!)
* and update Rx packet statistics here */
/* we are in big luck: the acx100 doesn't modify any of the fields */
/* in the 802.11 frame. just pass this packet into the PF_PACKET */
/* subsystem. yeah. */
/* sanity check */
if (unlikely(skb_len > WLAN_A4FR_MAXLEN_WEP)) {
printk("%s: oversized frame of %d bytes dropped\n",
adev->ndev->name, skb_len);
goto end;
}
/* allocate skb */
skb = dev_alloc_skb(skb_len);
if (unlikely(!skb)) {
printk("%s: no memory for skb (%u bytes)\n",
adev->ndev->name, skb_len);
goto end;
}
skb_put(skb, skb_len);
memcpy(skb->data, hdr, skb_len);
memset(&stats, 0, sizeof(stats));
stats.mac_time = le16_to_cpu(rxbuf->time);
//stats.rssi =
stats.signal = rxbuf->phy_snr;
stats.noise = rxbuf->phy_level;
stats.rate = rxbuf->phy_plcp_signal / 5;
stats.received_channel = adev->channel;
//stats.control =
stats.mask = 0
| IEEE80211_STATMASK_SIGNAL
| IEEE80211_STATMASK_NOISE
| IEEE80211_STATMASK_RATE
//| IEEE80211_STATMASK_RSSI
;
stats.freq = IEEE80211_24GHZ_BAND;
stats.len = skb->len;
_TODO_ieee80211_rx_any(adev->ieee, skb, &stats);
end:
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_l_softmac_process_rxbuf);
/***********************************************************************
** acx_l_handle_txrate_auto
**
** Theory of operation:
** client->rate_cap is a bitmask of rates client is capable of.
** client->rate_cfg is a bitmask of allowed (configured) rates.
** It is set as a result of iwconfig rate N [auto]
** or iwpriv set_rates "N,N,N N,N,N" commands.
** It can be fixed (e.g. 0x0080 == 18Mbit only),
** auto (0x00ff == 18Mbit or any lower value),
** and code handles any bitmask (0x1081 == try 54Mbit,18Mbit,1Mbit _only_).
**
** client->rate_cur is a value for rate111 field in tx descriptor.
** It is always set to txrate_cfg sans zero or more most significant
** bits. This routine handles selection of new rate_cur value depending on
** outcome of last tx event.
**
** client->rate_100 is a precalculated rate value for acx100
** (we can do without it, but will need to calculate it on each tx).
**
** You cannot configure mixed usage of 5.5 and/or 11Mbit rate
** with PBCC and CCK modulation. Either both at CCK or both at PBCC.
** In theory you can implement it, but so far it is considered not worth doing.
**
** 22Mbit, of course, is PBCC always. */
/* maps acx100 tx descr rate field to acx111 one */
static u16
rate100to111(u8 r)
{
switch (r) {
case RATE100_1: return RATE111_1;
case RATE100_2: return RATE111_2;
case RATE100_5:
case (RATE100_5 | RATE100_PBCC511): return RATE111_5;
case RATE100_11:
case (RATE100_11 | RATE100_PBCC511): return RATE111_11;
case RATE100_22: return RATE111_22;
default:
printk("acx: unexpected acx100 txrate: %u! "
"Please report\n", r);
return RATE111_1;
}
}
void
acx_l_handle_txrate_auto(acx_device_t *adev, struct client *txc,
u16 cur, u8 rate100, u16 rate111,
u8 error, int pkts_to_ignore)
{
u16 sent_rate;
int slower_rate_was_used;
/* vda: hmm. current code will do this:
** 1. send packets at 11 Mbit, stepup++
** 2. will try to send at 22Mbit. hardware will see no ACK,
** retries at 11Mbit, success. code notes that used rate
** is lower. stepup = 0, fallback++
** 3. repeat step 2 fallback_count times. Fall back to
** 11Mbit. go to step 1.
** If stepup_count is large (say, 16) and fallback_count
** is small (3), this wouldn't be too bad wrt throughput */
if (unlikely(!cur)) {
printk("acx: BUG! ratemask is empty\n");
return; /* or else we may lock up the box */
}
/* do some preparations, i.e. calculate the one rate that was
* used to send this packet */
if (IS_ACX111(adev)) {
sent_rate = 1 << highest_bit(rate111 & RATE111_ALL);
} else {
sent_rate = rate100to111(rate100);
}
/* sent_rate has only one bit set now, corresponding to tx rate
* which was used by hardware to tx this particular packet */
/* now do the actual auto rate management */
log(L_XFER, "tx: %sclient=%p/"MACSTR" used=%04X cur=%04X cfg=%04X "
"__=%u/%u ^^=%u/%u\n",
(txc->ignore_count > 0) ? "[IGN] " : "",
txc, MAC(txc->address), sent_rate, cur, txc->rate_cfg,
txc->fallback_count, adev->fallback_threshold,
txc->stepup_count, adev->stepup_threshold
);
/* we need to ignore old packets already in the tx queue since
* they use older rate bytes configured before our last rate change,
* otherwise our mechanism will get confused by interpreting old data.
* Do it after logging above */
if (txc->ignore_count) {
txc->ignore_count--;
return;
}
/* true only if the only nonzero bit in sent_rate is
** less significant than highest nonzero bit in cur */
slower_rate_was_used = ( cur > ((sent_rate<<1)-1) );
if (slower_rate_was_used || error) {
txc->stepup_count = 0;
if (++txc->fallback_count <= adev->fallback_threshold)
return;
txc->fallback_count = 0;
/* clear highest 1 bit in cur */
sent_rate = RATE111_54;
while (!(cur & sent_rate)) sent_rate >>= 1;
CLEAR_BIT(cur, sent_rate);
if (!cur) /* we can't disable all rates! */
cur = sent_rate;
log(L_XFER, "tx: falling back to ratemask %04X\n", cur);
} else { /* there was neither lower rate nor error */
txc->fallback_count = 0;
if (++txc->stepup_count <= adev->stepup_threshold)
return;
txc->stepup_count = 0;
/* Sanitize. Sort of not needed, but I dont trust hw that much...
** what if it can report bogus tx rates sometimes? */
while (!(cur & sent_rate)) sent_rate >>= 1;
/* try to find a higher sent_rate that isn't yet in our
* current set, but is an allowed cfg */
while (1) {
sent_rate <<= 1;
if (sent_rate > txc->rate_cfg)
/* no higher rates allowed by config */
return;
if (!(cur & sent_rate) && (txc->rate_cfg & sent_rate))
/* found */
break;
/* not found, try higher one */
}
SET_BIT(cur, sent_rate);
log(L_XFER, "tx: stepping up to ratemask %04X\n", cur);
}
txc->rate_cur = cur;
txc->ignore_count = pkts_to_ignore;
/* calculate acx100 style rate byte if needed */
if (IS_ACX100(adev)) {
txc->rate_100 = acx_bitpos2rate100[highest_bit(cur)];
}
}
EXPORT_SYMBOL_GPL(acx_l_handle_txrate_auto);
//SM
int
acx_i_ieee80211_start_xmit(struct ieee80211_txb *txb,
struct net_device *ndev,
int pri)
{
acx_device_t *adev = ndev2adev(ndev);
int i;
int rc = -ENODEV;
unsigned long flags;
if (unlikely(!txb)) { /* indicate success */
rc = OK;
goto end_no_unlock;
}
if (unlikely(!adev)) {
goto end_no_unlock;
}
acx_lock(adev, flags);
for (i = 0; i < txb->nr_frags; i++) {
tx_t *tx;
void *txbuf;
struct sk_buff *skb = txb->fragments[i];
tx = acx_l_alloc_tx(adev);
if (unlikely(!tx)) {
printk_ratelimited("%s: start_xmit: txdesc ring is full, "
"dropping tx\n", ndev->name);
rc = -ENOMEM;
goto end;
}
txbuf = acx_l_get_txbuf(adev, tx);
if (unlikely(!txbuf)) {
/* Card was removed */
rc = -ENOMEM;
acx_l_dealloc_tx(adev, tx);
goto end;
}
memcpy(txbuf, skb->data, skb->len);
acx_l_tx_data(adev, tx, skb->len);
//ndev->trans_start = jiffies;
//adev->stats.tx_packets++;
//adev->stats.tx_bytes += skb->len;
}
rc = 0;
end:
acx_unlock(adev, flags);
end_no_unlock:
if (!rc && txb)
ieee80211_txb_free(txb);
FN_EXIT1(rc);
return rc;
}
EXPORT_SYMBOL_GPL(acx_i_ieee80211_start_xmit);
/***********************************************************************
** acx_l_update_ratevector
**
** Updates adev->rate_supported[_len] according to rate_{basic,oper}
*/
const u8
acx_bitpos2ratebyte[] = {
DOT11RATEBYTE_1,
DOT11RATEBYTE_2,
DOT11RATEBYTE_5_5,
DOT11RATEBYTE_6_G,
DOT11RATEBYTE_9_G,
DOT11RATEBYTE_11,
DOT11RATEBYTE_12_G,
DOT11RATEBYTE_18_G,
DOT11RATEBYTE_22,
DOT11RATEBYTE_24_G,
DOT11RATEBYTE_36_G,
DOT11RATEBYTE_48_G,
DOT11RATEBYTE_54_G,
};
void
acx_l_update_ratevector(acx_device_t *adev)
{
u16 bcfg = adev->rate_basic;
u16 ocfg = adev->rate_oper;
u8 *supp = adev->rate_supported;
const u8 *dot11 = acx_bitpos2ratebyte;
FN_ENTER;
while (ocfg) {
if (ocfg & 1) {
*supp = *dot11;
if (bcfg & 1) {
*supp |= 0x80;
}
supp++;
}
dot11++;
ocfg >>= 1;
bcfg >>= 1;
}
adev->rate_supported_len = supp - adev->rate_supported;
if (acx_debug & L_ASSOC) {
printk("new ratevector: ");
acx_dump_bytes(adev->rate_supported, adev->rate_supported_len);
}
FN_EXIT0;
}
/***********************************************************************
** acx_l_sta_list_init
*/
static void
acx_l_sta_list_init(acx_device_t *adev)
{
FN_ENTER;
memset(adev->sta_hash_tab, 0, sizeof(adev->sta_hash_tab));
memset(adev->sta_list, 0, sizeof(adev->sta_list));
FN_EXIT0;
}
/***********************************************************************
** acx_set_status
**
** This function is called in many atomic regions, must not sleep
**
** This function does not need locking UNLESS you call it
** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can
** wake queue. This can race with stop_queue elsewhere.
** See acx_stop_queue comment. */
void
acx_set_status(acx_device_t *adev, u16 new_status)
{
#define QUEUE_OPEN_AFTER_ASSOC 1 /* this really seems to be needed now */
u16 old_status = adev->status;
FN_ENTER;
log(L_ASSOC, "%s(%d):%s\n",
__func__, new_status, acx_get_status_name(new_status));
/* wireless_send_event never sleeps */
if (ACX_STATUS_4_ASSOCIATED == new_status) {
union iwreq_data wrqu;
wrqu.data.length = 0;
wrqu.data.flags = 0;
wireless_send_event(adev->ndev, SIOCGIWSCAN, &wrqu, NULL);
wrqu.data.length = 0;
wrqu.data.flags = 0;
MAC_COPY(wrqu.ap_addr.sa_data, adev->bssid);
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(adev->ndev, SIOCGIWAP, &wrqu, NULL);
} else {
union iwreq_data wrqu;
/* send event with empty BSSID to indicate we're not associated */
MAC_ZERO(wrqu.ap_addr.sa_data);
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(adev->ndev, SIOCGIWAP, &wrqu, NULL);
}
adev->status = new_status;
switch (new_status) {
case ACX_STATUS_1_SCANNING:
adev->scan_retries = 0;
/* 1.0 s initial scan time */
acx_set_timer(adev, 1000000);
break;
case ACX_STATUS_2_WAIT_AUTH:
case ACX_STATUS_3_AUTHENTICATED:
adev->auth_or_assoc_retries = 0;
acx_set_timer(adev, 1500000); /* 1.5 s */
break;
}
#if QUEUE_OPEN_AFTER_ASSOC
if (new_status == ACX_STATUS_4_ASSOCIATED) {
if (old_status < ACX_STATUS_4_ASSOCIATED) {
/* ah, we're newly associated now,
* so let's indicate carrier */
acx_carrier_on(adev->ndev, "after association");
acx_wake_queue(adev->ndev, "after association");
}
} else {
/* not associated any more, so let's kill carrier */
if (old_status >= ACX_STATUS_4_ASSOCIATED) {
acx_carrier_off(adev->ndev, "after losing association");
acx_stop_queue(adev->ndev, "after losing association");
}
}
#endif
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_set_status);
/***********************************************************************
** acx_i_timer
**
** Fires up periodically. Used to kick scan/auth/assoc if something goes wrong
*/
void
acx_i_timer(unsigned long address)
{
unsigned long flags;
acx_device_t *adev = (acx_device_t*)address;
FN_ENTER;
acx_lock(adev, flags);
log(L_DEBUG|L_ASSOC, "%s: adev->status=%d (%s)\n",
__func__, adev->status, acx_get_status_name(adev->status));
switch (adev->status) {
case ACX_STATUS_1_SCANNING:
/* was set to 0 by set_status() */
if (++adev->scan_retries < 7) {
acx_set_timer(adev, 1000000);
/* used to interrogate for scan status.
** We rely on SCAN_COMPLETE IRQ instead */
log(L_ASSOC, "continuing scan (%d sec)\n",
adev->scan_retries);
} else {
log(L_ASSOC, "stopping scan\n");
/* send stop_scan cmd when we leave the interrupt context,
* and make a decision what to do next (COMPLETE_SCAN) */
acx_schedule_task(adev,
ACX_AFTER_IRQ_CMD_STOP_SCAN + ACX_AFTER_IRQ_COMPLETE_SCAN);
}
break;
case ACX_STATUS_2_WAIT_AUTH:
/* was set to 0 by set_status() */
if (++adev->auth_or_assoc_retries < 10) {
log(L_ASSOC, "resend authen1 request (attempt %d)\n",
adev->auth_or_assoc_retries + 1);
//sm? acx_l_transmit_authen1(adev);
} else {
/* time exceeded: fall back to scanning mode */
log(L_ASSOC,
"authen1 request reply timeout, giving up\n");
/* we are a STA, need to find AP anyhow */
acx_set_status(adev, ACX_STATUS_1_SCANNING);
acx_schedule_task(adev, ACX_AFTER_IRQ_RESTART_SCAN);
}
/* used to be 1500000, but some other driver uses 2.5s */
acx_set_timer(adev, 2500000);
break;
case ACX_STATUS_3_AUTHENTICATED:
/* was set to 0 by set_status() */
if (++adev->auth_or_assoc_retries < 10) {
log(L_ASSOC, "resend assoc request (attempt %d)\n",
adev->auth_or_assoc_retries + 1);
//sm? acx_l_transmit_assoc_req(adev);
} else {
/* time exceeded: give up */
log(L_ASSOC,
"association request reply timeout, giving up\n");
/* we are a STA, need to find AP anyhow */
acx_set_status(adev, ACX_STATUS_1_SCANNING);
acx_schedule_task(adev, ACX_AFTER_IRQ_RESTART_SCAN);
}
acx_set_timer(adev, 2500000); /* see above */
break;
case ACX_STATUS_4_ASSOCIATED:
default:
break;
}
acx_unlock(adev, flags);
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_i_timer);
/***********************************************************************
** acx_set_timer
**
** Sets the 802.11 state management timer's timeout.
*/
void
acx_set_timer(acx_device_t *adev, int timeout_us)
{
FN_ENTER;
log(L_DEBUG|L_IRQ, "%s(%u ms)\n", __func__, timeout_us/1000);
if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) {
printk("attempt to set the timer "
"when the card interface is not up!\n");
goto end;
}
/* first check if the timer was already initialized, THEN modify it */
if (adev->mgmt_timer.function) {
mod_timer(&adev->mgmt_timer,
jiffies + (timeout_us * HZ / 1000000));
}
end:
FN_EXIT0;
}
/***********************************************************************
** acx_s_complete_scan
**
** Called either from after_interrupt_task() if:
** 1) there was Scan_Complete IRQ, or
** 2) scanning expired in timer()
** We need to decide which ESS or IBSS to join.
** Iterates thru adev->sta_list:
** if adev->ap is not bcast, will join only specified
** ESS or IBSS with this bssid
** checks peers' caps for ESS/IBSS bit
** checks peers' SSID, allows exact match or hidden SSID
** If station to join is chosen:
** points adev->ap_client to the chosen struct client
** sets adev->essid_for_assoc for future assoc attempt
** Auth/assoc is not yet performed
** Returns OK if there is no need to restart scan
*/
int
acx_s_complete_scan(acx_device_t *