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 *adev)
{
struct client *bss;
unsigned long flags;
u16 needed_cap;
int i;
int idx_found = -1;
int result = OK;
FN_ENTER;
switch (adev->mode) {
case ACX_MODE_0_ADHOC:
needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */
break;
case ACX_MODE_2_STA:
needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */
break;
default:
printk("acx: driver bug: mode=%d in complete_scan()\n", adev->mode);
dump_stack();
goto end;
}
acx_lock(adev, flags);
/* TODO: sta_iterator hiding implementation would be nice here... */
for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
bss = &adev->sta_list[i];
if (!bss->used) continue;
log(L_ASSOC, "scan table: SSID='%s' CH=%d SIR=%d SNR=%d\n",
bss->essid, bss->channel, bss->sir, bss->snr);
if (!mac_is_bcast(adev->ap))
if (!mac_is_equal(bss->bssid, adev->ap))
continue; /* keep looking */
/* broken peer with no mode flags set? */
if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) {
printk("%s: strange peer "MACSTR" found with "
"neither ESS (AP) nor IBSS (Ad-Hoc) "
"capability - skipped\n",
adev->ndev->name, MAC(bss->address));
continue;
}
log(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n",
bss->cap_info, needed_cap);
/* does peer station support what we need? */
if ((bss->cap_info & needed_cap) != needed_cap)
continue; /* keep looking */
/* strange peer with NO basic rates?! */
if (unlikely(!bss->rate_bas)) {
printk("%s: strange peer "MACSTR" with empty rate set "
"- skipped\n",
adev->ndev->name, MAC(bss->address));
continue;
}
/* do we support all basic rates of this peer? */
if ((bss->rate_bas & adev->rate_oper) != bss->rate_bas) {
/* we probably need to have all rates as operational rates,
even in case of an 11M-only configuration */
#ifdef THIS_IS_TROUBLESOME
printk("%s: peer "MACSTR": incompatible basic rates "
"(AP requests 0x%04X, we have 0x%04X) "
"- skipped\n",
adev->ndev->name, MAC(bss->address),
bss->rate_bas, adev->rate_oper);
continue;
#else
printk("%s: peer "MACSTR": incompatible basic rates "
"(AP requests 0x%04X, we have 0x%04X). "
"Considering anyway...\n",
adev->ndev->name, MAC(bss->address),
bss->rate_bas, adev->rate_oper);
#endif
}
if ( !(adev->reg_dom_chanmask & (1<<(bss->channel-1))) ) {
printk("%s: warning: peer "MACSTR" is on channel %d "
"outside of channel range of current "
"regulatory domain - couldn't join "
"even if other settings match. "
"You might want to adapt your config\n",
adev->ndev->name, MAC(bss->address),
bss->channel);
continue; /* keep looking */
}
if (!adev->essid_active || !strcmp(bss->essid, adev->essid)) {
log(L_ASSOC,
"found station with matching ESSID! ('%s' "
"station, '%s' config)\n",
bss->essid,
(adev->essid_active) ? adev->essid : "[any]");
/* TODO: continue looking for peer with better SNR */
bss->used = CLIENT_JOIN_CANDIDATE;
idx_found = i;
/* stop searching if this station is
* on the current channel, otherwise
* keep looking for an even better match */
if (bss->channel == adev->channel)
break;
} else
if (is_hidden_essid(bss->essid)) {
/* hmm, station with empty or single-space SSID:
* using hidden SSID broadcast?
*/
/* This behaviour is broken: which AP from zillion
** of APs with hidden SSID you'd try?
** We should use Probe requests to get Probe responses
** and check for real SSID (are those never hidden?) */
bss->used = CLIENT_JOIN_CANDIDATE;
if (idx_found == -1)
idx_found = i;
log(L_ASSOC, "found station with empty or "
"single-space (hidden) SSID, considering "
"for assoc attempt\n");
/* ...and keep looking for better matches */
} else {
log(L_ASSOC, "ESSID doesn't match! ('%s' "
"station, '%s' config)\n",
bss->essid,
(adev->essid_active) ? adev->essid : "[any]");
}
}
/* TODO: iterate thru join candidates instead */
/* TODO: rescan if not associated within some timeout */
if (idx_found != -1) {
char *essid_src;
size_t essid_len;
bss = &adev->sta_list[idx_found];
adev->ap_client = bss;
if (is_hidden_essid(bss->essid)) {
/* if the ESSID of the station we found is empty
* (no broadcast), then use user-configured ESSID
* instead */
essid_src = adev->essid;
essid_len = adev->essid_len;
} else {
essid_src = bss->essid;
essid_len = strlen(bss->essid);
}
acx_update_capabilities(adev);
memcpy(adev->essid_for_assoc, essid_src, essid_len);
adev->essid_for_assoc[essid_len] = '\0';
adev->channel = bss->channel;
MAC_COPY(adev->bssid, bss->bssid);
bss->rate_cfg = (bss->rate_cap & adev->rate_oper);
bss->rate_cur = 1 << lowest_bit(bss->rate_cfg);
bss->rate_100 = acx_rate111to100(bss->rate_cur);
acxlog_mac(L_ASSOC,
"matching station found: ", adev->bssid, ", joining\n");
/* TODO: do we need to switch to the peer's channel first? */
if (ACX_MODE_0_ADHOC == adev->mode) {
acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
} else {
//sm? acx_l_transmit_authen1(adev);
acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH);
}
} else { /* idx_found == -1 */
/* uh oh, no station found in range */
if (ACX_MODE_0_ADHOC == adev->mode) {
printk("%s: no matching station found in range, "
"generating our own IBSS instead\n",
adev->ndev->name);
/* we do it the HostAP way: */
MAC_COPY(adev->bssid, adev->dev_addr);
adev->bssid[0] |= 0x02; /* 'local assigned addr' bit */
/* add IBSS bit to our caps... */
acx_update_capabilities(adev);
acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
/* In order to cmd_join be called below */
idx_found = 0;
} else {
/* we shall scan again, AP can be
** just temporarily powered off */
log(L_ASSOC,
"no matching station found in range yet\n");
acx_set_status(adev, ACX_STATUS_1_SCANNING);
result = NOT_OK;
}
}
acx_unlock(adev, flags);
if (idx_found != -1) {
if (ACX_MODE_0_ADHOC == adev->mode) {
/* need to update channel in beacon template */
SET_BIT(adev->set_mask, SET_TEMPLATES);
if (ACX_STATE_IFACE_UP & adev->dev_state_mask)
acx_s_update_card_settings(adev);
}
/* Inform firmware on our decision to start or join BSS */
acx_s_cmd_join_bssid(adev, adev->bssid);
}
end:
FN_EXIT1(result);
return result;
}
/***********************************************************************
** acx_s_read_fw
**
** Loads a firmware image
**
** Returns:
** 0 unable to load file
** pointer to firmware success
*/
firmware_image_t*
acx_s_read_fw(struct device *dev, const char *file, u32 *size)
{
firmware_image_t *res;
const struct firmware *fw_entry;
res = NULL;
log(L_INIT, "requesting firmware image '%s'\n", file);
if (!request_firmware(&fw_entry, file, dev)) {
*size = 8;
if (fw_entry->size >= 8)
*size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4));
if (fw_entry->size != *size) {
printk("acx: firmware size does not match "
"firmware header: %d != %d, "
"aborting fw upload\n",
(int) fw_entry->size, (int) *size);
goto release_ret;
}
res = vmalloc(*size);
if (!res) {
printk("acx: no memory for firmware "
"(%u bytes)\n", *size);
goto release_ret;
}
memcpy(res, fw_entry->data, fw_entry->size);
release_ret:
release_firmware(fw_entry);
return res;
}
printk("acx: firmware image '%s' was not provided. "
"Check your hotplug scripts\n", file);
/* checksum will be verified in write_fw, so don't bother here */
return res;
}
EXPORT_SYMBOL_GPL(acx_s_read_fw);
/***********************************************************************
** acx_s_set_wepkey
*/
static void
acx100_s_set_wepkey(acx_device_t *adev)
{
ie_dot11WEPDefaultKey_t dk;
int i;
for (i = 0; i < WEP_KEYS; i++) {
if (adev->ieee->sec.key_sizes[i] != 0) {
log(L_INIT, "setting WEP key: %d with "
"total size: %d\n", i, (int) adev->ieee->sec.key_sizes[i]);
dk.action = 1;
dk.keySize = adev->ieee->sec.key_sizes[i];
dk.defaultKeyNum = i;
memcpy(dk.key, adev->ieee->sec.keys[i], dk.keySize);
acx_s_configure(adev, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE);
}
}
}
static void
acx111_s_set_wepkey(acx_device_t *adev)
{
acx111WEPDefaultKey_t dk;
int i;
for (i = 0; i < WEP_KEYS; i++) {
if (adev->ieee->sec.key_sizes[i] != 0) {
log(L_INIT, "setting WEP key: %d with "
"total size: %d\n", i, (int) adev->ieee->sec.key_sizes[i]);
memset(&dk, 0, sizeof(dk));
dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */
dk.keySize = adev->ieee->sec.key_sizes[i];
/* are these two lines necessary? */
dk.type = 0; /* default WEP key */
dk.index = 0; /* ignored when setting default key */
dk.defaultKeyNum = i;
memcpy(dk.key, adev->ieee->sec.keys[i], dk.keySize);
acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk));
}
}
}
static void
acx_s_set_wepkey(acx_device_t *adev)
{
if (IS_ACX111(adev))
acx111_s_set_wepkey(adev);
else
acx100_s_set_wepkey(adev);
}
/***********************************************************************
** acx100_s_init_wep
**
** FIXME: this should probably be moved into the new card settings
** management, but since we're also modifying the memory map layout here
** due to the WEP key space we want, we should take care...
*/
static int
acx100_s_init_wep(acx_device_t *adev)
{
acx100_ie_wep_options_t options;
ie_dot11WEPDefaultKeyID_t dk;
acx_ie_memmap_t pt;
int res = NOT_OK;
FN_ENTER;
if (OK != acx_s_interrogate(adev, &pt, ACX1xx_IE_MEMORY_MAP)) {
goto fail;
}
log(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd);
pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4);
pt.WEPCacheEnd = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4);
if (OK != acx_s_configure(adev, &pt, ACX1xx_IE_MEMORY_MAP)) {
goto fail;
}
/* let's choose maximum setting: 4 default keys, plus 10 other keys: */
options.NumKeys = cpu_to_le16(WEP_KEYS + 10);
options.WEPOption = 0x00;
log(L_ASSOC, "writing WEP options\n");
acx_s_configure(adev, &options, ACX100_IE_WEP_OPTIONS);
acx100_s_set_wepkey(adev);
if (adev->ieee->sec.key_sizes[adev->ieee->sec.active_key] != 0) {
log(L_ASSOC, "setting active default WEP key number: %d\n",
adev->ieee->sec.active_key);
dk.KeyID = adev->ieee->sec.active_key;
acx_s_configure(adev, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */
}
/* FIXME!!! wep_key_struct is filled nowhere! But adev
* is initialized to 0, and we don't REALLY need those keys either */
/* for (i = 0; i < 10; i++) {
if (adev->wep_key_struct[i].len != 0) {
MAC_COPY(wep_mgmt.MacAddr, adev->wep_key_struct[i].addr);
wep_mgmt.KeySize = cpu_to_le16(adev->wep_key_struct[i].len);
memcpy(&wep_mgmt.Key, adev->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize));
wep_mgmt.Action = cpu_to_le16(1);
log(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize));
if (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) {
adev->wep_key_struct[i].index = i;
}
}
}
*/
/* now retrieve the updated WEPCacheEnd pointer... */
if (OK != acx_s_interrogate(adev, &pt, ACX1xx_IE_MEMORY_MAP)) {
printk("%s: ACX1xx_IE_MEMORY_MAP read #2 FAILED\n",
adev->ndev->name);
goto fail;
}
/* ...and tell it to start allocating templates at that location */
/* (no endianness conversion needed) */
pt.PacketTemplateStart = pt.WEPCacheEnd;
if (OK != acx_s_configure(adev, &pt, ACX1xx_IE_MEMORY_MAP)) {
printk("%s: ACX1xx_IE_MEMORY_MAP write #2 FAILED\n",
adev->ndev->name);
goto fail;
}
res = OK;
fail:
FN_EXIT1(res);
return res;
}
static int
acx_s_init_max_template_generic(acx_device_t *adev, unsigned int len, unsigned int cmd)
{
int res;
union {
acx_template_nullframe_t null;
acx_template_beacon_t b;
acx_template_tim_t tim;
acx_template_probereq_t preq;
acx_template_proberesp_t presp;
} templ;
memset(&templ, 0, len);
templ.null.size = cpu_to_le16(len - 2);
res = acx_s_issue_cmd(adev, cmd, &templ, len);
return res;
}
static inline int
acx_s_init_max_null_data_template(acx_device_t *adev)
{
return acx_s_init_max_template_generic(
adev, sizeof(acx_template_nullframe_t), ACX1xx_CMD_CONFIG_NULL_DATA
);
}
static inline int
acx_s_init_max_beacon_template(acx_device_t *adev)
{
return acx_s_init_max_template_generic(
adev, sizeof(acx_template_beacon_t), ACX1xx_CMD_CONFIG_BEACON
);
}
static inline int
acx_s_init_max_tim_template(acx_device_t *adev)
{
return acx_s_init_max_template_generic(
adev, sizeof(acx_template_tim_t), ACX1xx_CMD_CONFIG_TIM
);
}
static inline int
acx_s_init_max_probe_response_template(acx_device_t *adev)
{
return acx_s_init_max_template_generic(
adev, sizeof(acx_template_proberesp_t), ACX1xx_CMD_CONFIG_PROBE_RESPONSE
);
}
static inline int
acx_s_init_max_probe_request_template(acx_device_t *adev)
{
return acx_s_init_max_template_generic(
adev, sizeof(acx_template_probereq_t), ACX1xx_CMD_CONFIG_PROBE_REQUEST
);
}
/***********************************************************************
** acx_s_set_tim_template
**
** FIXME: In full blown driver we will regularly update partial virtual bitmap
** by calling this function
** (it can be done by irq handler on each DTIM irq or by timer...)
[802.11 7.3.2.6] TIM information element:
- 1 EID
- 1 Length
1 1 DTIM Count
indicates how many beacons (including this) appear before next DTIM
(0=this one is a DTIM)
2 1 DTIM Period
number of beacons between successive DTIMs
(0=reserved, 1=all TIMs are DTIMs, 2=every other, etc)
3 1 Bitmap Control
bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?)
set to 1 in TIM elements with a value of 0 in the DTIM Count field
when one or more broadcast or multicast frames are buffered at the AP.
bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE).
4 n Partial Virtual Bitmap
Visible part of traffic-indication bitmap.
Full bitmap consists of 2008 bits (251 octets) such that bit number N
(0<=N<=2007) in the bitmap corresponds to bit number (N mod 8)
in octet number N/8 where the low-order bit of each octet is bit0,
and the high order bit is bit7.
Each set bit in virtual bitmap corresponds to traffic buffered by AP
for a specific station (with corresponding AID?).
Partial Virtual Bitmap shows a part of bitmap which has non-zero.
Bitmap Offset is a number of skipped zero octets (see above).
'Missing' octets at the tail are also assumed to be zero.
Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55
This means that traffic-indication bitmap is:
00000000 00000000 01010101 01010101 01010101 00000000 00000000...
(is bit0 in the map is always 0 and real value is in Bitmap Control bit0?)
*/
static int
acx_s_set_tim_template(acx_device_t *adev)
{
/* For now, configure smallish test bitmap, all zero ("no pending data") */
enum { bitmap_size = 5 };
acx_template_tim_t t;
int result;
FN_ENTER;
memset(&t, 0, sizeof(t));
t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */
t.tim_eid = WLAN_EID_TIM;
t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */
result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t));
FN_EXIT1(result);
return result;
}
/***********************************************************************
** acx_fill_beacon_or_proberesp_template
**
** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!!
**
** NB: we use the fact that
** struct acx_template_proberesp and struct acx_template_beacon are the same
** (well, almost...)
**
** [802.11] Beacon's body consist of these IEs:
** 1 Timestamp
** 2 Beacon interval
** 3 Capability information
** 4 SSID
** 5 Supported rates (up to 8 rates)
** 6 FH Parameter Set (frequency-hopping PHYs only)
** 7 DS Parameter Set (direct sequence PHYs only)
** 8 CF Parameter Set (only if PCF is supported)
** 9 IBSS Parameter Set (ad-hoc only)
**
** Beacon only:
** 10 TIM (AP only) (see 802.11 7.3.2.6)
** 11 Country Information (802.11d)
** 12 FH Parameters (802.11d)
** 13 FH Pattern Table (802.11d)
** ... (?!! did not yet find relevant PDF file... --vda)
** 19 ERP Information (extended rate PHYs)
** 20 Extended Supported Rates (if more than 8 rates)
**
** Proberesp only:
** 10 Country information (802.11d)
** 11 FH Parameters (802.11d)
** 12 FH Pattern Table (802.11d)
** 13-n Requested information elements (802.11d)
** ????
** 18 ERP Information (extended rate PHYs)
** 19 Extended Supported Rates (if more than 8 rates)
*/
static int
acx_fill_beacon_or_proberesp_template(acx_device_t *adev,
struct acx_template_beacon *templ,
u16 fc /* in host order! */)
{
int len;
u8 *p;
FN_ENTER;
memset(templ, 0, sizeof(*templ));
MAC_BCAST(templ->da);
MAC_COPY(templ->sa, adev->dev_addr);
MAC_COPY(templ->bssid, adev->bssid);
templ->beacon_interval = cpu_to_le16(adev->beacon_interval);
acx_update_capabilities(adev);
templ->cap = cpu_to_le16(adev->capabilities);
p = templ->variable;
p = wlan_fill_ie_ssid(p, adev->essid_len, adev->essid);
p = wlan_fill_ie_rates(p, adev->rate_supported_len, adev->rate_supported);
p = wlan_fill_ie_ds_parms(p, adev->channel);
/* NB: should go AFTER tim, but acx seem to keep tim last always */
p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, adev->rate_supported);
switch (adev->mode) {
case ACX_MODE_0_ADHOC:
/* ATIM window */
p = wlan_fill_ie_ibss_parms(p, 0); break;
case ACX_MODE_3_AP:
/* TIM IE is set up as separate template */
break;
}
len = p - (u8*)templ;
templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc);
/* - 2: do not count 'u16 size' field */
templ->size = cpu_to_le16(len - 2);
FN_EXIT1(len);
return len;
}
#if POWER_SAVE_80211
/***********************************************************************
** acx_s_set_null_data_template
*/
static int
acx_s_set_null_data_template(acx_device_t *adev)
{
struct acx_template_nullframe b;
int result;
FN_ENTER;
/* memset(&b, 0, sizeof(b)); not needed, setting all members */
b.size = cpu_to_le16(sizeof(b) - 2);
b.hdr.fc = WF_FTYPE_MGMTi | WF_FSTYPE_NULLi;
b.hdr.dur = 0;
MAC_BCAST(b.hdr.a1);
MAC_COPY(b.hdr.a2, adev->dev_addr);
MAC_COPY(b.hdr.a3, adev->bssid);
b.hdr.seq = 0;
result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b));
FN_EXIT1(result);
return result;
}
#endif
/***********************************************************************
** acx_s_set_beacon_template
*/
static int
acx_s_set_beacon_template(acx_device_t *adev)
{
struct acx_template_beacon bcn;
int len, result;
FN_ENTER;
len = acx_fill_beacon_or_proberesp_template(adev, &bcn, WF_FSTYPE_BEACON);
result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_BEACON, &bcn, len);
FN_EXIT1(result);
return result;
}
/***********************************************************************
** acx_s_set_probe_response_template
*/
static int
acx_s_set_probe_response_template(acx_device_t *adev)
{
struct acx_template_proberesp pr;
int len, result;
FN_ENTER;
len = acx_fill_beacon_or_proberesp_template(adev, &pr, WF_FSTYPE_PROBERESP);
result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len);
FN_EXIT1(result);
return result;
}
/***********************************************************************
** acx_s_init_packet_templates()
**
** NOTE: order is very important here, to have a correct memory layout!
** init templates: max Probe Request (station mode), max NULL data,
** max Beacon, max TIM, max Probe Response.
*/
static int
acx_s_init_packet_templates(acx_device_t *adev)
{
acx_ie_memmap_t mm; /* ACX100 only */
int result = NOT_OK;
FN_ENTER;
log(L_DEBUG|L_INIT, "initializing max packet templates\n");
if (OK != acx_s_init_max_probe_request_template(adev))
goto failed;
if (OK != acx_s_init_max_null_data_template(adev))
goto failed;
if (OK != acx_s_init_max_beacon_template(adev))
goto failed;
if (OK != acx_s_init_max_tim_template(adev))
goto failed;
if (OK != acx_s_init_max_probe_response_template(adev))
goto failed;
if (IS_ACX111(adev)) {
/* ACX111 doesn't need the memory map magic below,
* and the other templates will be set later (acx_start) */
result = OK;
goto success;
}
/* ACX100 will have its TIM template set,
* and we also need to update the memory map */
if (OK != acx_s_set_tim_template(adev))
goto failed_acx100;
log(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm));
if (OK != acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP))
goto failed_acx100;
mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4);
if (OK != acx_s_configure(adev, &mm, ACX1xx_IE_MEMORY_MAP))
goto failed_acx100;
result = OK;
goto success;
failed_acx100:
log(L_DEBUG|L_INIT,
/* "cb=0x%X\n" */
"ACXMemoryMap:\n"
".CodeStart=0x%X\n"
".CodeEnd=0x%X\n"
".WEPCacheStart=0x%X\n"
".WEPCacheEnd=0x%X\n"
".PacketTemplateStart=0x%X\n"
".PacketTemplateEnd=0x%X\n",
/* len, */
le32_to_cpu(mm.CodeStart),
le32_to_cpu(mm.CodeEnd),
le32_to_cpu(mm.WEPCacheStart),
le32_to_cpu(mm.WEPCacheEnd),
le32_to_cpu(mm.PacketTemplateStart),
le32_to_cpu(mm.PacketTemplateEnd));
failed:
printk("%s: init_packet_templates() FAILED\n", adev->ndev->name);
success:
FN_EXIT1(result);
return result;
}
/***********************************************************************
*/
static int
acx_s_set_probe_request_template(acx_device_t *adev)
{
struct acx_template_probereq probereq;
char *p;
int res;
int frame_len;
FN_ENTER;
memset(&probereq, 0, sizeof(probereq));
probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi;
MAC_BCAST(probereq.da);
MAC_COPY(probereq.sa, adev->dev_addr);
MAC_BCAST(probereq.bssid);
p = probereq.variable;
p = wlan_fill_ie_ssid(p, adev->essid_len, adev->essid);
p = wlan_fill_ie_rates(p, adev->rate_supported_len, adev->rate_supported);
p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, adev->rate_supported);
frame_len = p - (char*)&probereq;
probereq.size = cpu_to_le16(frame_len - 2);
res = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len);
FN_EXIT0;
return res;
}
/***********************************************************************
** acx_s_init_mac
*/
int
acx_s_init_mac(acx_device_t *adev)
{
int result = NOT_OK;
FN_ENTER;
if (IS_ACX111(adev)) {
adev->ie_len = acx111_ie_len;
adev->ie_len_dot11 = acx111_ie_len_dot11;
} else {
adev->ie_len = acx100_ie_len;
adev->ie_len_dot11 = acx100_ie_len_dot11;
}
if (IS_PCI(adev)) {
adev->memblocksize = 256; /* 256 is default */
/* try to load radio for both ACX100 and ACX111, since both
* chips have at least some firmware versions making use of an
* external radio module */
acxpci_s_upload_radio(adev);
} else {
adev->memblocksize = 128;
}
if (IS_ACX111(adev)) {
/* for ACX111, the order is different from ACX100
1. init packet templates
2. create station context and create dma regions
3. init wep default keys
*/
if (OK != acx_s_init_packet_templates(adev))
goto fail;
if (OK != acx111_s_create_dma_regions(adev)) {
printk("%s: acx111_create_dma_regions FAILED\n",
adev->ndev->name);
goto fail;
}
} else {
if (OK != acx100_s_init_wep(adev))
goto fail;
if (OK != acx_s_init_packet_templates(adev))
goto fail;
if (OK != acx100_s_create_dma_regions(adev)) {
printk("%s: acx100_create_dma_regions FAILED\n",
adev->ndev->name);
goto fail;
}
}
MAC_COPY(adev->ndev->dev_addr, adev->dev_addr);
result = OK;
fail:
if (result)
printk("acx: init_mac() FAILED\n");
FN_EXIT1(result);
return result;
}
EXPORT_SYMBOL_GPL(acx_s_init_mac);
void
acx_s_set_sane_reg_domain(acx_device_t *adev, int do_set)
{
unsigned mask;
unsigned int i;
for (i = 0; i < sizeof(acx_reg_domain_ids); i++)
if (acx_reg_domain_ids[i] == adev->reg_dom_id)
break;
if (sizeof(acx_reg_domain_ids) == i) {
log(L_INIT, "Invalid or unsupported regulatory domain"
" 0x%02X specified, falling back to FCC (USA)!"
" Please report if this sounds fishy!\n",
adev->reg_dom_id);
i = 0;
adev->reg_dom_id = acx_reg_domain_ids[i];
/* since there was a mismatch, we need to force updating */
do_set = 1;
}
if (do_set) {
acx_ie_generic_t dom;
dom.m.bytes[0] = adev->reg_dom_id;
acx_s_configure(adev, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN);
}
adev->reg_dom_chanmask = reg_domain_channel_masks[i];
mask = (1 << (adev->channel - 1));
if (!(adev->reg_dom_chanmask & mask)) {
/* hmm, need to adjust our channel to reside within domain */
mask = 1;
for (i = 1; i <= 14; i++) {
if (adev->reg_dom_chanmask & mask) {
printk("%s: adjusting selected channel from %d "
"to %d due to new regulatory domain\n",
adev->ndev->name, adev->channel, i);
adev->channel = i;
break;
}
mask <<= 1;
}
}
}
#if POWER_SAVE_80211
static void
acx_s_update_80211_powersave_mode(acx_device_t *adev)
{
/* merge both structs in a union to be able to have common code */
union {
acx111_ie_powersave_t acx111;
acx100_ie_powersave_t acx100;
} pm;
/* change 802.11 power save mode settings */
log(L_INIT, "updating 802.11 power save mode settings: "
"wakeup_cfg 0x%02X, listen interval %u, "
"options 0x%02X, hangover period %u, "
"enhanced_ps_transition_time %u\n",
adev->ps_wakeup_cfg, adev->ps_listen_interval,
adev->ps_options, adev->ps_hangover_period,
adev->ps_enhanced_transition_time);
acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT);
log(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, "
"listen interval %u, options 0x%02X, "
"hangover period %u, "
"enhanced_ps_transition_time %u, beacon_rx_time %u\n",
pm.acx111.wakeup_cfg,
pm.acx111.listen_interval,
pm.acx111.options,
pm.acx111.hangover_period,
IS_ACX111(adev) ?
pm.acx111.enhanced_ps_transition_time
: pm.acx100.enhanced_ps_transition_time,
IS_ACX111(adev) ?
pm.acx111.beacon_rx_time
: (u32)-1
);
pm.acx111.wakeup_cfg = adev->ps_wakeup_cfg;
pm.acx111.listen_interval = adev->ps_listen_interval;
pm.acx111.options = adev->ps_options;
pm.acx111.hangover_period = adev->ps_hangover_period;
if (IS_ACX111(adev)) {
pm.acx111.beacon_rx_time = cpu_to_le32(adev->ps_beacon_rx_time);
pm.acx111.enhanced_ps_transition_time = cpu_to_le32(adev->ps_enhanced_transition_time);
} else {
pm.acx100.enhanced_ps_transition_time = cpu_to_le16(adev->ps_enhanced_transition_time);
}
acx_s_configure(adev, &pm, ACX1xx_IE_POWER_MGMT);
acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT);
log(L_INIT, "wakeup_cfg: 0x%02X\n", pm.acx111.wakeup_cfg);
acx_s_msleep(40);
acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT);
log(L_INIT, "wakeup_cfg: 0x%02X\n", pm.acx111.wakeup_cfg);
log(L_INIT, "power save mode change %s\n",
(pm.acx111.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful");
/* FIXME: maybe verify via PS_CFG_PENDING bit here
* that power save mode change was successful. */
/* FIXME: we shouldn't trigger a scan immediately after
* fiddling with power save mode (since the firmware is sending
* a NULL frame then). */
}
#endif
/***********************************************************************
** acx_s_update_card_settings
**
** Applies accumulated changes in various adev->xxxx members
** Called by ioctl commit handler, acx_start, acx_set_defaults,
** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG),
*/
static void
acx111_s_sens_radio_16_17(acx_device_t *adev)
{
u32 feature1, feature2;
if ((adev->sensitivity < 1) || (adev->sensitivity > 3)) {
printk("%s: invalid sensitivity setting (1..3), "
"setting to 1\n", adev->ndev->name);
adev->sensitivity = 1;
}
acx111_s_get_feature_config(adev, &feature1, &feature2);
CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX);
if (adev->sensitivity > 1)
SET_BIT(feature1, FEATURE1_LOW_RX);
if (adev->sensitivity > 2)
SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX);
acx111_s_feature_set(adev, feature1, feature2);
}
void
acx_s_update_card_settings(acx_device_t *adev)
{
unsigned long flags;
unsigned int start_scan = 0;
int i;
FN_ENTER;
log(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n",
adev->get_mask, adev->set_mask);
/* Track dependencies betweed various settings */
if (adev->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) {
log(L_INIT, "important setting has been changed. "
"Need to update packet templates, too\n");
SET_BIT(adev->set_mask, SET_TEMPLATES);
}
if (adev->set_mask & GETSET_CHANNEL) {
/* This will actually tune RX/TX to the channel */
SET_BIT(adev->set_mask, GETSET_RX|GETSET_TX);
switch (adev->mode) {
case ACX_MODE_0_ADHOC:
case ACX_MODE_3_AP:
/* Beacons contain channel# - update them */
SET_BIT(adev->set_mask, SET_TEMPLATES);
}
switch (adev->mode) {
case ACX_MODE_0_ADHOC:
case ACX_MODE_2_STA:
start_scan = 1;
}
}
/* Apply settings */
#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */
/* send a disassoc request in case it's required */
if (adev->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP)) {
if (ACX_MODE_2_STA == adev->mode) {
if (ACX_STATUS_4_ASSOCIATED == adev->status) {
log(L_ASSOC, "we were ASSOCIATED - "
"sending disassoc request\n");
acx_lock(adev, flags);
acx_l_transmit_disassoc(adev, NULL);
/* FIXME: deauth? */
acx_unlock(adev, flags);
}
/* need to reset some other stuff as well */
log(L_DEBUG, "resetting bssid\n");
MAC_ZERO(adev->bssid);
SET_BIT(adev->set_mask, SET_TEMPLATES|SET_STA_LIST);
start_scan = 1;
}
}
#endif
if (adev->get_mask & GETSET_STATION_ID) {
u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN];
const u8 *paddr;
acx_s_interrogate(adev, &stationID, ACX1xx_IE_DOT11_STATION_ID);
paddr = &stationID[4];
for (i = 0; i < ETH_ALEN; i++) {
/* we copy the MAC address (reversed in
* the card) to the netdevice's MAC
* address, and on ifup it will be
* copied into iwadev->dev_addr */
adev->ndev->dev_addr[ETH_ALEN - 1 - i] = paddr[i];
}
CLEAR_BIT(adev->get_mask, GETSET_STATION_ID);
}
if (adev->get_mask & GETSET_SENSITIVITY) {
if ((RADIO_RFMD_11 == adev->radio_type)
|| (RADIO_MAXIM_0D == adev->radio_type)
|| (RADIO_RALINK_15 == adev->radio_type)) {
acx_s_read_phy_reg(adev, 0x30, &adev->sensitivity);
} else {
log(L_INIT, "don't know how to get sensitivity "
"for radio type 0x%02X\n", adev->radio_type);
adev->sensitivity = 0;
}
log(L_INIT, "got sensitivity value %u\n", adev->sensitivity);
CLEAR_BIT(adev->get_mask, GETSET_SENSITIVITY);
}
if (adev->get_mask & GETSET_ANTENNA) {
u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN];
memset(antenna, 0, sizeof(antenna));
acx_s_interrogate(adev, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA);
adev->antenna = antenna[4];
log(L_INIT, "got antenna value 0x%02X\n", adev->antenna);
CLEAR_BIT(adev->get_mask, GETSET_ANTENNA);
}
if (adev->get_mask & GETSET_ED_THRESH) {
if (IS_ACX100(adev)) {
u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN];
memset(ed_threshold, 0, sizeof(ed_threshold));
acx_s_interrogate(adev, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD);
adev->ed_threshold = ed_threshold[4];
} else {
log(L_INIT, "acx111 doesn't support ED\n");
adev->ed_threshold = 0;
}
log(L_INIT, "got Energy Detect (ED) threshold %u\n", adev->ed_threshold);
CLEAR_BIT(adev->get_mask, GETSET_ED_THRESH);
}
if (adev->get_mask & GETSET_CCA) {
if (IS_ACX100(adev)) {
u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN];
memset(cca, 0, sizeof(adev->cca));
acx_s_interrogate(adev, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE);
adev->cca = cca[4];
} else {
log(L_INIT, "acx111 doesn't support CCA\n");
adev->cca = 0;
}
log(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", adev->cca);
CLEAR_BIT(adev->get_mask, GETSET_CCA);
}
if (adev->get_mask & GETSET_REG_DOMAIN) {
acx_ie_generic_t dom;
acx_s_interrogate(adev, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN);
adev->reg_dom_id = dom.m.bytes[0];
acx_s_set_sane_reg_domain(adev, 0);
log(L_INIT, "got regulatory domain 0x%02X\n", adev->reg_dom_id);
CLEAR_BIT(adev->get_mask, GETSET_REG_DOMAIN);
}
if (adev->set_mask & GETSET_STATION_ID) {
u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN];
u8 *paddr;
paddr = &stationID[4];
for (i = 0; i < ETH_ALEN; i++) {
/* copy the MAC address we obtained when we noticed
* that the ethernet iface's MAC changed
* to the card (reversed in
* the card!) */
paddr[i] = adev->dev_addr[ETH_ALEN - 1 - i];
}
acx_s_configure(adev, &stationID, ACX1xx_IE_DOT11_STATION_ID);
CLEAR_BIT(adev->set_mask, GETSET_STATION_ID);
}
if (adev->set_mask & SET_TEMPLATES) {
log(L_INIT, "updating packet templates\n");
switch (adev->mode) {
case ACX_MODE_2_STA:
acx_s_set_probe_request_template(adev);
#if POWER_SAVE_80211
acx_s_set_null_data_template(adev);
#endif
break;
case ACX_MODE_0_ADHOC:
acx_s_set_probe_request_template(adev);
#if POWER_SAVE_80211
/* maybe power save functionality is somehow possible
* for Ad-Hoc mode, too... FIXME: verify it somehow? firmware debug fields? */
acx_s_set_null_data_template(adev);
#endif
/* fall through */
case ACX_MODE_3_AP:
acx_s_set_beacon_template(adev);
acx_s_set_tim_template(adev);
/* BTW acx111 firmware would not send probe responses
** if probe request does not have all basic rates flagged
** by 0x80! Thus firmware does not conform to 802.11,
** it should ignore 0x80 bit in ratevector from STA.
** We can 'fix' it by not using this template and
** sending probe responses by hand. TODO --vda */
acx_s_set_probe_response_template(adev);
}
/* Needed if generated frames are to be emitted at different tx rate now */
log(L_IRQ, "redoing cmd_join_bssid() after template cfg\n");
acx_s_cmd_join_bssid(adev, adev->bssid);
CLEAR_BIT(adev->set_mask, SET_TEMPLATES);
}
if (adev->set_mask & SET_STA_LIST) {
acx_lock(adev, flags);
acx_l_sta_list_init(adev);
CLEAR_BIT(adev->set_mask, SET_STA_LIST);
acx_unlock(adev, flags);
}
if (adev->set_mask & SET_RATE_FALLBACK) {
u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN];
/* configure to not do fallbacks when not in auto rate mode */
rate[4] = (adev->rate_auto) ? /* adev->txrate_fallback_retries */ 1 : 0;
log(L_INIT, "updating Tx fallback to %u retries\n", rate[4]);
acx_s_configure(adev, &rate, ACX1xx_IE_RATE_FALLBACK);
CLEAR_BIT(adev->set_mask, SET_RATE_FALLBACK);
}
if (adev->set_mask & GETSET_TXPOWER) {
log(L_INIT, "updating transmit power: %u dBm\n",
adev->tx_level_dbm);
acx_s_set_tx_level(adev, adev->tx_level_dbm);
CLEAR_BIT(adev->set_mask, GETSET_TXPOWER);
}
if (adev->set_mask & GETSET_SENSITIVITY) {
log(L_INIT, "updating sensitivity value: %u\n",
adev->sensitivity);
switch (adev->radio_type) {
case RADIO_RFMD_11:
case RADIO_MAXIM_0D:
case RADIO_RALINK_15:
acx_s_write_phy_reg(adev, 0x30, adev->sensitivity);
break;
case RADIO_RADIA_16:
case RADIO_UNKNOWN_17:
acx111_s_sens_radio_16_17(adev);
break;
default:
log(L_INIT, "don't know how to modify sensitivity "
"for radio type 0x%02X\n", adev->radio_type);
}
CLEAR_BIT(adev->set_mask, GETSET_SENSITIVITY);
}
if (adev->set_mask & GETSET_ANTENNA) {
/* antenna */
u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN];
memset(antenna, 0, sizeof(antenna));
antenna[4] = adev->antenna;
log(L_INIT, "updating antenna value: 0x%02X\n",
adev->antenna);
acx_s_configure(adev, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA);
CLEAR_BIT(adev->set_mask, GETSET_ANTENNA);
}
if (adev->set_mask & GETSET_ED_THRESH) {
/* ed_threshold */
log(L_INIT, "updating Energy Detect (ED) threshold: %u\n",
adev->ed_threshold);
if (IS_ACX100(adev)) {
u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN];
memset(ed_threshold, 0, sizeof(ed_threshold));
ed_threshold[4] = adev->ed_threshold;
acx_s_configure(adev, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD);
}
else
log(L_INIT, "acx111 doesn't support ED!\n");
CLEAR_BIT(adev->set_mask, GETSET_ED_THRESH);
}
if (adev->set_mask & GETSET_CCA) {
/* CCA value */
log(L_INIT, "updating Channel Clear Assessment "
"(CCA) value: 0x%02X\n", adev->cca);
if (IS_ACX100(adev)) {
u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN];
memset(cca, 0, sizeof(cca));
cca[4] = adev->cca;
acx_s_configure(adev, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE);
}
else
log(L_INIT, "acx111 doesn't support CCA!\n");
CLEAR_BIT(adev->set_mask, GETSET_CCA);
}
if (adev->set_mask & GETSET_LED_POWER) {
/* Enable Tx */
log(L_INIT, "updating power LED status: %u\n", adev->led_power);
acx_lock(adev, flags);
if (IS_PCI(adev))
acxpci_l_power_led(adev, adev->led_power);
CLEAR_BIT(adev->set_mask, GETSET_LED_POWER);
acx_unlock(adev, flags);
}
if (adev->set_mask & GETSET_POWER_80211) {
#if POWER_SAVE_80211
acx_s_update_80211_powersave_mode(adev);
#endif
CLEAR_BIT(adev->set_mask, GETSET_POWER_80211);
}
if (adev->set_mask & GETSET_CHANNEL) {
/* channel */
log(L_INIT, "updating channel to: %u\n", adev->channel);
CLEAR_BIT(adev->set_mask, GETSET_CHANNEL);
}
if (adev->set_mask & GETSET_TX) {
/* set Tx */
log(L_INIT, "updating: %s Tx\n",
adev->tx_disabled ? "disable" : "enable");
if (adev->tx_disabled)
acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0);
else
acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1);
CLEAR_BIT(adev->set_mask, GETSET_TX);
}
if (adev->set_mask & GETSET_RX) {
/* Enable Rx */
log(L_INIT, "updating: enable Rx on channel: %u\n",
adev->channel);
acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1);
CLEAR_BIT(adev->set_mask, GETSET_RX);
}
if (adev->set_mask & GETSET_RETRY) {
u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN];
u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN];
log(L_INIT, "updating short retry limit: %u, long retry limit: %u\n",
adev->short_retry, adev->long_retry);
short_retry[0x4] = adev->short_retry;
long_retry[0x4] = adev->long_retry;
acx_s_configure(adev, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT);
acx_s_configure(adev, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT);
CLEAR_BIT(adev->set_mask, GETSET_RETRY);
}
if (adev->set_mask & SET_MSDU_LIFETIME) {
u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN];
log(L_INIT, "updating tx MSDU lifetime: %u\n",
adev->msdu_lifetime);
*(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)adev->msdu_lifetime);
acx_s_configure(adev, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME);
CLEAR_BIT(adev->set_mask, SET_MSDU_LIFETIME);
}
if (adev->set_mask & GETSET_REG_DOMAIN) {
log(L_INIT, "updating regulatory domain: 0x%02X\n",
adev->reg_dom_id);
acx_s_set_sane_reg_domain(adev, 1);
CLEAR_BIT(adev->set_mask, GETSET_REG_DOMAIN);
}
if (adev->set_mask & GETSET_MODE) {
adev->ndev->type = (adev->mode == ACX_MODE_MONITOR) ?
adev->monitor_type : ARPHRD_ETHER;
switch (adev->mode) {
case ACX_MODE_3_AP:
acx_lock(adev, flags);
acx_l_sta_list_init(adev);
adev->aid = 0;
adev->ap_client = NULL;
MAC_COPY(adev->bssid, adev->dev_addr);
/* this basically says "we're connected" */
acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
acx_unlock(adev, flags);
acx111_s_feature_off(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
/* start sending beacons */
acx_s_cmd_join_bssid(adev, adev->bssid);
break;
case ACX_MODE_MONITOR:
acx111_s_feature_on(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
/* this stops beacons */
acx_s_cmd_join_bssid(adev, adev->bssid);
/* this basically says "we're connected" */
acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
SET_BIT(adev->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS);
break;
case ACX_MODE_0_ADHOC:
case ACX_MODE_2_STA:
acx111_s_feature_off(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
acx_lock(adev, flags);
adev->aid = 0;
adev->ap_client = NULL;
acx_unlock(adev, flags);
/* we want to start looking for peer or AP */
start_scan = 1;
break;
case ACX_MODE_OFF:
/* TODO: disable RX/TX, stop any scanning activity etc: */
/* adev->tx_disabled = 1; */
/* SET_BIT(adev->set_mask, GETSET_RX|GETSET_TX); */
/* This stops beacons (invalid macmode...) */
acx_s_cmd_join_bssid(adev, adev->bssid);
acx_set_status(adev, ACX_STATUS_0_STOPPED);
break;
}
CLEAR_BIT(adev->set_mask, GETSET_MODE);
}
if (adev->set_mask & SET_RXCONFIG) {
acx_s_initialize_rx_config(adev);
CLEAR_BIT(adev->set_mask, SET_RXCONFIG);
}
if (adev->set_mask & GETSET_RESCAN) {
switch (adev->mode) {
case ACX_MODE_0_ADHOC:
case ACX_MODE_2_STA:
start_scan = 1;
break;
}
CLEAR_BIT(adev->set_mask, GETSET_RESCAN);
}
if (adev->set_mask & GETSET_WEP) {
/* encode */
ie_dot11WEPDefaultKeyID_t dkey;
#ifdef DEBUG_WEP
struct {
u16 type;
u16 len;
u8 val;
} ACX_PACKED keyindic;
#endif
log(L_INIT, "updating WEP key settings\n");
acx_s_set_wepkey(adev);
dkey.KeyID = adev->ieee->sec.active_key;
log(L_INIT, "setting WEP key %u as default\n", dkey.KeyID);
acx_s_configure(adev, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET);
#ifdef DEBUG_WEP
keyindic.val = 3;
acx_s_configure(adev, &keyindic, ACX111_IE_KEY_CHOOSE);
#endif
start_scan = 1;
CLEAR_BIT(adev->set_mask, GETSET_WEP);
}
if (adev->set_mask & SET_WEP_OPTIONS) {
acx100_ie_wep_options_t options;
if (IS_ACX111(adev)) {
log(L_DEBUG, "setting WEP Options for acx111 is not supported\n");
} else {
log(L_INIT, "setting WEP Options\n");
/* let's choose maximum setting: 4 default keys,
* plus 10 other keys: */
options.NumKeys = cpu_to_le16(WEP_KEYS + 10);
/* don't decrypt default key only,
* don't override decryption: */
options.WEPOption = 0;
if (adev->mode == ACX_MODE_MONITOR) {
/* don't decrypt default key only,
* override decryption mechanism: */
options.WEPOption = 2;
}
acx_s_configure(adev, &options, ACX100_IE_WEP_OPTIONS);
}
CLEAR_BIT(adev->set_mask, SET_WEP_OPTIONS);
}
/* Rescan was requested */
if (start_scan) {
switch (adev->mode) {
case ACX_MODE_0_ADHOC:
case ACX_MODE_2_STA:
/* We can avoid clearing list if join code
** will be a bit more clever about not picking
** 'bad' AP over and over again */
acx_lock(adev, flags);
adev->ap_client = NULL;
acx_l_sta_list_init(adev);
acx_set_status(adev, ACX_STATUS_1_SCANNING);
acx_unlock(adev, flags);
acx_s_cmd_start_scan(adev);
}
}
/* debug, rate, and nick don't need any handling */
/* what about sniffing mode?? */
log(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n",
adev->get_mask, adev->set_mask);
/* end: */
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_s_update_card_settings);
/***********************************************************************
** acx_e_after_interrupt_task
*/
static int
acx_s_recalib_radio(acx_device_t *adev)
{
if (IS_ACX111(adev)) {
acx111_cmd_radiocalib_t cal;
printk("%s: recalibrating radio\n", adev->ndev->name);
/* automatic recalibration, choose all methods: */
cal.methods = cpu_to_le32(0x8000000f);
/* automatic recalibration every 60 seconds (value in TUs)
* I wonder what the firmware default here is? */
cal.interval = cpu_to_le32(58594);
return acx_s_issue_cmd_timeo(adev, ACX111_CMD_RADIOCALIB,
&cal, sizeof(cal), CMD_TIMEOUT_MS(100));
} else {
/* On ACX100, we need to recalibrate the radio
* by issuing a GETSET_TX|GETSET_RX */
if (/* (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0)) &&
(OK == acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */
(OK == acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1)) &&
(OK == acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1)) )
return OK;
return NOT_OK;
}
}
static void
acx_s_after_interrupt_recalib(acx_device_t *adev)
{
int res;
/* this helps with ACX100 at least;
* hopefully ACX111 also does a
* recalibration here */
/* clear flag beforehand, since we want to make sure
* it's cleared; then only set it again on specific circumstances */
CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
/* better wait a bit between recalibrations to
* prevent overheating due to torturing the card
* into working too long despite high temperature
* (just a safety measure) */
if (adev->recalib_time_last_success
&& time_before(jiffies, adev->recalib_time_last_success
+ RECALIB_PAUSE * 60 * HZ)) {
if (adev->recalib_msg_ratelimit <= 4) {
printk("%s: less than " STRING(RECALIB_PAUSE)
" minutes since last radio recalibration, "
"not recalibrating (maybe card is too hot?)\n",
adev->ndev->name);
adev->recalib_msg_ratelimit++;
if (adev->recalib_msg_ratelimit == 5)
printk("disabling above message\n");
}
return;
}
adev->recalib_msg_ratelimit = 0;
/* note that commands sometimes fail (card busy),
* so only clear flag if we were fully successful */
res = acx_s_recalib_radio(adev);
if (res == OK) {
printk("%s: successfully recalibrated radio\n",
adev->ndev->name);
adev->recalib_time_last_success = jiffies;
adev->recalib_failure_count = 0;
} else {
/* failed: resubmit, but only limited
* amount of times within some time range
* to prevent endless loop */
adev->recalib_time_last_success = 0; /* we failed */
/* if some time passed between last
* attempts, then reset failure retry counter
* to be able to do next recalib attempt */
if (time_after(jiffies, adev->recalib_time_last_attempt + 5*HZ))
adev->recalib_failure_count = 0;
if (adev->recalib_failure_count < 5) {
/* increment inside only, for speedup of outside path */
adev->recalib_failure_count++;
adev->recalib_time_last_attempt = jiffies;
acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
}
}
}
static void
acx_e_after_interrupt_task(struct work_struct *work)
{
acx_device_t *adev =
container_of(work, acx_device_t, after_interrupt_task);
FN_ENTER;
acx_sem_lock(adev);
if (!adev->after_interrupt_jobs)
goto end; /* no jobs to do */
#if TX_CLEANUP_IN_SOFTIRQ
/* can happen only on PCI */
if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) {
acx_lock(adev, flags);
acxpci_l_clean_txdesc(adev);
CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP);
acx_unlock(adev, flags);
}
#endif
/* we see lotsa tx errors */
if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) {
acx_s_after_interrupt_recalib(adev);
}
/* a poor interrupt code wanted to do update_card_settings() */
if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) {
if (ACX_STATE_IFACE_UP & adev->dev_state_mask)
acx_s_update_card_settings(adev);
CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
}
/* 1) we detected that no Scan_Complete IRQ came from fw, or
** 2) we found too many STAs */
if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) {
log(L_IRQ, "sending a stop scan cmd...\n");
acx_s_issue_cmd(adev, ACX1xx_CMD_STOP_SCAN, NULL, 0);
/* HACK: set the IRQ bit, since we won't get a
* scan complete IRQ any more on ACX111 (works on ACX100!),
* since _we_, not a fw, have stopped the scan */
SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE);
CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN);
}
/* either fw sent Scan_Complete or we detected that
** no Scan_Complete IRQ came from fw. Finish scanning,
** pick join partner if any */
if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) {
if (adev->status == ACX_STATUS_1_SCANNING) {
if (OK != acx_s_complete_scan(adev)) {
SET_BIT(adev->after_interrupt_jobs,
ACX_AFTER_IRQ_RESTART_SCAN);
}
} else {
/* + scan kills current join status - restore it
** (do we need it for STA?) */
/* + does it happen only with active scans?
** active and passive scans? ALL scans including
** background one? */
/* + was not verified that everything is restored
** (but at least we start to emit beacons again) */
switch (adev->mode) {
case ACX_MODE_0_ADHOC:
case ACX_MODE_3_AP:
log(L_IRQ, "redoing cmd_join_bssid() after scan\n");
acx_s_cmd_join_bssid(adev, adev->bssid);
}
}
CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN);
}
/* STA auth or assoc timed out, start over again */
if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) {
log(L_IRQ, "sending a start_scan cmd...\n");
acx_s_cmd_start_scan(adev);
CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN);
}
/* whee, we got positive assoc response! 8) */
if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) {
acx_ie_generic_t pdr;
/* tiny race window exists, checking that we still a STA */
switch (adev->mode) {
case ACX_MODE_2_STA:
pdr.m.aid = cpu_to_le16(adev->aid);
acx_s_configure(adev, &pdr, ACX1xx_IE_ASSOC_ID);
acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
log(L_ASSOC|L_DEBUG, "ASSOCIATED!\n");
CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE);
}
}
end:
acx_sem_unlock(adev);
FN_EXIT0;
}
/***********************************************************************
** acx_schedule_task
**
** Schedule the call of the after_interrupt method after leaving
** the interrupt context.
*/
void
acx_schedule_task(acx_device_t *adev, unsigned int set_flag)
{
SET_BIT(adev->after_interrupt_jobs, set_flag);
schedule_work(&adev->after_interrupt_task);
}
EXPORT_SYMBOL_GPL(acx_schedule_task);
/***********************************************************************
*/
void
acx_init_task_scheduler(acx_device_t *adev)
{
/* configure task scheduler */
INIT_WORK(&adev->after_interrupt_task, acx_e_after_interrupt_task);
}
EXPORT_SYMBOL_GPL(acx_init_task_scheduler);
/***********************************************************************
** acx_s_start
*/
void
acx_s_start(acx_device_t *adev)
{
FN_ENTER;
/*
* Ok, now we do everything that can possibly be done with ioctl
* calls to make sure that when it was called before the card
* was up we get the changes asked for
*/
SET_BIT(adev->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP
|GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA
|GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL
|GETSET_TX|GETSET_RX);
log(L_INIT, "updating initial settings on iface activation\n");
acx_s_update_card_settings(adev);
FN_EXIT0;
}
EXPORT_SYMBOL_GPL(acx_s_start);
/***********************************************************************
** acx_update_capabilities
*/
void
acx_update_capabilities(acx_device_t *adev)
{
u16 cap = 0;
switch (adev->mode) {
case ACX_MODE_3_AP:
SET_BIT(cap, WF_MGMT_CAP_ESS); break;
case ACX_MODE_0_ADHOC:
SET_BIT(cap, WF_MGMT_CAP_IBSS); break;
/* other types of stations do not emit beacons */
}
if (adev->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY) {
SET_BIT(cap, WF_MGMT_CAP_PRIVACY);
}
if (adev->cfgopt_dot11ShortPreambleOption) {
SET_BIT(cap, WF_MGMT_CAP_SHORT);
}
if (adev->cfgopt_dot11PBCCOption) {
SET_BIT(cap, WF_MGMT_CAP_PBCC);
}
if (adev->cfgopt_dot11ChannelAgility) {
SET_BIT(cap, WF_MGMT_CAP_AGILITY);
}
log(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n",
adev->capabilities, cap);
adev->capabilities = cap;
}
/***********************************************************************
** Common function to parse ALL configoption struct formats
** (ACX100 and ACX111; FIXME: how to make it work with ACX100 USB!?!?).
** FIXME: logging should be removed here and added to a /proc file instead
*/
void
acx_s_parse_configoption(acx_device_t *adev, const acx111_ie_configoption_t *pcfg)
{
const u8 *pEle;
int i;
int is_acx111 = IS_ACX111(adev);
if (acx_debug & L_DEBUG) {
printk("configoption struct content:\n");
acx_dump_bytes(pcfg, sizeof(*pcfg));
}
if (( is_acx111 && (adev->eeprom_version == 5))
|| (!is_acx111 && (adev->eeprom_version == 4))
|| (!is_acx111 && (adev->eeprom_version == 5))) {
/* these versions are known to be supported */
} else {
printk("unknown chip and EEPROM version combination (%s, v%d), "
"don't know how to parse config options yet. "
"Please report\n", is_acx111 ? "ACX111" : "ACX100",
adev->eeprom_version);
return;
}
/* first custom-parse the first part which has chip-specific layout */
pEle = (const u8 *) pcfg;
pEle += 4; /* skip (type,len) header */
memcpy(adev->cfgopt_NVSv, pEle, sizeof(adev->cfgopt_NVSv));
pEle += sizeof(adev->cfgopt_NVSv);
if (is_acx111) {
adev->cfgopt_NVS_vendor_offs = le16_to_cpu(*(u16 *)pEle);
pEle += sizeof(adev->cfgopt_NVS_vendor_offs);
adev->cfgopt_probe_delay = 200; /* good default value? */
pEle += 2; /* FIXME: unknown, value 0x0001 */
} else {
memcpy(adev->cfgopt_MAC, pEle, sizeof(adev->cfgopt_MAC));
pEle += sizeof(adev->cfgopt_MAC);
adev->cfgopt_probe_delay = le16_to_cpu(*(u16 *)pEle);
pEle += sizeof(adev->cfgopt_probe_delay);
if ((adev->cfgopt_probe_delay < 100) || (adev->cfgopt_probe_delay > 500)) {
printk("strange probe_delay value %d, "
"tweaking to 200\n", adev->cfgopt_probe_delay);
adev->cfgopt_probe_delay = 200;
}
}
adev->cfgopt_eof_memory = le32_to_cpu(*(u32 *)pEle);
pEle += sizeof(adev->cfgopt_eof_memory);
printk("NVS_vendor_offs:%04X probe_delay:%d eof_memory:%d\n",
adev->cfgopt_NVS_vendor_offs,
adev->cfgopt_probe_delay,
adev->cfgopt_eof_memory);
adev->cfgopt_dot11CCAModes = *pEle++;
adev->cfgopt_dot11Diversity = *pEle++;
adev->cfgopt_dot11ShortPreambleOption = *pEle++;
adev->cfgopt_dot11PBCCOption = *pEle++;
adev->cfgopt_dot11ChannelAgility = *pEle++;
adev->cfgopt_dot11PhyType = *pEle++;
adev->cfgopt_dot11TempType = *pEle++;
printk("CCAModes:%02X Diversity:%02X ShortPreOpt:%02X "
"PBCC:%02X ChanAgil:%02X PHY:%02X Temp:%02X\n",
adev->cfgopt_dot11CCAModes,
adev->cfgopt_dot11Diversity,
adev->cfgopt_dot11ShortPreambleOption,
adev->cfgopt_dot11PBCCOption,
adev->cfgopt_dot11ChannelAgility,
adev->cfgopt_dot11PhyType,
adev->cfgopt_dot11TempType);
/* then use common parsing for next part which has common layout */
pEle++; /* skip table_count (6) */
adev->cfgopt_antennas.type = pEle[0];
adev->cfgopt_antennas.len = pEle[1];
printk("AntennaID:%02X Len:%02X Data:",
adev->cfgopt_antennas.type, adev->cfgopt_antennas.len);
for (i = 0; i < pEle[1]; i++) {
adev->cfgopt_antennas.list[i] = pEle[i+2];
printk("%02X ", pEle[i+2]);
}
printk("\n");
pEle += pEle[1] + 2;
adev->cfgopt_power_levels.type = pEle[0];
adev->cfgopt_power_levels.len = pEle[1];
printk("PowerLevelID:%02X Len:%02X Data:",
adev->cfgopt_power_levels.type, adev->cfgopt_power_levels.len);
for (i = 0; i < pEle[1]; i++) {
adev->cfgopt_power_levels.list[i] = le16_to_cpu(*(u16 *)&pEle[i*2+2]);
printk("%04X ", adev->cfgopt_power_levels.list[i]);
}
printk("\n");
pEle += pEle[1]*2 + 2;
adev->cfgopt_data_rates.type = pEle[0];
adev->cfgopt_data_rates.len = pEle[1];
printk("DataRatesID:%02X Len:%02X Data:",
adev->cfgopt_data_rates.type, adev->cfgopt_data_rates.len);
for (i = 0; i < pEle[1]; i++) {
adev->cfgopt_data_rates.list[i] = pEle[i+2];
printk("%02X ", pEle[i+2]);
}
printk("\n");
pEle += pEle[1] + 2;
adev->cfgopt_domains.type = pEle[0];
adev->cfgopt_domains.len = pEle[1];
printk("DomainID:%02X Len:%02X Data:",
adev->cfgopt_domains.type, adev->cfgopt_domains.len);
for (i = 0; i < pEle[1]; i++) {
adev->cfgopt_domains.list[i] = pEle[i+2];
printk("%02X ", pEle[i+2]);
}
printk("\n");
pEle += pEle[1] + 2;
adev->cfgopt_product_id.type = pEle[0];
adev->cfgopt_product_id.len = pEle[1];
for (i = 0; i < pEle[1]; i++) {
adev->cfgopt_product_id.list[i] = pEle[i+2];
}
printk("ProductID:%02X Len:%02X Data:%.*s\n",
adev->cfgopt_product_id.type, adev->cfgopt_product_id.len,
adev->cfgopt_product_id.len, (char *)adev->cfgopt_product_id.list);
pEle += pEle[1] + 2;
adev->cfgopt_manufacturer.type = pEle[0];
adev->cfgopt_manufacturer.len = pEle[1];
for (i = 0; i < pEle[1]; i++) {
adev->cfgopt_manufacturer.list[i] = pEle[i+2];
}
printk("ManufacturerID:%02X Len:%02X Data:%.*s\n",
adev->cfgopt_manufacturer.type, adev->cfgopt_manufacturer.len,
adev->cfgopt_manufacturer.len, (char *)adev->cfgopt_manufacturer.list);
/*
printk("EEPROM part:\n");
for (i=0; i<58; i++) {
printk("%02X =======> 0x%02X\n",
i, (u8 *)adev->cfgopt_NVSv[i-2]);
}
*/
}
EXPORT_SYMBOL_GPL(acx_s_parse_configoption);
//SM
void
acx_e_ieee80211_set_security(struct net_device *ndev,
struct ieee80211_security *sec)
{
/* Shamelessly copied from the rt2x00 project. */
acx_device_t *adev = ndev2adev(ndev);
unsigned long flags;
int i;
acx_sem_lock(adev);
acx_lock(adev, flags);
for (i = 0; i < WEP_KEYS; ++i) {
/* This gives us the flag for the 4 WEP keys. */
if (sec->flags & (1 << i)) {
adev->ieee->sec.encode_alg[i] = sec->encode_alg[i];
adev->ieee->sec.key_sizes[i] = sec->key_sizes[i];
if (sec->key_sizes[i] != 0) {
memcpy(adev->ieee->sec.keys[i], sec->keys[i],
sec->key_sizes[i]);
/* Make sure WEP flag is set. */
adev->ieee->sec.flags |= (1 << i);
} else if (sec->level != SEC_LEVEL_1)
/* Make sure WEP flag isn't set. */
adev->ieee->sec.flags &= ~(1 << i);
}
SET_BIT(adev->set_mask, SET_WEP_OPTIONS);
}
if (sec->flags & SEC_ACTIVE_KEY) {
/* Check the key number is valid. */
if (sec->active_key < WEP_KEYS) {
adev->ieee->sec.active_key = sec->active_key;
adev->ieee->sec.flags |= SEC_ACTIVE_KEY;
} else
adev->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
} else
adev->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
if (sec->flags & SEC_AUTH_MODE) {
adev->ieee->sec.auth_mode = sec->auth_mode;
adev->ieee->sec.flags |= SEC_AUTH_MODE;
SET_BIT(adev->set_mask, SET_WEP_OPTIONS);
}
if (sec->flags & SEC_ENCRYPT) {
adev->ieee->sec.encrypt = sec->encrypt;
adev->ieee->sec.flags |= SEC_ENCRYPT;
SET_BIT(adev->set_mask, GETSET_WEP);
}
if (sec->flags & SEC_ENABLED) {
adev->ieee->sec.enabled = sec->enabled;
adev->ieee->sec.flags |= SEC_ENABLED;
SET_BIT(adev->set_mask, GETSET_WEP);
}
if (sec->flags & SEC_LEVEL) {
adev->ieee->sec.level = sec->level;
adev->ieee->sec.flags |= SEC_LEVEL;
SET_BIT(adev->set_mask, GETSET_WEP);
}
acx_unlock(adev, flags);
acx_sem_unlock(adev);
acx_s_update_card_settings(adev);
}
EXPORT_SYMBOL_GPL(acx_e_ieee80211_set_security);
void
acx_e_ieee80211_set_chan(struct net_device *ndev, u8 channel)
{
acx_device_t *adev = ndev2adev(ndev);
acx_sem_lock(adev);
acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1);
acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1);
acx_sem_unlock(adev);
}
EXPORT_SYMBOL_GPL(acx_e_ieee80211_set_chan);