Commit cbdbc5eb authored by John W. Linville's avatar John W. Linville
parents 8d8d3fdc e5e2f24b
...@@ -6742,12 +6742,12 @@ S: Maintained ...@@ -6742,12 +6742,12 @@ S: Maintained
F: drivers/net/wireless/wl1251/* F: drivers/net/wireless/wl1251/*
WL1271 WIRELESS DRIVER WL1271 WIRELESS DRIVER
M: Luciano Coelho <luciano.coelho@nokia.com> M: Luciano Coelho <coelho@ti.com>
L: linux-wireless@vger.kernel.org L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org W: http://wireless.kernel.org/en/users/Drivers/wl12xx
T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git
S: Maintained S: Maintained
F: drivers/net/wireless/wl12xx/wl1271* F: drivers/net/wireless/wl12xx/
F: include/linux/wl12xx.h F: include/linux/wl12xx.h
WL3501 WIRELESS PCMCIA CARD DRIVER WL3501 WIRELESS PCMCIA CARD DRIVER
......
...@@ -751,10 +751,10 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) ...@@ -751,10 +751,10 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
return 0; return 0;
} }
int wl1271_acx_rate_policies(struct wl1271 *wl) int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
{ {
struct acx_rate_policy *acx; struct acx_sta_rate_policy *acx;
struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf; struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf;
int idx = 0; int idx = 0;
int ret = 0; int ret = 0;
...@@ -794,6 +794,38 @@ int wl1271_acx_rate_policies(struct wl1271 *wl) ...@@ -794,6 +794,38 @@ int wl1271_acx_rate_policies(struct wl1271 *wl)
return ret; return ret;
} }
int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
u8 idx)
{
struct acx_ap_rate_policy *acx;
int ret = 0;
wl1271_debug(DEBUG_ACX, "acx ap rate policy");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
acx->rate_policy.enabled_rates = cpu_to_le32(c->enabled_rates);
acx->rate_policy.short_retry_limit = c->short_retry_limit;
acx->rate_policy.long_retry_limit = c->long_retry_limit;
acx->rate_policy.aflags = c->aflags;
acx->rate_policy_idx = cpu_to_le32(idx);
ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("Setting of ap rate policy failed: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
u8 aifsn, u16 txop) u8 aifsn, u16 txop)
{ {
...@@ -1233,6 +1265,7 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, ...@@ -1233,6 +1265,7 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
struct wl1271_acx_ht_capabilities *acx; struct wl1271_acx_ht_capabilities *acx;
u8 mac_address[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; u8 mac_address[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
int ret = 0; int ret = 0;
u32 ht_capabilites = 0;
wl1271_debug(DEBUG_ACX, "acx ht capabilities setting"); wl1271_debug(DEBUG_ACX, "acx ht capabilities setting");
...@@ -1244,16 +1277,16 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, ...@@ -1244,16 +1277,16 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
/* Allow HT Operation ? */ /* Allow HT Operation ? */
if (allow_ht_operation) { if (allow_ht_operation) {
acx->ht_capabilites = ht_capabilites =
WL1271_ACX_FW_CAP_HT_OPERATION; WL1271_ACX_FW_CAP_HT_OPERATION;
if (ht_cap->cap & IEEE80211_HT_CAP_GRN_FLD) if (ht_cap->cap & IEEE80211_HT_CAP_GRN_FLD)
acx->ht_capabilites |= ht_capabilites |=
WL1271_ACX_FW_CAP_GREENFIELD_FRAME_FORMAT; WL1271_ACX_FW_CAP_GREENFIELD_FRAME_FORMAT;
if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
acx->ht_capabilites |= ht_capabilites |=
WL1271_ACX_FW_CAP_SHORT_GI_FOR_20MHZ_PACKETS; WL1271_ACX_FW_CAP_SHORT_GI_FOR_20MHZ_PACKETS;
if (ht_cap->cap & IEEE80211_HT_CAP_LSIG_TXOP_PROT) if (ht_cap->cap & IEEE80211_HT_CAP_LSIG_TXOP_PROT)
acx->ht_capabilites |= ht_capabilites |=
WL1271_ACX_FW_CAP_LSIG_TXOP_PROTECTION; WL1271_ACX_FW_CAP_LSIG_TXOP_PROTECTION;
/* get data from A-MPDU parameters field */ /* get data from A-MPDU parameters field */
...@@ -1261,10 +1294,10 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, ...@@ -1261,10 +1294,10 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
acx->ampdu_min_spacing = ht_cap->ampdu_density; acx->ampdu_min_spacing = ht_cap->ampdu_density;
memcpy(acx->mac_address, mac_address, ETH_ALEN); memcpy(acx->mac_address, mac_address, ETH_ALEN);
} else { /* HT operations are not allowed */
acx->ht_capabilites = 0;
} }
acx->ht_capabilites = cpu_to_le32(ht_capabilites);
ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx)); ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx));
if (ret < 0) { if (ret < 0) {
wl1271_warning("acx ht capabilities setting failed: %d", ret); wl1271_warning("acx ht capabilities setting failed: %d", ret);
...@@ -1309,6 +1342,91 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl, ...@@ -1309,6 +1342,91 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl,
return ret; return ret;
} }
/* Configure BA session initiator/receiver parameters setting in the FW. */
int wl1271_acx_set_ba_session(struct wl1271 *wl,
enum ieee80211_back_parties direction,
u8 tid_index, u8 policy)
{
struct wl1271_acx_ba_session_policy *acx;
int ret;
wl1271_debug(DEBUG_ACX, "acx ba session setting");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
/* ANY role */
acx->role_id = 0xff;
acx->tid = tid_index;
acx->enable = policy;
acx->ba_direction = direction;
switch (direction) {
case WLAN_BACK_INITIATOR:
acx->win_size = wl->conf.ht.tx_ba_win_size;
acx->inactivity_timeout = wl->conf.ht.inactivity_timeout;
break;
case WLAN_BACK_RECIPIENT:
acx->win_size = RX_BA_WIN_SIZE;
acx->inactivity_timeout = 0;
break;
default:
wl1271_error("Incorrect acx command id=%x\n", direction);
ret = -EINVAL;
goto out;
}
ret = wl1271_cmd_configure(wl,
ACX_BA_SESSION_POLICY_CFG,
acx,
sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx ba session setting failed: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
/* setup BA session receiver setting in the FW. */
int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
bool enable)
{
struct wl1271_acx_ba_receiver_setup *acx;
int ret;
wl1271_debug(DEBUG_ACX, "acx ba receiver session setting");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
/* Single link for now */
acx->link_id = 1;
acx->tid = tid_index;
acx->enable = enable;
acx->win_size = 0;
acx->ssn = ssn;
ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx,
sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx ba receiver session failed: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime) int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
{ {
struct wl1271_acx_fw_tsf_information *tsf_info; struct wl1271_acx_fw_tsf_information *tsf_info;
...@@ -1334,3 +1452,27 @@ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime) ...@@ -1334,3 +1452,27 @@ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
kfree(tsf_info); kfree(tsf_info);
return ret; return ret;
} }
int wl1271_acx_max_tx_retry(struct wl1271 *wl)
{
struct wl1271_acx_max_tx_retry *acx = NULL;
int ret;
wl1271_debug(DEBUG_ACX, "acx max tx retry");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx)
return -ENOMEM;
acx->max_tx_retry = cpu_to_le16(wl->conf.tx.ap_max_tx_retries);
ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx max tx retry failed: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
...@@ -747,13 +747,23 @@ struct acx_rate_class { ...@@ -747,13 +747,23 @@ struct acx_rate_class {
#define ACX_TX_BASIC_RATE 0 #define ACX_TX_BASIC_RATE 0
#define ACX_TX_AP_FULL_RATE 1 #define ACX_TX_AP_FULL_RATE 1
#define ACX_TX_RATE_POLICY_CNT 2 #define ACX_TX_RATE_POLICY_CNT 2
struct acx_rate_policy { struct acx_sta_rate_policy {
struct acx_header header; struct acx_header header;
__le32 rate_class_cnt; __le32 rate_class_cnt;
struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES]; struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES];
} __packed; } __packed;
#define ACX_TX_AP_MODE_MGMT_RATE 4
#define ACX_TX_AP_MODE_BCST_RATE 5
struct acx_ap_rate_policy {
struct acx_header header;
__le32 rate_policy_idx;
struct acx_rate_class rate_policy;
} __packed;
struct acx_ac_cfg { struct acx_ac_cfg {
struct acx_header header; struct acx_header header;
u8 ac; u8 ac;
...@@ -1051,6 +1061,59 @@ struct wl1271_acx_ht_information { ...@@ -1051,6 +1061,59 @@ struct wl1271_acx_ht_information {
u8 padding[3]; u8 padding[3];
} __packed; } __packed;
#define RX_BA_WIN_SIZE 8
struct wl1271_acx_ba_session_policy {
struct acx_header header;
/*
* Specifies role Id, Range 0-7, 0xFF means ANY role.
* Future use. For now this field is irrelevant
*/
u8 role_id;
/*
* Specifies Link Id, Range 0-31, 0xFF means ANY Link Id.
* Not applicable if Role Id is set to ANY.
*/
u8 link_id;
u8 tid;
u8 enable;
/* Windows size in number of packets */
u16 win_size;
/*
* As initiator inactivity timeout in time units(TU) of 1024us.
* As receiver reserved
*/
u16 inactivity_timeout;
/* Initiator = 1/Receiver = 0 */
u8 ba_direction;
u8 padding[3];
} __packed;
struct wl1271_acx_ba_receiver_setup {
struct acx_header header;
/* Specifies Link Id, Range 0-31, 0xFF means ANY Link Id */
u8 link_id;
u8 tid;
u8 enable;
u8 padding[1];
/* Windows size in number of packets */
u16 win_size;
/* BA session starting sequence number. RANGE 0-FFF */
u16 ssn;
} __packed;
struct wl1271_acx_fw_tsf_information { struct wl1271_acx_fw_tsf_information {
struct acx_header header; struct acx_header header;
...@@ -1062,6 +1125,17 @@ struct wl1271_acx_fw_tsf_information { ...@@ -1062,6 +1125,17 @@ struct wl1271_acx_fw_tsf_information {
u8 padding[3]; u8 padding[3];
} __packed; } __packed;
struct wl1271_acx_max_tx_retry {
struct acx_header header;
/*
* the number of frames transmission failures before
* issuing the aging event.
*/
__le16 max_tx_retry;
u8 padding_1[2];
} __packed;
enum { enum {
ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003, ACX_MEM_CFG = 0x0003,
...@@ -1113,12 +1187,13 @@ enum { ...@@ -1113,12 +1187,13 @@ enum {
ACX_RSSI_SNR_WEIGHTS = 0x0052, ACX_RSSI_SNR_WEIGHTS = 0x0052,
ACX_KEEP_ALIVE_MODE = 0x0053, ACX_KEEP_ALIVE_MODE = 0x0053,
ACX_SET_KEEP_ALIVE_CONFIG = 0x0054, ACX_SET_KEEP_ALIVE_CONFIG = 0x0054,
ACX_BA_SESSION_RESPONDER_POLICY = 0x0055, ACX_BA_SESSION_POLICY_CFG = 0x0055,
ACX_BA_SESSION_INITIATOR_POLICY = 0x0056, ACX_BA_SESSION_RX_SETUP = 0x0056,
ACX_PEER_HT_CAP = 0x0057, ACX_PEER_HT_CAP = 0x0057,
ACX_HT_BSS_OPERATION = 0x0058, ACX_HT_BSS_OPERATION = 0x0058,
ACX_COEX_ACTIVITY = 0x0059, ACX_COEX_ACTIVITY = 0x0059,
ACX_SET_DCO_ITRIM_PARAMS = 0x0061, ACX_SET_DCO_ITRIM_PARAMS = 0x0061,
ACX_MAX_TX_FAILURE = 0x0072,
DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_RX_MSDU_LIFE_TIME = 0x1004,
DOT11_CUR_TX_PWR = 0x100D, DOT11_CUR_TX_PWR = 0x100D,
DOT11_RX_DOT11_MODE = 0x1012, DOT11_RX_DOT11_MODE = 0x1012,
...@@ -1160,7 +1235,9 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble); ...@@ -1160,7 +1235,9 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble);
int wl1271_acx_cts_protect(struct wl1271 *wl, int wl1271_acx_cts_protect(struct wl1271 *wl,
enum acx_ctsprotect_type ctsprotect); enum acx_ctsprotect_type ctsprotect);
int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats); int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
int wl1271_acx_rate_policies(struct wl1271 *wl); int wl1271_acx_sta_rate_policies(struct wl1271 *wl);
int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
u8 idx);
int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
u8 aifsn, u16 txop); u8 aifsn, u16 txop);
int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
...@@ -1185,6 +1262,12 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, ...@@ -1185,6 +1262,12 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
bool allow_ht_operation); bool allow_ht_operation);
int wl1271_acx_set_ht_information(struct wl1271 *wl, int wl1271_acx_set_ht_information(struct wl1271 *wl,
u16 ht_operation_mode); u16 ht_operation_mode);
int wl1271_acx_set_ba_session(struct wl1271 *wl,
enum ieee80211_back_parties direction,
u8 tid_index, u8 policy);
int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
bool enable);
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
int wl1271_acx_max_tx_retry(struct wl1271 *wl);
#endif /* __WL1271_ACX_H__ */ #endif /* __WL1271_ACX_H__ */
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "boot.h" #include "boot.h"
#include "io.h" #include "io.h"
#include "event.h" #include "event.h"
#include "rx.h"
static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
[PART_DOWN] = { [PART_DOWN] = {
...@@ -100,6 +101,22 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) ...@@ -100,6 +101,22 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
} }
static void wl1271_parse_fw_ver(struct wl1271 *wl)
{
int ret;
ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u",
&wl->chip.fw_ver[0], &wl->chip.fw_ver[1],
&wl->chip.fw_ver[2], &wl->chip.fw_ver[3],
&wl->chip.fw_ver[4]);
if (ret != 5) {
wl1271_warning("fw version incorrect value");
memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
return;
}
}
static void wl1271_boot_fw_version(struct wl1271 *wl) static void wl1271_boot_fw_version(struct wl1271 *wl)
{ {
struct wl1271_static_data static_data; struct wl1271_static_data static_data;
...@@ -107,11 +124,13 @@ static void wl1271_boot_fw_version(struct wl1271 *wl) ...@@ -107,11 +124,13 @@ static void wl1271_boot_fw_version(struct wl1271 *wl)
wl1271_read(wl, wl->cmd_box_addr, &static_data, sizeof(static_data), wl1271_read(wl, wl->cmd_box_addr, &static_data, sizeof(static_data),
false); false);
strncpy(wl->chip.fw_ver, static_data.fw_version, strncpy(wl->chip.fw_ver_str, static_data.fw_version,
sizeof(wl->chip.fw_ver)); sizeof(wl->chip.fw_ver_str));
/* make sure the string is NULL-terminated */ /* make sure the string is NULL-terminated */
wl->chip.fw_ver[sizeof(wl->chip.fw_ver) - 1] = '\0'; wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
wl1271_parse_fw_ver(wl);
} }
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
...@@ -231,7 +250,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) ...@@ -231,7 +250,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
*/ */
if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
if (wl->nvs->general_params.dual_mode_select) /* for now 11a is unsupported in AP mode */
if (wl->bss_type != BSS_TYPE_AP_BSS &&
wl->nvs->general_params.dual_mode_select)
wl->enable_11a = true; wl->enable_11a = true;
} }
...@@ -431,6 +452,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) ...@@ -431,6 +452,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
PSPOLL_DELIVERY_FAILURE_EVENT_ID | PSPOLL_DELIVERY_FAILURE_EVENT_ID |
SOFT_GEMINI_SENSE_EVENT_ID; SOFT_GEMINI_SENSE_EVENT_ID;
if (wl->bss_type == BSS_TYPE_AP_BSS)
wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID;
ret = wl1271_event_unmask(wl); ret = wl1271_event_unmask(wl);
if (ret < 0) { if (ret < 0) {
wl1271_error("EVENT mask setting failed"); wl1271_error("EVENT mask setting failed");
...@@ -595,8 +619,7 @@ int wl1271_boot(struct wl1271 *wl) ...@@ -595,8 +619,7 @@ int wl1271_boot(struct wl1271 *wl)
wl1271_boot_enable_interrupts(wl); wl1271_boot_enable_interrupts(wl);
/* set the wl1271 default filters */ /* set the wl1271 default filters */
wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl1271_set_default_filters(wl);
wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
wl1271_event_mbox_config(wl); wl1271_event_mbox_config(wl);
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "wl12xx_80211.h" #include "wl12xx_80211.h"
#include "cmd.h" #include "cmd.h"
#include "event.h" #include "event.h"
#include "tx.h"
#define WL1271_CMD_FAST_POLL_COUNT 50 #define WL1271_CMD_FAST_POLL_COUNT 50
...@@ -221,7 +222,7 @@ int wl1271_cmd_ext_radio_parms(struct wl1271 *wl) ...@@ -221,7 +222,7 @@ int wl1271_cmd_ext_radio_parms(struct wl1271 *wl)
* Poll the mailbox event field until any of the bits in the mask is set or a * Poll the mailbox event field until any of the bits in the mask is set or a
* timeout occurs (WL1271_EVENT_TIMEOUT in msecs) * timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
*/ */
static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask)
{ {
u32 events_vector, event; u32 events_vector, event;
unsigned long timeout; unsigned long timeout;
...@@ -230,7 +231,8 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) ...@@ -230,7 +231,8 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
do { do {
if (time_after(jiffies, timeout)) { if (time_after(jiffies, timeout)) {
ieee80211_queue_work(wl->hw, &wl->recovery_work); wl1271_debug(DEBUG_CMD, "timeout waiting for event %d",
(int)mask);
return -ETIMEDOUT; return -ETIMEDOUT;
} }
...@@ -248,6 +250,19 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) ...@@ -248,6 +250,19 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
return 0; return 0;
} }
static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
{
int ret;
ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask);
if (ret != 0) {
ieee80211_queue_work(wl->hw, &wl->recovery_work);
return ret;
}
return 0;
}
int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
{ {
struct wl1271_cmd_join *join; struct wl1271_cmd_join *join;
...@@ -490,8 +505,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, ...@@ -490,8 +505,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
cmd->len = cpu_to_le16(buf_len); cmd->len = cpu_to_le16(buf_len);
cmd->template_type = template_id; cmd->template_type = template_id;
cmd->enabled_rates = cpu_to_le32(rates); cmd->enabled_rates = cpu_to_le32(rates);
cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit; cmd->short_retry_limit = wl->conf.tx.tmpl_short_retry_limit;
cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit; cmd->long_retry_limit = wl->conf.tx.tmpl_long_retry_limit;
cmd->index = index; cmd->index = index;
if (buf) if (buf)
...@@ -659,15 +674,15 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr) ...@@ -659,15 +674,15 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr)
/* llc layer */ /* llc layer */
memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header)); memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header));
tmpl.llc_type = htons(ETH_P_ARP); tmpl.llc_type = cpu_to_be16(ETH_P_ARP);
/* arp header */ /* arp header */
arp_hdr = &tmpl.arp_hdr; arp_hdr = &tmpl.arp_hdr;
arp_hdr->ar_hrd = htons(ARPHRD_ETHER); arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);
arp_hdr->ar_pro = htons(ETH_P_IP); arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);
arp_hdr->ar_hln = ETH_ALEN; arp_hdr->ar_hln = ETH_ALEN;
arp_hdr->ar_pln = 4; arp_hdr->ar_pln = 4;
arp_hdr->ar_op = htons(ARPOP_REPLY); arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
/* arp payload */ /* arp payload */
memcpy(tmpl.sender_hw, wl->vif->addr, ETH_ALEN); memcpy(tmpl.sender_hw, wl->vif->addr, ETH_ALEN);
...@@ -702,9 +717,9 @@ int wl1271_build_qos_null_data(struct wl1271 *wl) ...@@ -702,9 +717,9 @@ int wl1271_build_qos_null_data(struct wl1271 *wl)
wl->basic_rate); wl->basic_rate);
} }
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id)
{ {
struct wl1271_cmd_set_keys *cmd; struct wl1271_cmd_set_sta_keys *cmd;
int ret = 0; int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id); wl1271_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id);
...@@ -731,11 +746,42 @@ int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) ...@@ -731,11 +746,42 @@ int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
return ret; return ret;
} }
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id)
{
struct wl1271_cmd_set_ap_keys *cmd;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd set_ap_default_wep_key %d", id);
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->hlid = WL1271_AP_BROADCAST_HLID;
cmd->key_id = id;
cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
cmd->key_action = cpu_to_le16(KEY_SET_ID);
cmd->key_type = KEY_WEP;
ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_warning("cmd set_ap_default_wep_key failed: %d", ret);
goto out;
}
out:
kfree(cmd);
return ret;
}
int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, const u8 *addr, u8 key_size, const u8 *key, const u8 *addr,
u32 tx_seq_32, u16 tx_seq_16) u32 tx_seq_32, u16 tx_seq_16)
{ {
struct wl1271_cmd_set_keys *cmd; struct wl1271_cmd_set_sta_keys *cmd;
int ret = 0; int ret = 0;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
...@@ -788,6 +834,67 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, ...@@ -788,6 +834,67 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
return ret; return ret;
} }
int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
u16 tx_seq_16)
{
struct wl1271_cmd_set_ap_keys *cmd;
int ret = 0;
u8 lid_type;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
if (hlid == WL1271_AP_BROADCAST_HLID) {
if (key_type == KEY_WEP)
lid_type = WEP_DEFAULT_LID_TYPE;
else
lid_type = BROADCAST_LID_TYPE;
} else {
lid_type = UNICAST_LID_TYPE;
}
wl1271_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d"
" hlid: %d", (int)action, (int)id, (int)lid_type,
(int)key_type, (int)hlid);
cmd->lid_key_type = lid_type;
cmd->hlid = hlid;
cmd->key_action = cpu_to_le16(action);
cmd->key_size = key_size;
cmd->key_type = key_type;
cmd->key_id = id;
cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16);
cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32);
if (key_type == KEY_TKIP) {
/*
* We get the key in the following form:
* TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
* but the target is expecting:
* TKIP - RX MIC - TX MIC
*/
memcpy(cmd->key, key, 16);
memcpy(cmd->key + 16, key + 24, 8);
memcpy(cmd->key + 24, key + 16, 8);
} else {
memcpy(cmd->key, key, key_size);
}
wl1271_dump(DEBUG_CRYPT, "TARGET AP KEY: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_warning("could not set ap keys");
goto out;
}
out:
kfree(cmd);
return ret;
}
int wl1271_cmd_disconnect(struct wl1271 *wl) int wl1271_cmd_disconnect(struct wl1271 *wl)
{ {
struct wl1271_cmd_disconnect *cmd; struct wl1271_cmd_disconnect *cmd;
...@@ -850,3 +957,180 @@ int wl1271_cmd_set_sta_state(struct wl1271 *wl) ...@@ -850,3 +957,180 @@ int wl1271_cmd_set_sta_state(struct wl1271 *wl)
out: out:
return ret; return ret;
} }
int wl1271_cmd_start_bss(struct wl1271 *wl)
{
struct wl1271_cmd_bss_start *cmd;
struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
int ret;
wl1271_debug(DEBUG_CMD, "cmd start bss");
/*
* FIXME: We currently do not support hidden SSID. The real SSID
* should be fetched from mac80211 first.
*/
if (wl->ssid_len == 0) {
wl1271_warning("Hidden SSID currently not supported for AP");
ret = -EINVAL;
goto out;
}
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN);
cmd->aging_period = cpu_to_le16(WL1271_AP_DEF_INACTIV_SEC);
cmd->bss_index = WL1271_AP_BSS_INDEX;
cmd->global_hlid = WL1271_AP_GLOBAL_HLID;
cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID;
cmd->basic_rate_set = cpu_to_le32(wl->basic_rate_set);
cmd->beacon_interval = cpu_to_le16(wl->beacon_int);
cmd->dtim_interval = bss_conf->dtim_period;
cmd->beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
cmd->channel = wl->channel;
cmd->ssid_len = wl->ssid_len;
cmd->ssid_type = SSID_TYPE_PUBLIC;
memcpy(cmd->ssid, wl->ssid, wl->ssid_len);
switch (wl->band) {
case IEEE80211_BAND_2GHZ:
cmd->band = RADIO_BAND_2_4GHZ;
break;
case IEEE80211_BAND_5GHZ:
cmd->band = RADIO_BAND_5GHZ;
break;
default:
wl1271_warning("bss start - unknown band: %d", (int)wl->band);
cmd->band = RADIO_BAND_2_4GHZ;
break;
}
ret = wl1271_cmd_send(wl, CMD_BSS_START, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to initiate cmd start bss");
goto out_free;
}
out_free:
kfree(cmd);
out:
return ret;
}
int wl1271_cmd_stop_bss(struct wl1271 *wl)
{
struct wl1271_cmd_bss_start *cmd;
int ret;
wl1271_debug(DEBUG_CMD, "cmd stop bss");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->bss_index = WL1271_AP_BSS_INDEX;
ret = wl1271_cmd_send(wl, CMD_BSS_STOP, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to initiate cmd stop bss");
goto out_free;
}
out_free:
kfree(cmd);
out:
return ret;
}
int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
{
struct wl1271_cmd_add_sta *cmd;
int ret;
wl1271_debug(DEBUG_CMD, "cmd add sta %d", (int)hlid);
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
/* currently we don't support UAPSD */
cmd->sp_len = 0;
memcpy(cmd->addr, sta->addr, ETH_ALEN);
cmd->bss_index = WL1271_AP_BSS_INDEX;
cmd->aid = sta->aid;
cmd->hlid = hlid;
/*
* FIXME: Does STA support QOS? We need to propagate this info from
* hostapd. Currently not that important since this is only used for
* sending the correct flavor of null-data packet in response to a
* trigger.
*/
cmd->wmm = 0;
cmd->supported_rates = cpu_to_le32(wl1271_tx_enabled_rates_get(wl,
sta->supp_rates[wl->band]));
wl1271_debug(DEBUG_CMD, "new sta rates: 0x%x", cmd->supported_rates);
ret = wl1271_cmd_send(wl, CMD_ADD_STA, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to initiate cmd add sta");
goto out_free;
}
out_free:
kfree(cmd);
out:
return ret;
}
int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid)
{
struct wl1271_cmd_remove_sta *cmd;
int ret;
wl1271_debug(DEBUG_CMD, "cmd remove sta %d", (int)hlid);
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->hlid = hlid;
/* We never send a deauth, mac80211 is in charge of this */
cmd->reason_opcode = 0;
cmd->send_deauth_flag = 0;
ret = wl1271_cmd_send(wl, CMD_REMOVE_STA, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to initiate cmd remove sta");
goto out_free;
}
/*
* We are ok with a timeout here. The event is sometimes not sent
* due to a firmware bug.
*/
wl1271_cmd_wait_for_event_or_timeout(wl, STA_REMOVE_COMPLETE_EVENT_ID);
out_free:
kfree(cmd);
out:
return ret;
}
...@@ -54,12 +54,20 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, ...@@ -54,12 +54,20 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr); int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr);
int wl1271_build_qos_null_data(struct wl1271 *wl); int wl1271_build_qos_null_data(struct wl1271 *wl);
int wl1271_cmd_build_klv_null_data(struct wl1271 *wl); int wl1271_cmd_build_klv_null_data(struct wl1271 *wl);
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id);
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id);
u8 key_size, const u8 *key, const u8 *addr, int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u32 tx_seq_32, u16 tx_seq_16); u8 key_size, const u8 *key, const u8 *addr,
u32 tx_seq_32, u16 tx_seq_16);
int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
u16 tx_seq_16);
int wl1271_cmd_disconnect(struct wl1271 *wl); int wl1271_cmd_disconnect(struct wl1271 *wl);
int wl1271_cmd_set_sta_state(struct wl1271 *wl); int wl1271_cmd_set_sta_state(struct wl1271 *wl);
int wl1271_cmd_start_bss(struct wl1271 *wl);
int wl1271_cmd_stop_bss(struct wl1271 *wl);
int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid);
int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid);
enum wl1271_commands { enum wl1271_commands {
CMD_INTERROGATE = 1, /*use this to read information elements*/ CMD_INTERROGATE = 1, /*use this to read information elements*/
...@@ -98,6 +106,12 @@ enum wl1271_commands { ...@@ -98,6 +106,12 @@ enum wl1271_commands {
CMD_STOP_PERIODIC_SCAN = 51, CMD_STOP_PERIODIC_SCAN = 51,
CMD_SET_STA_STATE = 52, CMD_SET_STA_STATE = 52,
/* AP mode commands */
CMD_BSS_START = 60,
CMD_BSS_STOP = 61,
CMD_ADD_STA = 62,
CMD_REMOVE_STA = 63,
NUM_COMMANDS, NUM_COMMANDS,
MAX_COMMAND_ID = 0xFFFF, MAX_COMMAND_ID = 0xFFFF,
}; };
...@@ -126,6 +140,13 @@ enum cmd_templ { ...@@ -126,6 +140,13 @@ enum cmd_templ {
* For CTS-to-self (FastCTS) mechanism * For CTS-to-self (FastCTS) mechanism
* for BT/WLAN coexistence (SoftGemini). */ * for BT/WLAN coexistence (SoftGemini). */
CMD_TEMPL_ARP_RSP, CMD_TEMPL_ARP_RSP,
/* AP-mode specific */
CMD_TEMPL_AP_BEACON = 13,
CMD_TEMPL_AP_PROBE_RESPONSE,
CMD_TEMPL_AP_ARP_RSP,
CMD_TEMPL_DEAUTH_AP,
CMD_TEMPL_MAX = 0xff CMD_TEMPL_MAX = 0xff
}; };
...@@ -270,7 +291,6 @@ struct wl1271_cmd_ps_params { ...@@ -270,7 +291,6 @@ struct wl1271_cmd_ps_params {
/* HW encryption keys */ /* HW encryption keys */
#define NUM_ACCESS_CATEGORIES_COPY 4 #define NUM_ACCESS_CATEGORIES_COPY 4
#define MAX_KEY_SIZE 32
enum wl1271_cmd_key_action { enum wl1271_cmd_key_action {
KEY_ADD_OR_REPLACE = 1, KEY_ADD_OR_REPLACE = 1,
...@@ -289,7 +309,7 @@ enum wl1271_cmd_key_type { ...@@ -289,7 +309,7 @@ enum wl1271_cmd_key_type {
/* FIXME: Add description for key-types */ /* FIXME: Add description for key-types */
struct wl1271_cmd_set_keys { struct wl1271_cmd_set_sta_keys {
struct wl1271_cmd_header header; struct wl1271_cmd_header header;
/* Ignored for default WEP key */ /* Ignored for default WEP key */
...@@ -318,6 +338,57 @@ struct wl1271_cmd_set_keys { ...@@ -318,6 +338,57 @@ struct wl1271_cmd_set_keys {
__le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __packed; } __packed;
enum wl1271_cmd_lid_key_type {
UNICAST_LID_TYPE = 0,
BROADCAST_LID_TYPE = 1,
WEP_DEFAULT_LID_TYPE = 2
};
struct wl1271_cmd_set_ap_keys {
struct wl1271_cmd_header header;
/*
* Indicates whether the HLID is a unicast key set
* or broadcast key set. A special value 0xFF is
* used to indicate that the HLID is on WEP-default
* (multi-hlids). of type wl1271_cmd_lid_key_type.
*/
u8 hlid;
/*
* In WEP-default network (hlid == 0xFF) used to
* indicate which network STA/IBSS/AP role should be
* changed
*/
u8 lid_key_type;
/*
* Key ID - For TKIP and AES key types, this field
* indicates the value that should be inserted into
* the KeyID field of frames transmitted using this
* key entry. For broadcast keys the index use as a
* marker for TX/RX key.
* For WEP default network (HLID=0xFF), this field
* indicates the ID of the key to add or remove.
*/
u8 key_id;
u8 reserved_1;
/* key_action_e */
__le16 key_action;
/* key size in bytes */
u8 key_size;
/* key_type_e */
u8 key_type;
/* This field holds the security key data to add to the STA table */
u8 key[MAX_KEY_SIZE];
__le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
__le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __packed;
struct wl1271_cmd_test_header { struct wl1271_cmd_test_header {
u8 id; u8 id;
u8 padding[3]; u8 padding[3];
...@@ -412,4 +483,68 @@ struct wl1271_cmd_set_sta_state { ...@@ -412,4 +483,68 @@ struct wl1271_cmd_set_sta_state {
u8 padding[3]; u8 padding[3];
} __packed; } __packed;
enum wl1271_ssid_type {
SSID_TYPE_PUBLIC = 0,
SSID_TYPE_HIDDEN = 1
};
struct wl1271_cmd_bss_start {
struct wl1271_cmd_header header;
/* wl1271_ssid_type */
u8 ssid_type;
u8 ssid_len;
u8 ssid[IW_ESSID_MAX_SIZE];
u8 padding_1[2];
/* Basic rate set */
__le32 basic_rate_set;
/* Aging period in seconds*/
__le16 aging_period;
/*
* This field specifies the time between target beacon
* transmission times (TBTTs), in time units (TUs).
* Valid values are 1 to 1024.
*/
__le16 beacon_interval;
u8 bssid[ETH_ALEN];
u8 bss_index;
/* Radio band */
u8 band;
u8 channel;
/* The host link id for the AP's global queue */
u8 global_hlid;
/* The host link id for the AP's broadcast queue */
u8 broadcast_hlid;
/* DTIM count */
u8 dtim_interval;
/* Beacon expiry time in ms */
u8 beacon_expiry;
u8 padding_2[3];
} __packed;
struct wl1271_cmd_add_sta {
struct wl1271_cmd_header header;
u8 addr[ETH_ALEN];
u8 hlid;
u8 aid;
u8 psd_type[NUM_ACCESS_CATEGORIES_COPY];
__le32 supported_rates;
u8 bss_index;
u8 sp_len;
u8 wmm;
u8 padding1;
} __packed;
struct wl1271_cmd_remove_sta {
struct wl1271_cmd_header header;
u8 hlid;
u8 reason_opcode;
u8 send_deauth_flag;
u8 padding1;
} __packed;
#endif /* __WL1271_CMD_H__ */ #endif /* __WL1271_CMD_H__ */
...@@ -496,6 +496,26 @@ struct conf_rx_settings { ...@@ -496,6 +496,26 @@ struct conf_rx_settings {
CONF_HW_BIT_RATE_2MBPS) CONF_HW_BIT_RATE_2MBPS)
#define CONF_TX_RATE_RETRY_LIMIT 10 #define CONF_TX_RATE_RETRY_LIMIT 10
/*
* Rates supported for data packets when operating as AP. Note the absense
* of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop
* one. The rate dropped is not mandatory under any operating mode.
*/
#define CONF_TX_AP_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \
CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \
CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \
CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \
CONF_HW_BIT_RATE_18MBPS | CONF_HW_BIT_RATE_24MBPS | \
CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \
CONF_HW_BIT_RATE_54MBPS)
/*
* Default rates for management traffic when operating in AP mode. This
* should be configured according to the basic rate set of the AP
*/
#define CONF_TX_AP_DEFAULT_MGMT_RATES (CONF_HW_BIT_RATE_1MBPS | \
CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS)
struct conf_tx_rate_class { struct conf_tx_rate_class {
/* /*
...@@ -636,9 +656,9 @@ struct conf_tx_settings { ...@@ -636,9 +656,9 @@ struct conf_tx_settings {
/* /*
* Configuration for rate classes for TX (currently only one * Configuration for rate classes for TX (currently only one
* rate class supported.) * rate class supported). Used in non-AP mode.
*/ */
struct conf_tx_rate_class rc_conf; struct conf_tx_rate_class sta_rc_conf;
/* /*
* Configuration for access categories for TX rate control. * Configuration for access categories for TX rate control.
...@@ -646,6 +666,28 @@ struct conf_tx_settings { ...@@ -646,6 +666,28 @@ struct conf_tx_settings {
u8 ac_conf_count; u8 ac_conf_count;
struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT]; struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT];
/*
* Configuration for rate classes in AP-mode. These rate classes
* are for the AC TX queues
*/
struct conf_tx_rate_class ap_rc_conf[CONF_TX_MAX_AC_COUNT];
/*
* Management TX rate class for AP-mode.
*/
struct conf_tx_rate_class ap_mgmt_conf;
/*
* Broadcast TX rate class for AP-mode.
*/
struct conf_tx_rate_class ap_bcst_conf;
/*
* AP-mode - allow this number of TX retries to a station before an
* event is triggered from FW.
*/
u16 ap_max_tx_retries;
/* /*
* Configuration for TID parameters. * Configuration for TID parameters.
*/ */
...@@ -687,6 +729,12 @@ struct conf_tx_settings { ...@@ -687,6 +729,12 @@ struct conf_tx_settings {
* Range: CONF_HW_BIT_RATE_* bit mask * Range: CONF_HW_BIT_RATE_* bit mask
*/ */
u32 basic_rate_5; u32 basic_rate_5;
/*
* TX retry limits for templates
*/
u8 tmpl_short_retry_limit;
u8 tmpl_long_retry_limit;
}; };
enum { enum {
...@@ -1036,30 +1084,30 @@ struct conf_scan_settings { ...@@ -1036,30 +1084,30 @@ struct conf_scan_settings {
/* /*
* The minimum time to wait on each channel for active scans * The minimum time to wait on each channel for active scans
* *
* Range: 0 - 65536 tu * Range: u32 tu/1000
*/ */
u16 min_dwell_time_active; u32 min_dwell_time_active;
/* /*
* The maximum time to wait on each channel for active scans * The maximum time to wait on each channel for active scans
* *
* Range: 0 - 65536 tu * Range: u32 tu/1000
*/ */
u16 max_dwell_time_active; u32 max_dwell_time_active;
/* /*
* The maximum time to wait on each channel for passive scans * The minimum time to wait on each channel for passive scans
* *
* Range: 0 - 65536 tu * Range: u32 tu/1000
*/ */
u16 min_dwell_time_passive; u32 min_dwell_time_passive;
/* /*
* The maximum time to wait on each channel for passive scans * The maximum time to wait on each channel for passive scans
* *
* Range: 0 - 65536 tu * Range: u32 tu/1000
*/ */
u16 max_dwell_time_passive; u32 max_dwell_time_passive;
/* /*
* Number of probe requests to transmit on each active scan channel * Number of probe requests to transmit on each active scan channel
...@@ -1090,6 +1138,11 @@ struct conf_rf_settings { ...@@ -1090,6 +1138,11 @@ struct conf_rf_settings {
u8 tx_per_channel_power_compensation_5[CONF_TX_PWR_COMPENSATION_LEN_5]; u8 tx_per_channel_power_compensation_5[CONF_TX_PWR_COMPENSATION_LEN_5];
}; };
struct conf_ht_setting {
u16 tx_ba_win_size;
u16 inactivity_timeout;
};
struct conf_drv_settings { struct conf_drv_settings {
struct conf_sg_settings sg; struct conf_sg_settings sg;
struct conf_rx_settings rx; struct conf_rx_settings rx;
...@@ -1100,6 +1153,7 @@ struct conf_drv_settings { ...@@ -1100,6 +1153,7 @@ struct conf_drv_settings {
struct conf_roam_trigger_settings roam_trigger; struct conf_roam_trigger_settings roam_trigger;
struct conf_scan_settings scan; struct conf_scan_settings scan;
struct conf_rf_settings rf; struct conf_rf_settings rf;
struct conf_ht_setting ht;
}; };
#endif #endif
...@@ -261,27 +261,25 @@ static ssize_t gpio_power_write(struct file *file, ...@@ -261,27 +261,25 @@ static ssize_t gpio_power_write(struct file *file,
unsigned long value; unsigned long value;
int ret; int ret;
mutex_lock(&wl->mutex);
len = min(count, sizeof(buf) - 1); len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len)) { if (copy_from_user(buf, user_buf, len)) {
ret = -EFAULT; return -EFAULT;
goto out;
} }
buf[len] = '\0'; buf[len] = '\0';
ret = strict_strtoul(buf, 0, &value); ret = strict_strtoul(buf, 0, &value);
if (ret < 0) { if (ret < 0) {
wl1271_warning("illegal value in gpio_power"); wl1271_warning("illegal value in gpio_power");
goto out; return -EINVAL;
} }
mutex_lock(&wl->mutex);
if (value) if (value)
wl1271_power_on(wl); wl1271_power_on(wl);
else else
wl1271_power_off(wl); wl1271_power_off(wl);
out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
return count; return count;
} }
...@@ -293,12 +291,13 @@ static const struct file_operations gpio_power_ops = { ...@@ -293,12 +291,13 @@ static const struct file_operations gpio_power_ops = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
static int wl1271_debugfs_add_files(struct wl1271 *wl) static int wl1271_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{ {
int ret = 0; int ret = 0;
struct dentry *entry, *stats; struct dentry *entry, *stats;
stats = debugfs_create_dir("fw-statistics", wl->rootdir); stats = debugfs_create_dir("fw-statistics", rootdir);
if (!stats || IS_ERR(stats)) { if (!stats || IS_ERR(stats)) {
entry = stats; entry = stats;
goto err; goto err;
...@@ -395,16 +394,11 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl) ...@@ -395,16 +394,11 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl)
DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
DEBUGFS_ADD(tx_queue_len, wl->rootdir); DEBUGFS_ADD(tx_queue_len, rootdir);
DEBUGFS_ADD(retry_count, wl->rootdir); DEBUGFS_ADD(retry_count, rootdir);
DEBUGFS_ADD(excessive_retries, wl->rootdir); DEBUGFS_ADD(excessive_retries, rootdir);
DEBUGFS_ADD(gpio_power, wl->rootdir);
entry = debugfs_create_x32("debug_level", 0600, wl->rootdir, DEBUGFS_ADD(gpio_power, rootdir);
&wl12xx_debug_level);
if (!entry || IS_ERR(entry))
goto err;
return 0; return 0;
...@@ -419,7 +413,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl) ...@@ -419,7 +413,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl)
void wl1271_debugfs_reset(struct wl1271 *wl) void wl1271_debugfs_reset(struct wl1271 *wl)
{ {
if (!wl->rootdir) if (!wl->stats.fw_stats)
return; return;
memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
...@@ -430,13 +424,13 @@ void wl1271_debugfs_reset(struct wl1271 *wl) ...@@ -430,13 +424,13 @@ void wl1271_debugfs_reset(struct wl1271 *wl)
int wl1271_debugfs_init(struct wl1271 *wl) int wl1271_debugfs_init(struct wl1271 *wl)
{ {
int ret; int ret;
struct dentry *rootdir;
wl->rootdir = debugfs_create_dir(KBUILD_MODNAME, rootdir = debugfs_create_dir(KBUILD_MODNAME,
wl->hw->wiphy->debugfsdir); wl->hw->wiphy->debugfsdir);
if (IS_ERR(wl->rootdir)) { if (IS_ERR(rootdir)) {
ret = PTR_ERR(wl->rootdir); ret = PTR_ERR(rootdir);
wl->rootdir = NULL;
goto err; goto err;
} }
...@@ -450,7 +444,7 @@ int wl1271_debugfs_init(struct wl1271 *wl) ...@@ -450,7 +444,7 @@ int wl1271_debugfs_init(struct wl1271 *wl)
wl->stats.fw_stats_update = jiffies; wl->stats.fw_stats_update = jiffies;
ret = wl1271_debugfs_add_files(wl); ret = wl1271_debugfs_add_files(wl, rootdir);
if (ret < 0) if (ret < 0)
goto err_file; goto err_file;
...@@ -462,8 +456,7 @@ int wl1271_debugfs_init(struct wl1271 *wl) ...@@ -462,8 +456,7 @@ int wl1271_debugfs_init(struct wl1271 *wl)
wl->stats.fw_stats = NULL; wl->stats.fw_stats = NULL;
err_fw: err_fw:
debugfs_remove_recursive(wl->rootdir); debugfs_remove_recursive(rootdir);
wl->rootdir = NULL;
err: err:
return ret; return ret;
...@@ -473,8 +466,4 @@ void wl1271_debugfs_exit(struct wl1271 *wl) ...@@ -473,8 +466,4 @@ void wl1271_debugfs_exit(struct wl1271 *wl)
{ {
kfree(wl->stats.fw_stats); kfree(wl->stats.fw_stats);
wl->stats.fw_stats = NULL; wl->stats.fw_stats = NULL;
debugfs_remove_recursive(wl->rootdir);
wl->rootdir = NULL;
} }
...@@ -186,6 +186,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) ...@@ -186,6 +186,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
int ret; int ret;
u32 vector; u32 vector;
bool beacon_loss = false; bool beacon_loss = false;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
wl1271_event_mbox_dump(mbox); wl1271_event_mbox_dump(mbox);
...@@ -218,21 +219,21 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) ...@@ -218,21 +219,21 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
* BSS_LOSE_EVENT, beacon loss has to be reported to the stack. * BSS_LOSE_EVENT, beacon loss has to be reported to the stack.
* *
*/ */
if (vector & BSS_LOSE_EVENT_ID) { if ((vector & BSS_LOSE_EVENT_ID) && !is_ap) {
wl1271_info("Beacon loss detected."); wl1271_info("Beacon loss detected.");
/* indicate to the stack, that beacons have been lost */ /* indicate to the stack, that beacons have been lost */
beacon_loss = true; beacon_loss = true;
} }
if (vector & PS_REPORT_EVENT_ID) { if ((vector & PS_REPORT_EVENT_ID) && !is_ap) {
wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
ret = wl1271_event_ps_report(wl, mbox, &beacon_loss); ret = wl1271_event_ps_report(wl, mbox, &beacon_loss);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) if ((vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) && !is_ap)
wl1271_event_pspoll_delivery_fail(wl); wl1271_event_pspoll_delivery_fail(wl);
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
......
...@@ -59,6 +59,7 @@ enum { ...@@ -59,6 +59,7 @@ enum {
BSS_LOSE_EVENT_ID = BIT(18), BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19), REGAINED_BSS_EVENT_ID = BIT(19),
ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20), ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20),
STA_REMOVE_COMPLETE_EVENT_ID = BIT(21), /* AP */
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23), SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23),
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
...@@ -115,7 +116,12 @@ struct event_mailbox { ...@@ -115,7 +116,12 @@ struct event_mailbox {
u8 scheduled_scan_status; u8 scheduled_scan_status;
u8 ps_status; u8 ps_status;
u8 reserved_5[29]; /* AP FW only */
u8 hlid_removed;
__le16 sta_aging_status;
__le16 sta_tx_retry_exceeded;
u8 reserved_5[24];
} __packed; } __packed;
int wl1271_event_unmask(struct wl1271 *wl); int wl1271_event_unmask(struct wl1271 *wl);
......
...@@ -30,27 +30,9 @@ ...@@ -30,27 +30,9 @@
#include "acx.h" #include "acx.h"
#include "cmd.h" #include "cmd.h"
#include "reg.h" #include "reg.h"
#include "tx.h"
static int wl1271_init_hwenc_config(struct wl1271 *wl) int wl1271_sta_init_templates_config(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_feature_cfg(wl);
if (ret < 0) {
wl1271_warning("couldn't set feature config");
return ret;
}
ret = wl1271_cmd_set_default_wep_key(wl, wl->default_key);
if (ret < 0) {
wl1271_warning("couldn't set default key");
return ret;
}
return 0;
}
int wl1271_init_templates_config(struct wl1271 *wl)
{ {
int ret, i; int ret, i;
...@@ -118,6 +100,132 @@ int wl1271_init_templates_config(struct wl1271 *wl) ...@@ -118,6 +100,132 @@ int wl1271_init_templates_config(struct wl1271 *wl)
return 0; return 0;
} }
static int wl1271_ap_init_deauth_template(struct wl1271 *wl)
{
struct wl12xx_disconn_template *tmpl;
int ret;
tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL);
if (!tmpl) {
ret = -ENOMEM;
goto out;
}
tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_DEAUTH);
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP,
tmpl, sizeof(*tmpl), 0,
wl1271_tx_min_rate_get(wl));
out:
kfree(tmpl);
return ret;
}
static int wl1271_ap_init_null_template(struct wl1271 *wl)
{
struct ieee80211_hdr_3addr *nullfunc;
int ret;
nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL);
if (!nullfunc) {
ret = -ENOMEM;
goto out;
}
nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_NULLFUNC |
IEEE80211_FCTL_FROMDS);
/* nullfunc->addr1 is filled by FW */
memcpy(nullfunc->addr2, wl->mac_addr, ETH_ALEN);
memcpy(nullfunc->addr3, wl->mac_addr, ETH_ALEN);
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc,
sizeof(*nullfunc), 0,
wl1271_tx_min_rate_get(wl));
out:
kfree(nullfunc);
return ret;
}
static int wl1271_ap_init_qos_null_template(struct wl1271 *wl)
{
struct ieee80211_qos_hdr *qosnull;
int ret;
qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL);
if (!qosnull) {
ret = -ENOMEM;
goto out;
}
qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_QOS_NULLFUNC |
IEEE80211_FCTL_FROMDS);
/* qosnull->addr1 is filled by FW */
memcpy(qosnull->addr2, wl->mac_addr, ETH_ALEN);
memcpy(qosnull->addr3, wl->mac_addr, ETH_ALEN);
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull,
sizeof(*qosnull), 0,
wl1271_tx_min_rate_get(wl));
out:
kfree(qosnull);
return ret;
}
static int wl1271_ap_init_templates_config(struct wl1271 *wl)
{
int ret;
/*
* Put very large empty placeholders for all templates. These
* reserve memory for later.
*/
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
sizeof
(struct wl12xx_probe_resp_template),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL,
sizeof
(struct wl12xx_beacon_template),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL,
sizeof
(struct wl12xx_disconn_template),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
sizeof(struct wl12xx_null_data_template),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL,
sizeof
(struct wl12xx_qos_null_data_template),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter) static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter)
{ {
int ret; int ret;
...@@ -145,10 +253,6 @@ int wl1271_init_phy_config(struct wl1271 *wl) ...@@ -145,10 +253,6 @@ int wl1271_init_phy_config(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
if (ret < 0)
return ret;
ret = wl1271_acx_service_period_timeout(wl); ret = wl1271_acx_service_period_timeout(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -213,11 +317,186 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl) ...@@ -213,11 +317,186 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
return 0; return 0;
} }
static int wl1271_sta_hw_init(struct wl1271 *wl)
{
int ret;
ret = wl1271_cmd_ext_radio_parms(wl);
if (ret < 0)
return ret;
ret = wl1271_sta_init_templates_config(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
if (ret < 0)
return ret;
/* Initialize connection monitoring thresholds */
ret = wl1271_acx_conn_monit_params(wl, false);
if (ret < 0)
return ret;
/* Beacon filtering */
ret = wl1271_init_beacon_filter(wl);
if (ret < 0)
return ret;
/* Bluetooth WLAN coexistence */
ret = wl1271_init_pta(wl);
if (ret < 0)
return ret;
/* Beacons and broadcast settings */
ret = wl1271_init_beacon_broadcast(wl);
if (ret < 0)
return ret;
/* Configure for ELP power saving */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
if (ret < 0)
return ret;
/* Configure rssi/snr averaging weights */
ret = wl1271_acx_rssi_snr_avg_weights(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl)
{
int ret, i;
ret = wl1271_cmd_set_sta_default_wep_key(wl, wl->default_key);
if (ret < 0) {
wl1271_warning("couldn't set default key");
return ret;
}
/* disable all keep-alive templates */
for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
ret = wl1271_acx_keep_alive_config(wl, i,
ACX_KEEP_ALIVE_TPL_INVALID);
if (ret < 0)
return ret;
}
/* disable the keep-alive feature */
ret = wl1271_acx_keep_alive_mode(wl, false);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_ap_hw_init(struct wl1271 *wl)
{
int ret, i;
ret = wl1271_ap_init_templates_config(wl);
if (ret < 0)
return ret;
/* Configure for power always on */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
if (ret < 0)
return ret;
/* Configure initial TX rate classes */
for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
ret = wl1271_acx_ap_rate_policy(wl,
&wl->conf.tx.ap_rc_conf[i], i);
if (ret < 0)
return ret;
}
ret = wl1271_acx_ap_rate_policy(wl,
&wl->conf.tx.ap_mgmt_conf,
ACX_TX_AP_MODE_MGMT_RATE);
if (ret < 0)
return ret;
ret = wl1271_acx_ap_rate_policy(wl,
&wl->conf.tx.ap_bcst_conf,
ACX_TX_AP_MODE_BCST_RATE);
if (ret < 0)
return ret;
ret = wl1271_acx_max_tx_retry(wl);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl)
{
int ret;
ret = wl1271_ap_init_deauth_template(wl);
if (ret < 0)
return ret;
ret = wl1271_ap_init_null_template(wl);
if (ret < 0)
return ret;
ret = wl1271_ap_init_qos_null_template(wl);
if (ret < 0)
return ret;
return 0;
}
static void wl1271_check_ba_support(struct wl1271 *wl)
{
/* validate FW cose ver x.x.x.50-60.x */
if ((wl->chip.fw_ver[3] >= WL12XX_BA_SUPPORT_FW_COST_VER2_START) &&
(wl->chip.fw_ver[3] < WL12XX_BA_SUPPORT_FW_COST_VER2_END)) {
wl->ba_support = true;
return;
}
wl->ba_support = false;
}
static int wl1271_set_ba_policies(struct wl1271 *wl)
{
u8 tid_index;
u8 ret = 0;
/* Reset the BA RX indicators */
wl->ba_rx_bitmap = 0;
/* validate that FW support BA */
wl1271_check_ba_support(wl);
if (wl->ba_support)
/* 802.11n initiator BA session setting */
for (tid_index = 0; tid_index < CONF_TX_MAX_TID_COUNT;
++tid_index) {
ret = wl1271_acx_set_ba_session(wl, WLAN_BACK_INITIATOR,
tid_index, true);
if (ret < 0)
break;
}
return ret;
}
int wl1271_hw_init(struct wl1271 *wl) int wl1271_hw_init(struct wl1271 *wl)
{ {
struct conf_tx_ac_category *conf_ac; struct conf_tx_ac_category *conf_ac;
struct conf_tx_tid *conf_tid; struct conf_tx_tid *conf_tid;
int ret, i; int ret, i;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
ret = wl1271_cmd_general_parms(wl); ret = wl1271_cmd_general_parms(wl);
if (ret < 0) if (ret < 0)
...@@ -227,12 +506,12 @@ int wl1271_hw_init(struct wl1271 *wl) ...@@ -227,12 +506,12 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_cmd_ext_radio_parms(wl); /* Mode specific init */
if (ret < 0) if (is_ap)
return ret; ret = wl1271_ap_hw_init(wl);
else
ret = wl1271_sta_hw_init(wl);
/* Template settings */
ret = wl1271_init_templates_config(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -259,16 +538,6 @@ int wl1271_hw_init(struct wl1271 *wl) ...@@ -259,16 +538,6 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
goto out_free_memmap; goto out_free_memmap;
/* Initialize connection monitoring thresholds */
ret = wl1271_acx_conn_monit_params(wl, false);
if (ret < 0)
goto out_free_memmap;
/* Beacon filtering */
ret = wl1271_init_beacon_filter(wl);
if (ret < 0)
goto out_free_memmap;
/* Configure TX patch complete interrupt behavior */ /* Configure TX patch complete interrupt behavior */
ret = wl1271_acx_tx_config_options(wl); ret = wl1271_acx_tx_config_options(wl);
if (ret < 0) if (ret < 0)
...@@ -279,21 +548,11 @@ int wl1271_hw_init(struct wl1271 *wl) ...@@ -279,21 +548,11 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
goto out_free_memmap; goto out_free_memmap;
/* Bluetooth WLAN coexistence */
ret = wl1271_init_pta(wl);
if (ret < 0)
goto out_free_memmap;
/* Energy detection */ /* Energy detection */
ret = wl1271_init_energy_detection(wl); ret = wl1271_init_energy_detection(wl);
if (ret < 0) if (ret < 0)
goto out_free_memmap; goto out_free_memmap;
/* Beacons and boradcast settings */
ret = wl1271_init_beacon_broadcast(wl);
if (ret < 0)
goto out_free_memmap;
/* Default fragmentation threshold */ /* Default fragmentation threshold */
ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold); ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
if (ret < 0) if (ret < 0)
...@@ -321,23 +580,13 @@ int wl1271_hw_init(struct wl1271 *wl) ...@@ -321,23 +580,13 @@ int wl1271_hw_init(struct wl1271 *wl)
goto out_free_memmap; goto out_free_memmap;
} }
/* Configure TX rate classes */
ret = wl1271_acx_rate_policies(wl);
if (ret < 0)
goto out_free_memmap;
/* Enable data path */ /* Enable data path */
ret = wl1271_cmd_data_path(wl, 1); ret = wl1271_cmd_data_path(wl, 1);
if (ret < 0) if (ret < 0)
goto out_free_memmap; goto out_free_memmap;
/* Configure for ELP power saving */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
if (ret < 0)
goto out_free_memmap;
/* Configure HW encryption */ /* Configure HW encryption */
ret = wl1271_init_hwenc_config(wl); ret = wl1271_acx_feature_cfg(wl);
if (ret < 0) if (ret < 0)
goto out_free_memmap; goto out_free_memmap;
...@@ -346,21 +595,17 @@ int wl1271_hw_init(struct wl1271 *wl) ...@@ -346,21 +595,17 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
goto out_free_memmap; goto out_free_memmap;
/* disable all keep-alive templates */ /* Mode specific init - post mem init */
for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { if (is_ap)
ret = wl1271_acx_keep_alive_config(wl, i, ret = wl1271_ap_hw_init_post_mem(wl);
ACX_KEEP_ALIVE_TPL_INVALID); else
if (ret < 0) ret = wl1271_sta_hw_init_post_mem(wl);
goto out_free_memmap;
}
/* disable the keep-alive feature */
ret = wl1271_acx_keep_alive_mode(wl, false);
if (ret < 0) if (ret < 0)
goto out_free_memmap; goto out_free_memmap;
/* Configure rssi/snr averaging weights */ /* Configure initiator BA sessions policies */
ret = wl1271_acx_rssi_snr_avg_weights(wl); ret = wl1271_set_ba_policies(wl);
if (ret < 0) if (ret < 0)
goto out_free_memmap; goto out_free_memmap;
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
#include "wl12xx.h" #include "wl12xx.h"
int wl1271_hw_init_power_auth(struct wl1271 *wl); int wl1271_hw_init_power_auth(struct wl1271 *wl);
int wl1271_init_templates_config(struct wl1271 *wl); int wl1271_sta_init_templates_config(struct wl1271 *wl);
int wl1271_init_phy_config(struct wl1271 *wl); int wl1271_init_phy_config(struct wl1271 *wl);
int wl1271_init_pta(struct wl1271 *wl); int wl1271_init_pta(struct wl1271 *wl);
int wl1271_init_energy_detection(struct wl1271 *wl); int wl1271_init_energy_detection(struct wl1271 *wl);
......
...@@ -116,11 +116,11 @@ static struct conf_drv_settings default_conf = { ...@@ -116,11 +116,11 @@ static struct conf_drv_settings default_conf = {
}, },
.tx = { .tx = {
.tx_energy_detection = 0, .tx_energy_detection = 0,
.rc_conf = { .sta_rc_conf = {
.enabled_rates = 0, .enabled_rates = 0,
.short_retry_limit = 10, .short_retry_limit = 10,
.long_retry_limit = 10, .long_retry_limit = 10,
.aflags = 0 .aflags = 0,
}, },
.ac_conf_count = 4, .ac_conf_count = 4,
.ac_conf = { .ac_conf = {
...@@ -153,6 +153,45 @@ static struct conf_drv_settings default_conf = { ...@@ -153,6 +153,45 @@ static struct conf_drv_settings default_conf = {
.tx_op_limit = 1504, .tx_op_limit = 1504,
}, },
}, },
.ap_rc_conf = {
[0] = {
.enabled_rates = CONF_TX_AP_ENABLED_RATES,
.short_retry_limit = 10,
.long_retry_limit = 10,
.aflags = 0,
},
[1] = {
.enabled_rates = CONF_TX_AP_ENABLED_RATES,
.short_retry_limit = 10,
.long_retry_limit = 10,
.aflags = 0,
},
[2] = {
.enabled_rates = CONF_TX_AP_ENABLED_RATES,
.short_retry_limit = 10,
.long_retry_limit = 10,
.aflags = 0,
},
[3] = {
.enabled_rates = CONF_TX_AP_ENABLED_RATES,
.short_retry_limit = 10,
.long_retry_limit = 10,
.aflags = 0,
},
},
.ap_mgmt_conf = {
.enabled_rates = CONF_TX_AP_DEFAULT_MGMT_RATES,
.short_retry_limit = 10,
.long_retry_limit = 10,
.aflags = 0,
},
.ap_bcst_conf = {
.enabled_rates = CONF_HW_BIT_RATE_1MBPS,
.short_retry_limit = 10,
.long_retry_limit = 10,
.aflags = 0,
},
.ap_max_tx_retries = 100,
.tid_conf_count = 4, .tid_conf_count = 4,
.tid_conf = { .tid_conf = {
[CONF_TX_AC_BE] = { [CONF_TX_AC_BE] = {
...@@ -193,6 +232,8 @@ static struct conf_drv_settings default_conf = { ...@@ -193,6 +232,8 @@ static struct conf_drv_settings default_conf = {
.tx_compl_threshold = 4, .tx_compl_threshold = 4,
.basic_rate = CONF_HW_BIT_RATE_1MBPS, .basic_rate = CONF_HW_BIT_RATE_1MBPS,
.basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
.tmpl_short_retry_limit = 10,
.tmpl_long_retry_limit = 10,
}, },
.conn = { .conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
...@@ -233,13 +274,13 @@ static struct conf_drv_settings default_conf = { ...@@ -233,13 +274,13 @@ static struct conf_drv_settings default_conf = {
.avg_weight_rssi_beacon = 20, .avg_weight_rssi_beacon = 20,
.avg_weight_rssi_data = 10, .avg_weight_rssi_data = 10,
.avg_weight_snr_beacon = 20, .avg_weight_snr_beacon = 20,
.avg_weight_snr_data = 10 .avg_weight_snr_data = 10,
}, },
.scan = { .scan = {
.min_dwell_time_active = 7500, .min_dwell_time_active = 7500,
.max_dwell_time_active = 30000, .max_dwell_time_active = 30000,
.min_dwell_time_passive = 30000, .min_dwell_time_passive = 100000,
.max_dwell_time_passive = 60000, .max_dwell_time_passive = 100000,
.num_probe_reqs = 2, .num_probe_reqs = 2,
}, },
.rf = { .rf = {
...@@ -252,9 +293,14 @@ static struct conf_drv_settings default_conf = { ...@@ -252,9 +293,14 @@ static struct conf_drv_settings default_conf = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}, },
}, },
.ht = {
.tx_ba_win_size = 64,
.inactivity_timeout = 10000,
},
}; };
static void __wl1271_op_remove_interface(struct wl1271 *wl); static void __wl1271_op_remove_interface(struct wl1271 *wl);
static void wl1271_free_ap_keys(struct wl1271 *wl);
static void wl1271_device_release(struct device *dev) static void wl1271_device_release(struct device *dev)
...@@ -393,7 +439,7 @@ static int wl1271_plt_init(struct wl1271 *wl) ...@@ -393,7 +439,7 @@ static int wl1271_plt_init(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_init_templates_config(wl); ret = wl1271_sta_init_templates_config(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -616,9 +662,26 @@ static void wl1271_irq_work(struct work_struct *work) ...@@ -616,9 +662,26 @@ static void wl1271_irq_work(struct work_struct *work)
static int wl1271_fetch_firmware(struct wl1271 *wl) static int wl1271_fetch_firmware(struct wl1271 *wl)
{ {
const struct firmware *fw; const struct firmware *fw;
const char *fw_name;
int ret; int ret;
ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl)); switch (wl->bss_type) {
case BSS_TYPE_AP_BSS:
fw_name = WL1271_AP_FW_NAME;
break;
case BSS_TYPE_IBSS:
case BSS_TYPE_STA_BSS:
fw_name = WL1271_FW_NAME;
break;
default:
wl1271_error("no compatible firmware for bss_type %d",
wl->bss_type);
return -EINVAL;
}
wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
ret = request_firmware(&fw, fw_name, wl1271_wl_to_dev(wl));
if (ret < 0) { if (ret < 0) {
wl1271_error("could not get firmware: %d", ret); wl1271_error("could not get firmware: %d", ret);
...@@ -632,6 +695,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) ...@@ -632,6 +695,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
goto out; goto out;
} }
vfree(wl->fw);
wl->fw_len = fw->size; wl->fw_len = fw->size;
wl->fw = vmalloc(wl->fw_len); wl->fw = vmalloc(wl->fw_len);
...@@ -642,7 +706,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) ...@@ -642,7 +706,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
} }
memcpy(wl->fw, fw->data, wl->fw_len); memcpy(wl->fw, fw->data, wl->fw_len);
wl->fw_bss_type = wl->bss_type;
ret = 0; ret = 0;
out: out:
...@@ -778,7 +842,8 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ...@@ -778,7 +842,8 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
goto out; goto out;
} }
if (wl->fw == NULL) { /* Make sure the firmware type matches the BSS type */
if (wl->fw == NULL || wl->fw_bss_type != wl->bss_type) {
ret = wl1271_fetch_firmware(wl); ret = wl1271_fetch_firmware(wl);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -811,6 +876,8 @@ int wl1271_plt_start(struct wl1271 *wl) ...@@ -811,6 +876,8 @@ int wl1271_plt_start(struct wl1271 *wl)
goto out; goto out;
} }
wl->bss_type = BSS_TYPE_STA_BSS;
while (retries) { while (retries) {
retries--; retries--;
ret = wl1271_chip_wakeup(wl); ret = wl1271_chip_wakeup(wl);
...@@ -827,7 +894,7 @@ int wl1271_plt_start(struct wl1271 *wl) ...@@ -827,7 +894,7 @@ int wl1271_plt_start(struct wl1271 *wl)
wl->state = WL1271_STATE_PLT; wl->state = WL1271_STATE_PLT;
wl1271_notice("firmware booted in PLT mode (%s)", wl1271_notice("firmware booted in PLT mode (%s)",
wl->chip.fw_ver); wl->chip.fw_ver_str);
goto out; goto out;
irq_disable: irq_disable:
...@@ -854,12 +921,10 @@ int wl1271_plt_start(struct wl1271 *wl) ...@@ -854,12 +921,10 @@ int wl1271_plt_start(struct wl1271 *wl)
return ret; return ret;
} }
int wl1271_plt_stop(struct wl1271 *wl) int __wl1271_plt_stop(struct wl1271 *wl)
{ {
int ret = 0; int ret = 0;
mutex_lock(&wl->mutex);
wl1271_notice("power down"); wl1271_notice("power down");
if (wl->state != WL1271_STATE_PLT) { if (wl->state != WL1271_STATE_PLT) {
...@@ -875,12 +940,21 @@ int wl1271_plt_stop(struct wl1271 *wl) ...@@ -875,12 +940,21 @@ int wl1271_plt_stop(struct wl1271 *wl)
wl->state = WL1271_STATE_OFF; wl->state = WL1271_STATE_OFF;
wl->rx_counter = 0; wl->rx_counter = 0;
out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->irq_work);
cancel_work_sync(&wl->recovery_work); cancel_work_sync(&wl->recovery_work);
mutex_lock(&wl->mutex);
out:
return ret;
}
int wl1271_plt_stop(struct wl1271 *wl)
{
int ret;
mutex_lock(&wl->mutex);
ret = __wl1271_plt_stop(wl);
mutex_unlock(&wl->mutex);
return ret; return ret;
} }
...@@ -902,7 +976,8 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -902,7 +976,8 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
spin_lock_irqsave(&wl->wl_lock, flags); spin_lock_irqsave(&wl->wl_lock, flags);
if (sta && if (sta &&
(sta->supp_rates[conf->channel->band] != (sta->supp_rates[conf->channel->band] !=
(wl->sta_rate_set & HW_BG_RATES_MASK))) { (wl->sta_rate_set & HW_BG_RATES_MASK)) &&
wl->bss_type != BSS_TYPE_AP_BSS) {
wl->sta_rate_set = sta->supp_rates[conf->channel->band]; wl->sta_rate_set = sta->supp_rates[conf->channel->band];
set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags); set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
} }
...@@ -967,6 +1042,9 @@ static int wl1271_op_start(struct ieee80211_hw *hw) ...@@ -967,6 +1042,9 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
* *
* The MAC address is first known when the corresponding interface * The MAC address is first known when the corresponding interface
* is added. That is where we will initialize the hardware. * is added. That is where we will initialize the hardware.
*
* In addition, we currently have different firmwares for AP and managed
* operation. We will know which to boot according to interface type.
*/ */
return 0; return 0;
...@@ -1006,6 +1084,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -1006,6 +1084,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
wl->bss_type = BSS_TYPE_IBSS; wl->bss_type = BSS_TYPE_IBSS;
wl->set_bss_type = BSS_TYPE_STA_BSS; wl->set_bss_type = BSS_TYPE_STA_BSS;
break; break;
case NL80211_IFTYPE_AP:
wl->bss_type = BSS_TYPE_AP_BSS;
break;
default: default:
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
goto out; goto out;
...@@ -1061,11 +1142,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -1061,11 +1142,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
wl->vif = vif; wl->vif = vif;
wl->state = WL1271_STATE_ON; wl->state = WL1271_STATE_ON;
wl1271_info("firmware booted (%s)", wl->chip.fw_ver); wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
/* update hw/fw version info in wiphy struct */ /* update hw/fw version info in wiphy struct */
wiphy->hw_version = wl->chip.id; wiphy->hw_version = wl->chip.id;
strncpy(wiphy->fw_version, wl->chip.fw_ver, strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
sizeof(wiphy->fw_version)); sizeof(wiphy->fw_version));
/* /*
...@@ -1151,6 +1232,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) ...@@ -1151,6 +1232,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl->flags = 0; wl->flags = 0;
wl->vif = NULL; wl->vif = NULL;
wl->filters = 0; wl->filters = 0;
wl1271_free_ap_keys(wl);
memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
for (i = 0; i < NUM_TX_QUEUES; i++) for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_blocks_freed[i] = 0; wl->tx_blocks_freed[i] = 0;
...@@ -1186,8 +1269,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, ...@@ -1186,8 +1269,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
{ {
wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl1271_set_default_filters(wl);
wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
/* combine requested filters with current filter config */ /* combine requested filters with current filter config */
filters = wl->filters | filters; filters = wl->filters | filters;
...@@ -1322,25 +1404,7 @@ static void wl1271_set_band_rate(struct wl1271 *wl) ...@@ -1322,25 +1404,7 @@ static void wl1271_set_band_rate(struct wl1271 *wl)
wl->basic_rate_set = wl->conf.tx.basic_rate_5; wl->basic_rate_set = wl->conf.tx.basic_rate_5;
} }
static u32 wl1271_min_rate_get(struct wl1271 *wl) static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
{
int i;
u32 rate = 0;
if (!wl->basic_rate_set) {
WARN_ON(1);
wl->basic_rate_set = wl->conf.tx.basic_rate;
}
for (i = 0; !rate; i++) {
if ((wl->basic_rate_set >> i) & 0x1)
rate = 1 << i;
}
return rate;
}
static int wl1271_handle_idle(struct wl1271 *wl, bool idle)
{ {
int ret; int ret;
...@@ -1350,9 +1414,9 @@ static int wl1271_handle_idle(struct wl1271 *wl, bool idle) ...@@ -1350,9 +1414,9 @@ static int wl1271_handle_idle(struct wl1271 *wl, bool idle)
if (ret < 0) if (ret < 0)
goto out; goto out;
} }
wl->rate_set = wl1271_min_rate_get(wl); wl->rate_set = wl1271_tx_min_rate_get(wl);
wl->sta_rate_set = 0; wl->sta_rate_set = 0;
ret = wl1271_acx_rate_policies(wl); ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = wl1271_acx_keep_alive_config( ret = wl1271_acx_keep_alive_config(
...@@ -1381,14 +1445,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1381,14 +1445,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
struct ieee80211_conf *conf = &hw->conf; struct ieee80211_conf *conf = &hw->conf;
int channel, ret = 0; int channel, ret = 0;
bool is_ap;
channel = ieee80211_frequency_to_channel(conf->channel->center_freq); channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s", wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
" changed 0x%x",
channel, channel,
conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->flags & IEEE80211_CONF_PS ? "on" : "off",
conf->power_level, conf->power_level,
conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use"); conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
changed);
/* /*
* mac80211 will go to idle nearly immediately after transmitting some * mac80211 will go to idle nearly immediately after transmitting some
...@@ -1406,6 +1473,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1406,6 +1473,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
goto out; goto out;
} }
is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
ret = wl1271_ps_elp_wakeup(wl, false); ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1417,31 +1486,34 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1417,31 +1486,34 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
wl->band = conf->channel->band; wl->band = conf->channel->band;
wl->channel = channel; wl->channel = channel;
/* if (!is_ap) {
* FIXME: the mac80211 should really provide a fixed rate /*
* to use here. for now, just use the smallest possible rate * FIXME: the mac80211 should really provide a fixed
* for the band as a fixed rate for association frames and * rate to use here. for now, just use the smallest
* other control messages. * possible rate for the band as a fixed rate for
*/ * association frames and other control messages.
if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) */
wl1271_set_band_rate(wl); if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
wl1271_set_band_rate(wl);
wl->basic_rate = wl1271_min_rate_get(wl);
ret = wl1271_acx_rate_policies(wl);
if (ret < 0)
wl1271_warning("rate policy for update channel "
"failed %d", ret);
if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) { wl->basic_rate = wl1271_tx_min_rate_get(wl);
ret = wl1271_join(wl, false); ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0) if (ret < 0)
wl1271_warning("cmd join to update channel " wl1271_warning("rate policy for channel "
"failed %d", ret); "failed %d", ret);
if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
ret = wl1271_join(wl, false);
if (ret < 0)
wl1271_warning("cmd join on channel "
"failed %d", ret);
}
} }
} }
if (changed & IEEE80211_CONF_CHANGE_IDLE) { if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) {
ret = wl1271_handle_idle(wl, conf->flags & IEEE80211_CONF_IDLE); ret = wl1271_sta_handle_idle(wl,
conf->flags & IEEE80211_CONF_IDLE);
if (ret < 0) if (ret < 0)
wl1271_warning("idle mode change failed %d", ret); wl1271_warning("idle mode change failed %d", ret);
} }
...@@ -1548,7 +1620,8 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, ...@@ -1548,7 +1620,8 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
int ret; int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter"); wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
" total %x", changed, *total);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
...@@ -1562,15 +1635,16 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, ...@@ -1562,15 +1635,16 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
if (ret < 0) if (ret < 0)
goto out; goto out;
if (wl->bss_type != BSS_TYPE_AP_BSS) {
if (*total & FIF_ALLMULTI) if (*total & FIF_ALLMULTI)
ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0); ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
else if (fp) else if (fp)
ret = wl1271_acx_group_address_tbl(wl, fp->enabled, ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
fp->mc_list, fp->mc_list,
fp->mc_list_length); fp->mc_list_length);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out_sleep;
}
/* determine, whether supported filter values have changed */ /* determine, whether supported filter values have changed */
if (changed == 0) if (changed == 0)
...@@ -1593,38 +1667,192 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, ...@@ -1593,38 +1667,192 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
kfree(fp); kfree(fp);
} }
static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
u16 tx_seq_16)
{
struct wl1271_ap_key *ap_key;
int i;
wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
if (key_size > MAX_KEY_SIZE)
return -EINVAL;
/*
* Find next free entry in ap_keys. Also check we are not replacing
* an existing key.
*/
for (i = 0; i < MAX_NUM_KEYS; i++) {
if (wl->recorded_ap_keys[i] == NULL)
break;
if (wl->recorded_ap_keys[i]->id == id) {
wl1271_warning("trying to record key replacement");
return -EINVAL;
}
}
if (i == MAX_NUM_KEYS)
return -EBUSY;
ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
if (!ap_key)
return -ENOMEM;
ap_key->id = id;
ap_key->key_type = key_type;
ap_key->key_size = key_size;
memcpy(ap_key->key, key, key_size);
ap_key->hlid = hlid;
ap_key->tx_seq_32 = tx_seq_32;
ap_key->tx_seq_16 = tx_seq_16;
wl->recorded_ap_keys[i] = ap_key;
return 0;
}
static void wl1271_free_ap_keys(struct wl1271 *wl)
{
int i;
for (i = 0; i < MAX_NUM_KEYS; i++) {
kfree(wl->recorded_ap_keys[i]);
wl->recorded_ap_keys[i] = NULL;
}
}
static int wl1271_ap_init_hwenc(struct wl1271 *wl)
{
int i, ret = 0;
struct wl1271_ap_key *key;
bool wep_key_added = false;
for (i = 0; i < MAX_NUM_KEYS; i++) {
if (wl->recorded_ap_keys[i] == NULL)
break;
key = wl->recorded_ap_keys[i];
ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
key->id, key->key_type,
key->key_size, key->key,
key->hlid, key->tx_seq_32,
key->tx_seq_16);
if (ret < 0)
goto out;
if (key->key_type == KEY_WEP)
wep_key_added = true;
}
if (wep_key_added) {
ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key);
if (ret < 0)
goto out;
}
out:
wl1271_free_ap_keys(wl);
return ret;
}
static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, u32 tx_seq_32,
u16 tx_seq_16, struct ieee80211_sta *sta)
{
int ret;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
if (is_ap) {
struct wl1271_station *wl_sta;
u8 hlid;
if (sta) {
wl_sta = (struct wl1271_station *)sta->drv_priv;
hlid = wl_sta->hlid;
} else {
hlid = WL1271_AP_BROADCAST_HLID;
}
if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
/*
* We do not support removing keys after AP shutdown.
* Pretend we do to make mac80211 happy.
*/
if (action != KEY_ADD_OR_REPLACE)
return 0;
ret = wl1271_record_ap_key(wl, id,
key_type, key_size,
key, hlid, tx_seq_32,
tx_seq_16);
} else {
ret = wl1271_cmd_set_ap_key(wl, action,
id, key_type, key_size,
key, hlid, tx_seq_32,
tx_seq_16);
}
if (ret < 0)
return ret;
} else {
const u8 *addr;
static const u8 bcast_addr[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
addr = sta ? sta->addr : bcast_addr;
if (is_zero_ether_addr(addr)) {
/* We dont support TX only encryption */
return -EOPNOTSUPP;
}
/* The wl1271 does not allow to remove unicast keys - they
will be cleared automatically on next CMD_JOIN. Ignore the
request silently, as we dont want the mac80211 to emit
an error message. */
if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
return 0;
ret = wl1271_cmd_set_sta_key(wl, action,
id, key_type, key_size,
key, addr, tx_seq_32,
tx_seq_16);
if (ret < 0)
return ret;
/* the default WEP key needs to be configured at least once */
if (key_type == KEY_WEP) {
ret = wl1271_cmd_set_sta_default_wep_key(wl,
wl->default_key);
if (ret < 0)
return ret;
}
}
return 0;
}
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf) struct ieee80211_key_conf *key_conf)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
const u8 *addr;
int ret; int ret;
u32 tx_seq_32 = 0; u32 tx_seq_32 = 0;
u16 tx_seq_16 = 0; u16 tx_seq_16 = 0;
u8 key_type; u8 key_type;
static const u8 bcast_addr[ETH_ALEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
wl1271_debug(DEBUG_MAC80211, "mac80211 set key"); wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
addr = sta ? sta->addr : bcast_addr; wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
key_conf->cipher, key_conf->keyidx, key_conf->cipher, key_conf->keyidx,
key_conf->keylen, key_conf->flags); key_conf->keylen, key_conf->flags);
wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen); wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
if (is_zero_ether_addr(addr)) {
/* We dont support TX only encryption */
ret = -EOPNOTSUPP;
goto out;
}
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) { if (unlikely(wl->state == WL1271_STATE_OFF)) {
...@@ -1671,36 +1899,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ...@@ -1671,36 +1899,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
switch (cmd) { switch (cmd) {
case SET_KEY: case SET_KEY:
ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE, ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
key_conf->keyidx, key_type, key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key, key_conf->keylen, key_conf->key,
addr, tx_seq_32, tx_seq_16); tx_seq_32, tx_seq_16, sta);
if (ret < 0) { if (ret < 0) {
wl1271_error("Could not add or replace key"); wl1271_error("Could not add or replace key");
goto out_sleep; goto out_sleep;
} }
/* the default WEP key needs to be configured at least once */
if (key_type == KEY_WEP) {
ret = wl1271_cmd_set_default_wep_key(wl,
wl->default_key);
if (ret < 0)
goto out_sleep;
}
break; break;
case DISABLE_KEY: case DISABLE_KEY:
/* The wl1271 does not allow to remove unicast keys - they ret = wl1271_set_key(wl, KEY_REMOVE,
will be cleared automatically on next CMD_JOIN. Ignore the key_conf->keyidx, key_type,
request silently, as we dont want the mac80211 to emit key_conf->keylen, key_conf->key,
an error message. */ 0, 0, sta);
if (!is_broadcast_ether_addr(addr))
break;
ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
addr, 0, 0);
if (ret < 0) { if (ret < 0) {
wl1271_error("Could not remove key"); wl1271_error("Could not remove key");
goto out_sleep; goto out_sleep;
...@@ -1719,7 +1932,6 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ...@@ -1719,7 +1932,6 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
out_unlock: out_unlock:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
out:
return ret; return ret;
} }
...@@ -1821,7 +2033,7 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) ...@@ -1821,7 +2033,7 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
return ret; return ret;
} }
static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
int offset) int offset)
{ {
u8 *ptr = skb->data + offset; u8 *ptr = skb->data + offset;
...@@ -1831,89 +2043,210 @@ static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, ...@@ -1831,89 +2043,210 @@ static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
if (ptr[0] == WLAN_EID_SSID) { if (ptr[0] == WLAN_EID_SSID) {
wl->ssid_len = ptr[1]; wl->ssid_len = ptr[1];
memcpy(wl->ssid, ptr+2, wl->ssid_len); memcpy(wl->ssid, ptr+2, wl->ssid_len);
return; return 0;
} }
ptr += (ptr[1] + 2); ptr += (ptr[1] + 2);
} }
wl1271_error("No SSID in IEs!\n"); wl1271_error("No SSID in IEs!\n");
return -ENOENT;
} }
static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf, struct ieee80211_bss_conf *bss_conf,
u32 changed) u32 changed)
{ {
enum wl1271_cmd_ps_mode mode; int ret = 0;
struct wl1271 *wl = hw->priv;
struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid);
bool do_join = false;
bool set_assoc = false;
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed"); if (changed & BSS_CHANGED_ERP_SLOT) {
if (bss_conf->use_short_slot)
ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
else
ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
if (ret < 0) {
wl1271_warning("Set slot time failed %d", ret);
goto out;
}
}
mutex_lock(&wl->mutex); if (changed & BSS_CHANGED_ERP_PREAMBLE) {
if (bss_conf->use_short_preamble)
wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
else
wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
}
if (unlikely(wl->state == WL1271_STATE_OFF)) if (changed & BSS_CHANGED_ERP_CTS_PROT) {
goto out; if (bss_conf->use_cts_prot)
ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
else
ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
if (ret < 0) {
wl1271_warning("Set ctsprotect failed %d", ret);
goto out;
}
}
ret = wl1271_ps_elp_wakeup(wl, false); out:
if (ret < 0) return ret;
goto out; }
if ((changed & BSS_CHANGED_BEACON_INT) && static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
(wl->bss_type == BSS_TYPE_IBSS)) { struct ieee80211_vif *vif,
wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d", struct ieee80211_bss_conf *bss_conf,
u32 changed)
{
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
int ret = 0;
if ((changed & BSS_CHANGED_BEACON_INT)) {
wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
bss_conf->beacon_int); bss_conf->beacon_int);
wl->beacon_int = bss_conf->beacon_int; wl->beacon_int = bss_conf->beacon_int;
do_join = true;
} }
if ((changed & BSS_CHANGED_BEACON) && if ((changed & BSS_CHANGED_BEACON)) {
(wl->bss_type == BSS_TYPE_IBSS)) { struct ieee80211_hdr *hdr;
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); int ieoffset = offsetof(struct ieee80211_mgmt,
u.beacon.variable);
struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
u16 tmpl_id;
wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated"); if (!beacon)
goto out;
if (beacon) { wl1271_debug(DEBUG_MASTER, "beacon updated");
struct ieee80211_hdr *hdr;
int ieoffset = offsetof(struct ieee80211_mgmt,
u.beacon.variable);
wl1271_ssid_set(wl, beacon, ieoffset); ret = wl1271_ssid_set(wl, beacon, ieoffset);
if (ret < 0) {
dev_kfree_skb(beacon);
goto out;
}
tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
CMD_TEMPL_BEACON;
ret = wl1271_cmd_template_set(wl, tmpl_id,
beacon->data,
beacon->len, 0,
wl1271_tx_min_rate_get(wl));
if (ret < 0) {
dev_kfree_skb(beacon);
goto out;
}
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, hdr = (struct ieee80211_hdr *) beacon->data;
beacon->data, hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
beacon->len, 0, IEEE80211_STYPE_PROBE_RESP);
wl1271_min_rate_get(wl));
tmpl_id = is_ap ? CMD_TEMPL_AP_PROBE_RESPONSE :
CMD_TEMPL_PROBE_RESPONSE;
ret = wl1271_cmd_template_set(wl,
tmpl_id,
beacon->data,
beacon->len, 0,
wl1271_tx_min_rate_get(wl));
dev_kfree_skb(beacon);
if (ret < 0)
goto out;
}
if (ret < 0) { out:
dev_kfree_skb(beacon); return ret;
goto out_sleep; }
}
hdr = (struct ieee80211_hdr *) beacon->data; /* AP mode changes */
hdr->frame_control = cpu_to_le16( static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
IEEE80211_FTYPE_MGMT | struct ieee80211_vif *vif,
IEEE80211_STYPE_PROBE_RESP); struct ieee80211_bss_conf *bss_conf,
u32 changed)
{
int ret = 0;
ret = wl1271_cmd_template_set(wl, if ((changed & BSS_CHANGED_BASIC_RATES)) {
CMD_TEMPL_PROBE_RESPONSE, u32 rates = bss_conf->basic_rates;
beacon->data, struct conf_tx_rate_class mgmt_rc;
beacon->len, 0,
wl1271_min_rate_get(wl)); wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates);
dev_kfree_skb(beacon); wl->basic_rate = wl1271_tx_min_rate_get(wl);
if (ret < 0) wl1271_debug(DEBUG_AP, "basic rates: 0x%x",
goto out_sleep; wl->basic_rate_set);
/* update the AP management rate policy with the new rates */
mgmt_rc.enabled_rates = wl->basic_rate_set;
mgmt_rc.long_retry_limit = 10;
mgmt_rc.short_retry_limit = 10;
mgmt_rc.aflags = 0;
ret = wl1271_acx_ap_rate_policy(wl, &mgmt_rc,
ACX_TX_AP_MODE_MGMT_RATE);
if (ret < 0) {
wl1271_error("AP mgmt policy change failed %d", ret);
goto out;
}
}
/* Need to update the SSID (for filtering etc) */ ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
do_join = true; if (ret < 0)
goto out;
if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
if (bss_conf->enable_beacon) {
if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
ret = wl1271_cmd_start_bss(wl);
if (ret < 0)
goto out;
set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
wl1271_debug(DEBUG_AP, "started AP");
ret = wl1271_ap_init_hwenc(wl);
if (ret < 0)
goto out;
}
} else {
if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
ret = wl1271_cmd_stop_bss(wl);
if (ret < 0)
goto out;
clear_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
wl1271_debug(DEBUG_AP, "stopped AP");
}
} }
} }
if ((changed & BSS_CHANGED_BEACON_ENABLED) && ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
(wl->bss_type == BSS_TYPE_IBSS)) { if (ret < 0)
goto out;
out:
return;
}
/* STA/IBSS mode changes */
static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
u32 changed)
{
bool do_join = false, set_assoc = false;
bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
int ret;
struct ieee80211_sta *sta;
if (is_ibss) {
ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
changed);
if (ret < 0)
goto out;
}
if ((changed & BSS_CHANGED_BEACON_INT) && is_ibss)
do_join = true;
/* Need to update the SSID (for filtering etc) */
if ((changed & BSS_CHANGED_BEACON) && is_ibss)
do_join = true;
if ((changed & BSS_CHANGED_BEACON_ENABLED) && is_ibss) {
wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s", wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
bss_conf->enable_beacon ? "enabled" : "disabled"); bss_conf->enable_beacon ? "enabled" : "disabled");
...@@ -1924,7 +2257,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -1924,7 +2257,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
do_join = true; do_join = true;
} }
if (changed & BSS_CHANGED_CQM) { if ((changed & BSS_CHANGED_CQM)) {
bool enable = false; bool enable = false;
if (bss_conf->cqm_rssi_thold) if (bss_conf->cqm_rssi_thold)
enable = true; enable = true;
...@@ -1942,24 +2275,26 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -1942,24 +2275,26 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
* and enable the BSSID filter * and enable the BSSID filter
*/ */
memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
if (!is_zero_ether_addr(wl->bssid)) {
ret = wl1271_cmd_build_null_data(wl); ret = wl1271_cmd_build_null_data(wl);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
ret = wl1271_build_qos_null_data(wl); ret = wl1271_build_qos_null_data(wl);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
/* filter out all packets not from this BSSID */ /* filter out all packets not from this BSSID */
wl1271_configure_filters(wl, 0); wl1271_configure_filters(wl, 0);
/* Need to update the BSSID (for filtering etc) */ /* Need to update the BSSID (for filtering etc) */
do_join = true; do_join = true;
}
} }
if (changed & BSS_CHANGED_ASSOC) { if ((changed & BSS_CHANGED_ASSOC)) {
if (bss_conf->assoc) { if (bss_conf->assoc) {
u32 rates; u32 rates;
int ieoffset; int ieoffset;
...@@ -1975,10 +2310,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -1975,10 +2310,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
rates = bss_conf->basic_rates; rates = bss_conf->basic_rates;
wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
rates); rates);
wl->basic_rate = wl1271_min_rate_get(wl); wl->basic_rate = wl1271_tx_min_rate_get(wl);
ret = wl1271_acx_rate_policies(wl); ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
/* /*
* with wl1271, we don't need to update the * with wl1271, we don't need to update the
...@@ -1988,7 +2323,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -1988,7 +2323,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
*/ */
ret = wl1271_cmd_build_ps_poll(wl, wl->aid); ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
/* /*
* Get a template for hardware connection maintenance * Get a template for hardware connection maintenance
...@@ -2002,17 +2337,19 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -2002,17 +2337,19 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
/* enable the connection monitoring feature */ /* enable the connection monitoring feature */
ret = wl1271_acx_conn_monit_params(wl, true); ret = wl1271_acx_conn_monit_params(wl, true);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
/* If we want to go in PSM but we're not there yet */ /* If we want to go in PSM but we're not there yet */
if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) && if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
!test_bit(WL1271_FLAG_PSM, &wl->flags)) { !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
enum wl1271_cmd_ps_mode mode;
mode = STATION_POWER_SAVE_MODE; mode = STATION_POWER_SAVE_MODE;
ret = wl1271_ps_set_mode(wl, mode, ret = wl1271_ps_set_mode(wl, mode,
wl->basic_rate, wl->basic_rate,
true); true);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
} }
} else { } else {
/* use defaults when not associated */ /* use defaults when not associated */
...@@ -2029,10 +2366,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -2029,10 +2366,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
/* revert back to minimum rates for the current band */ /* revert back to minimum rates for the current band */
wl1271_set_band_rate(wl); wl1271_set_band_rate(wl);
wl->basic_rate = wl1271_min_rate_get(wl); wl->basic_rate = wl1271_tx_min_rate_get(wl);
ret = wl1271_acx_rate_policies(wl); ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
/* disable connection monitor features */ /* disable connection monitor features */
ret = wl1271_acx_conn_monit_params(wl, false); ret = wl1271_acx_conn_monit_params(wl, false);
...@@ -2040,74 +2377,54 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -2040,74 +2377,54 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
/* Disable the keep-alive feature */ /* Disable the keep-alive feature */
ret = wl1271_acx_keep_alive_mode(wl, false); ret = wl1271_acx_keep_alive_mode(wl, false);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
/* restore the bssid filter and go to dummy bssid */ /* restore the bssid filter and go to dummy bssid */
wl1271_unjoin(wl); wl1271_unjoin(wl);
wl1271_dummy_join(wl); wl1271_dummy_join(wl);
} }
}
if (changed & BSS_CHANGED_ERP_SLOT) {
if (bss_conf->use_short_slot)
ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
else
ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
if (ret < 0) {
wl1271_warning("Set slot time failed %d", ret);
goto out_sleep;
}
}
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
if (bss_conf->use_short_preamble)
wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
else
wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
} }
if (changed & BSS_CHANGED_ERP_CTS_PROT) { ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
if (bss_conf->use_cts_prot) if (ret < 0)
ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE); goto out;
else
ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
if (ret < 0) {
wl1271_warning("Set ctsprotect failed %d", ret);
goto out_sleep;
}
}
/* rcu_read_lock();
* Takes care of: New association with HT enable, sta = ieee80211_find_sta(vif, bss_conf->bssid);
* HT information change in beacon. if (sta) {
*/ /* handle new association with HT and HT information change */
if (sta && if ((changed & BSS_CHANGED_HT) &&
(changed & BSS_CHANGED_HT) && (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
(bss_conf->channel_type != NL80211_CHAN_NO_HT)) { ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap,
ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true); true);
if (ret < 0) { if (ret < 0) {
wl1271_warning("Set ht cap true failed %d", ret); wl1271_warning("Set ht cap true failed %d",
goto out_sleep; ret);
} rcu_read_unlock();
goto out;
}
ret = wl1271_acx_set_ht_information(wl, ret = wl1271_acx_set_ht_information(wl,
bss_conf->ht_operation_mode); bss_conf->ht_operation_mode);
if (ret < 0) { if (ret < 0) {
wl1271_warning("Set ht information failed %d", ret); wl1271_warning("Set ht information failed %d",
goto out_sleep; ret);
rcu_read_unlock();
goto out;
}
} }
} /* handle new association without HT and disassociation */
/* else if (changed & BSS_CHANGED_ASSOC) {
* Takes care of: New association without HT, ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap,
* Disassociation. false);
*/ if (ret < 0) {
else if (sta && (changed & BSS_CHANGED_ASSOC)) { wl1271_warning("Set ht cap false failed %d",
ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, false); ret);
if (ret < 0) { rcu_read_unlock();
wl1271_warning("Set ht cap false failed %d", ret); goto out;
goto out_sleep; }
} }
} }
rcu_read_unlock();
if (changed & BSS_CHANGED_ARP_FILTER) { if (changed & BSS_CHANGED_ARP_FILTER) {
__be32 addr = bss_conf->arp_addr_list[0]; __be32 addr = bss_conf->arp_addr_list[0];
...@@ -2124,76 +2441,128 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -2124,76 +2441,128 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
ret = wl1271_cmd_build_arp_rsp(wl, addr); ret = wl1271_cmd_build_arp_rsp(wl, addr);
if (ret < 0) { if (ret < 0) {
wl1271_warning("build arp rsp failed: %d", ret); wl1271_warning("build arp rsp failed: %d", ret);
goto out_sleep; goto out;
} }
ret = wl1271_acx_arp_ip_filter(wl, ret = wl1271_acx_arp_ip_filter(wl,
(ACX_ARP_FILTER_ARP_FILTERING | ACX_ARP_FILTER_ARP_FILTERING,
ACX_ARP_FILTER_AUTO_ARP),
addr); addr);
} else } else
ret = wl1271_acx_arp_ip_filter(wl, 0, addr); ret = wl1271_acx_arp_ip_filter(wl, 0, addr);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out;
} }
if (do_join) { if (do_join) {
ret = wl1271_join(wl, set_assoc); ret = wl1271_join(wl, set_assoc);
if (ret < 0) { if (ret < 0) {
wl1271_warning("cmd join failed %d", ret); wl1271_warning("cmd join failed %d", ret);
goto out_sleep; goto out;
} }
} }
out_sleep:
wl1271_ps_elp_sleep(wl);
out: out:
mutex_unlock(&wl->mutex); return;
} }
static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
const struct ieee80211_tx_queue_params *params) struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
u32 changed)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
u8 ps_scheme; bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
int ret; int ret;
mutex_lock(&wl->mutex); wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
(int)changed);
wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) { if (unlikely(wl->state == WL1271_STATE_OFF))
ret = -EAGAIN;
goto out; goto out;
}
ret = wl1271_ps_elp_wakeup(wl, false); ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0) if (ret < 0)
goto out; goto out;
/* the txop is confed in units of 32us by the mac80211, we need us */ if (is_ap)
ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
params->cw_min, params->cw_max, else
params->aifs, params->txop << 5); wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
if (ret < 0)
goto out_sleep; wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
}
static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct wl1271 *wl = hw->priv;
u8 ps_scheme;
int ret = 0;
mutex_lock(&wl->mutex);
wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
if (params->uapsd) if (params->uapsd)
ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER; ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
else else
ps_scheme = CONF_PS_SCHEME_LEGACY; ps_scheme = CONF_PS_SCHEME_LEGACY;
ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), if (wl->state == WL1271_STATE_OFF) {
CONF_CHANNEL_TYPE_EDCF, /*
wl1271_tx_get_queue(queue), * If the state is off, the parameters will be recorded and
ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0); * configured on init. This happens in AP-mode.
if (ret < 0) */
goto out_sleep; struct conf_tx_ac_category *conf_ac =
&wl->conf.tx.ac_conf[wl1271_tx_get_queue(queue)];
struct conf_tx_tid *conf_tid =
&wl->conf.tx.tid_conf[wl1271_tx_get_queue(queue)];
conf_ac->ac = wl1271_tx_get_queue(queue);
conf_ac->cw_min = (u8)params->cw_min;
conf_ac->cw_max = params->cw_max;
conf_ac->aifsn = params->aifs;
conf_ac->tx_op_limit = params->txop << 5;
conf_tid->queue_id = wl1271_tx_get_queue(queue);
conf_tid->channel_type = CONF_CHANNEL_TYPE_EDCF;
conf_tid->tsid = wl1271_tx_get_queue(queue);
conf_tid->ps_scheme = ps_scheme;
conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
conf_tid->apsd_conf[0] = 0;
conf_tid->apsd_conf[1] = 0;
} else {
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
/*
* the txop is confed in units of 32us by the mac80211,
* we need us
*/
ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
params->cw_min, params->cw_max,
params->aifs, params->txop << 5);
if (ret < 0)
goto out_sleep;
ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
CONF_CHANNEL_TYPE_EDCF,
wl1271_tx_get_queue(queue),
ps_scheme, CONF_ACK_POLICY_LEGACY,
0, 0);
if (ret < 0)
goto out_sleep;
out_sleep: out_sleep:
wl1271_ps_elp_sleep(wl); wl1271_ps_elp_sleep(wl);
}
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
...@@ -2247,6 +2616,172 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx, ...@@ -2247,6 +2616,172 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
return 0; return 0;
} }
static int wl1271_allocate_hlid(struct wl1271 *wl,
struct ieee80211_sta *sta,
u8 *hlid)
{
struct wl1271_station *wl_sta;
int id;
id = find_first_zero_bit(wl->ap_hlid_map, AP_MAX_STATIONS);
if (id >= AP_MAX_STATIONS) {
wl1271_warning("could not allocate HLID - too much stations");
return -EBUSY;
}
wl_sta = (struct wl1271_station *)sta->drv_priv;
__set_bit(id, wl->ap_hlid_map);
wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
*hlid = wl_sta->hlid;
return 0;
}
static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid)
{
int id = hlid - WL1271_AP_STA_HLID_START;
__clear_bit(id, wl->ap_hlid_map);
}
static int wl1271_op_sta_add(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct wl1271 *wl = hw->priv;
int ret = 0;
u8 hlid;
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
if (wl->bss_type != BSS_TYPE_AP_BSS)
goto out;
wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
ret = wl1271_allocate_hlid(wl, sta, &hlid);
if (ret < 0)
goto out;
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
ret = wl1271_cmd_add_sta(wl, sta, hlid);
if (ret < 0)
goto out_sleep;
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
}
static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct wl1271 *wl = hw->priv;
struct wl1271_station *wl_sta;
int ret = 0, id;
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
if (wl->bss_type != BSS_TYPE_AP_BSS)
goto out;
wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
wl_sta = (struct wl1271_station *)sta->drv_priv;
id = wl_sta->hlid - WL1271_AP_STA_HLID_START;
if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
goto out;
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid);
if (ret < 0)
goto out_sleep;
wl1271_free_hlid(wl, wl_sta->hlid);
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
}
int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
struct wl1271 *wl = hw->priv;
int ret;
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) {
ret = -EAGAIN;
goto out;
}
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
switch (action) {
case IEEE80211_AMPDU_RX_START:
if (wl->ba_support) {
ret = wl1271_acx_set_ba_receiver_session(wl, tid, *ssn,
true);
if (!ret)
wl->ba_rx_bitmap |= BIT(tid);
} else {
ret = -ENOTSUPP;
}
break;
case IEEE80211_AMPDU_RX_STOP:
ret = wl1271_acx_set_ba_receiver_session(wl, tid, 0, false);
if (!ret)
wl->ba_rx_bitmap &= ~BIT(tid);
break;
/*
* The BA initiator session management in FW independently.
* Falling break here on purpose for all TX APDU commands.
*/
case IEEE80211_AMPDU_TX_START:
case IEEE80211_AMPDU_TX_STOP:
case IEEE80211_AMPDU_TX_OPERATIONAL:
ret = -EINVAL;
break;
default:
wl1271_error("Incorrect ampdu action id=%x\n", action);
ret = -EINVAL;
}
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
}
/* can't be const, mac80211 writes to this */ /* can't be const, mac80211 writes to this */
static struct ieee80211_rate wl1271_rates[] = { static struct ieee80211_rate wl1271_rates[] = {
{ .bitrate = 10, { .bitrate = 10,
...@@ -2305,6 +2840,7 @@ static struct ieee80211_channel wl1271_channels[] = { ...@@ -2305,6 +2840,7 @@ static struct ieee80211_channel wl1271_channels[] = {
{ .hw_value = 11, .center_freq = 2462, .max_power = 25 }, { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
{ .hw_value = 12, .center_freq = 2467, .max_power = 25 }, { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
{ .hw_value = 13, .center_freq = 2472, .max_power = 25 }, { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
{ .hw_value = 14, .center_freq = 2484, .max_power = 25 },
}; };
/* mapping to indexes for wl1271_rates */ /* mapping to indexes for wl1271_rates */
...@@ -2493,6 +3029,9 @@ static const struct ieee80211_ops wl1271_ops = { ...@@ -2493,6 +3029,9 @@ static const struct ieee80211_ops wl1271_ops = {
.conf_tx = wl1271_op_conf_tx, .conf_tx = wl1271_op_conf_tx,
.get_tsf = wl1271_op_get_tsf, .get_tsf = wl1271_op_get_tsf,
.get_survey = wl1271_op_get_survey, .get_survey = wl1271_op_get_survey,
.sta_add = wl1271_op_sta_add,
.sta_remove = wl1271_op_sta_remove,
.ampdu_action = wl1271_op_ampdu_action,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd) CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
}; };
...@@ -2607,6 +3146,18 @@ int wl1271_register_hw(struct wl1271 *wl) ...@@ -2607,6 +3146,18 @@ int wl1271_register_hw(struct wl1271 *wl)
if (wl->mac80211_registered) if (wl->mac80211_registered)
return 0; return 0;
ret = wl1271_fetch_nvs(wl);
if (ret == 0) {
u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
wl->mac_addr[0] = nvs_ptr[11];
wl->mac_addr[1] = nvs_ptr[10];
wl->mac_addr[2] = nvs_ptr[6];
wl->mac_addr[3] = nvs_ptr[5];
wl->mac_addr[4] = nvs_ptr[4];
wl->mac_addr[5] = nvs_ptr[3];
}
SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
ret = ieee80211_register_hw(wl->hw); ret = ieee80211_register_hw(wl->hw);
...@@ -2629,6 +3180,9 @@ EXPORT_SYMBOL_GPL(wl1271_register_hw); ...@@ -2629,6 +3180,9 @@ EXPORT_SYMBOL_GPL(wl1271_register_hw);
void wl1271_unregister_hw(struct wl1271 *wl) void wl1271_unregister_hw(struct wl1271 *wl)
{ {
if (wl->state == WL1271_STATE_PLT)
__wl1271_plt_stop(wl);
unregister_netdevice_notifier(&wl1271_dev_notifier); unregister_netdevice_notifier(&wl1271_dev_notifier);
ieee80211_unregister_hw(wl->hw); ieee80211_unregister_hw(wl->hw);
wl->mac80211_registered = false; wl->mac80211_registered = false;
...@@ -2667,7 +3221,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl) ...@@ -2667,7 +3221,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC); BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->max_scan_ssids = 1;
/* /*
* Maximum length of elements in scanning probe request templates * Maximum length of elements in scanning probe request templates
...@@ -2676,8 +3230,20 @@ int wl1271_init_ieee80211(struct wl1271 *wl) ...@@ -2676,8 +3230,20 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
*/ */
wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
sizeof(struct ieee80211_header); sizeof(struct ieee80211_header);
wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; /*
* We keep local copies of the band structs because we need to
* modify them on a per-device basis.
*/
memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
sizeof(wl1271_band_2ghz));
memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
sizeof(wl1271_band_5ghz));
wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
&wl->bands[IEEE80211_BAND_2GHZ];
wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
&wl->bands[IEEE80211_BAND_5GHZ];
wl->hw->queues = 4; wl->hw->queues = 4;
wl->hw->max_rates = 1; wl->hw->max_rates = 1;
...@@ -2686,6 +3252,10 @@ int wl1271_init_ieee80211(struct wl1271 *wl) ...@@ -2686,6 +3252,10 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl)); SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
wl->hw->sta_data_size = sizeof(struct wl1271_station);
wl->hw->max_rx_aggregation_subframes = 8;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(wl1271_init_ieee80211); EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
...@@ -2735,8 +3305,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -2735,8 +3305,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->beacon_int = WL1271_DEFAULT_BEACON_INT; wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
wl->default_key = 0; wl->default_key = 0;
wl->rx_counter = 0; wl->rx_counter = 0;
wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG;
wl->rx_filter = WL1271_DEFAULT_RX_FILTER; wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER;
wl->psm_entry_retry = 0; wl->psm_entry_retry = 0;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC; wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
...@@ -2748,6 +3318,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -2748,6 +3318,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->flags = 0; wl->flags = 0;
wl->sg_enabled = true; wl->sg_enabled = true;
wl->hw_pg_ver = -1; wl->hw_pg_ver = -1;
wl->bss_type = MAX_BSS_TYPE;
wl->set_bss_type = MAX_BSS_TYPE;
wl->fw_bss_type = MAX_BSS_TYPE;
memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
for (i = 0; i < ACX_TX_DESCRIPTORS; i++) for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
...@@ -2837,9 +3410,9 @@ int wl1271_free_hw(struct wl1271 *wl) ...@@ -2837,9 +3410,9 @@ int wl1271_free_hw(struct wl1271 *wl)
} }
EXPORT_SYMBOL_GPL(wl1271_free_hw); EXPORT_SYMBOL_GPL(wl1271_free_hw);
u32 wl12xx_debug_level; u32 wl12xx_debug_level = DEBUG_NONE;
EXPORT_SYMBOL_GPL(wl12xx_debug_level); EXPORT_SYMBOL_GPL(wl12xx_debug_level);
module_param_named(debug_level, wl12xx_debug_level, uint, DEBUG_NONE); module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -198,6 +198,16 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) ...@@ -198,6 +198,16 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
pkt_offset += pkt_length; pkt_offset += pkt_length;
} }
} }
wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
cpu_to_le32(wl->rx_counter)); }
void wl1271_set_default_filters(struct wl1271 *wl)
{
if (wl->bss_type == BSS_TYPE_AP_BSS) {
wl->rx_config = WL1271_DEFAULT_AP_RX_CONFIG;
wl->rx_filter = WL1271_DEFAULT_AP_RX_FILTER;
} else {
wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG;
wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER;
}
} }
...@@ -86,8 +86,9 @@ ...@@ -86,8 +86,9 @@
/* /*
* RX Descriptor status * RX Descriptor status
* *
* Bits 0-2 - status * Bits 0-2 - error code
* Bits 3-7 - reserved * Bits 3-5 - process_id tag (AP mode FW)
* Bits 6-7 - reserved
*/ */
#define WL1271_RX_DESC_STATUS_MASK 0x07 #define WL1271_RX_DESC_STATUS_MASK 0x07
...@@ -110,12 +111,16 @@ struct wl1271_rx_descriptor { ...@@ -110,12 +111,16 @@ struct wl1271_rx_descriptor {
u8 snr; u8 snr;
__le32 timestamp; __le32 timestamp;
u8 packet_class; u8 packet_class;
u8 process_id; union {
u8 process_id; /* STA FW */
u8 hlid; /* AP FW */
} __packed;
u8 pad_len; u8 pad_len;
u8 reserved; u8 reserved;
} __packed; } __packed;
void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status); void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
void wl1271_set_default_filters(struct wl1271 *wl);
#endif #endif
...@@ -345,3 +345,4 @@ MODULE_LICENSE("GPL"); ...@@ -345,3 +345,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL1271_FW_NAME);
MODULE_FIRMWARE(WL1271_AP_FW_NAME);
...@@ -110,9 +110,9 @@ static void wl1271_spi_reset(struct wl1271 *wl) ...@@ -110,9 +110,9 @@ static void wl1271_spi_reset(struct wl1271 *wl)
spi_message_add_tail(&t, &m); spi_message_add_tail(&t, &m);
spi_sync(wl_to_spi(wl), &m); spi_sync(wl_to_spi(wl), &m);
kfree(cmd);
wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN);
kfree(cmd);
} }
static void wl1271_spi_init(struct wl1271 *wl) static void wl1271_spi_init(struct wl1271 *wl)
...@@ -495,4 +495,5 @@ MODULE_LICENSE("GPL"); ...@@ -495,4 +495,5 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL1271_FW_NAME);
MODULE_FIRMWARE(WL1271_AP_FW_NAME);
MODULE_ALIAS("spi:wl1271"); MODULE_ALIAS("spi:wl1271");
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/etherdevice.h>
#include "wl12xx.h" #include "wl12xx.h"
#include "io.h" #include "io.h"
...@@ -30,6 +31,23 @@ ...@@ -30,6 +31,23 @@
#include "ps.h" #include "ps.h"
#include "tx.h" #include "tx.h"
static int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id)
{
int ret;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
if (is_ap)
ret = wl1271_cmd_set_ap_default_wep_key(wl, id);
else
ret = wl1271_cmd_set_sta_default_wep_key(wl, id);
if (ret < 0)
return ret;
wl1271_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id);
return 0;
}
static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb) static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb)
{ {
int id; int id;
...@@ -99,7 +117,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ...@@ -99,7 +117,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
{ {
struct timespec ts; struct timespec ts;
struct wl1271_tx_hw_descr *desc; struct wl1271_tx_hw_descr *desc;
int pad, ac; int pad, ac, rate_idx;
s64 hosttime; s64 hosttime;
u16 tx_attr; u16 tx_attr;
...@@ -117,7 +135,11 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ...@@ -117,7 +135,11 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
getnstimeofday(&ts); getnstimeofday(&ts);
hosttime = (timespec_to_ns(&ts) >> 10); hosttime = (timespec_to_ns(&ts) >> 10);
desc->start_time = cpu_to_le32(hosttime - wl->time_offset); desc->start_time = cpu_to_le32(hosttime - wl->time_offset);
desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
if (wl->bss_type != BSS_TYPE_AP_BSS)
desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
else
desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU);
/* configure the tx attributes */ /* configure the tx attributes */
tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
...@@ -125,7 +147,41 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ...@@ -125,7 +147,41 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
/* queue (we use same identifiers for tid's and ac's */ /* queue (we use same identifiers for tid's and ac's */
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
desc->tid = ac; desc->tid = ac;
desc->aid = TX_HW_DEFAULT_AID;
if (wl->bss_type != BSS_TYPE_AP_BSS) {
desc->aid = TX_HW_DEFAULT_AID;
/* if the packets are destined for AP (have a STA entry)
send them with AP rate policies, otherwise use default
basic rates */
if (control->control.sta)
rate_idx = ACX_TX_AP_FULL_RATE;
else
rate_idx = ACX_TX_BASIC_RATE;
} else {
if (control->control.sta) {
struct wl1271_station *wl_sta;
wl_sta = (struct wl1271_station *)
control->control.sta->drv_priv;
desc->hlid = wl_sta->hlid;
rate_idx = ac;
} else {
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)
(skb->data + sizeof(*desc));
if (ieee80211_is_mgmt(hdr->frame_control)) {
desc->hlid = WL1271_AP_GLOBAL_HLID;
rate_idx = ACX_TX_AP_MODE_MGMT_RATE;
} else {
desc->hlid = WL1271_AP_BROADCAST_HLID;
rate_idx = ACX_TX_AP_MODE_BCST_RATE;
}
}
}
tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY;
desc->reserved = 0; desc->reserved = 0;
/* align the length (and store in terms of words) */ /* align the length (and store in terms of words) */
...@@ -136,14 +192,12 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ...@@ -136,14 +192,12 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
pad = pad - skb->len; pad = pad - skb->len;
tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD;
/* if the packets are destined for AP (have a STA entry) send them
with AP rate policies, otherwise use default basic rates */
if (control->control.sta)
tx_attr |= ACX_TX_AP_FULL_RATE << TX_HW_ATTR_OFST_RATE_POLICY;
desc->tx_attr = cpu_to_le16(tx_attr); desc->tx_attr = cpu_to_le16(tx_attr);
wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad); wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d "
"tx_attr: 0x%x len: %d life: %d mem: %d", pad, desc->hlid,
le16_to_cpu(desc->tx_attr), le16_to_cpu(desc->length),
le16_to_cpu(desc->life_time), desc->total_mem_blocks);
} }
/* caller must hold wl->mutex */ /* caller must hold wl->mutex */
...@@ -153,7 +207,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, ...@@ -153,7 +207,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
u32 extra = 0; u32 extra = 0;
int ret = 0; int ret = 0;
u8 idx;
u32 total_len; u32 total_len;
if (!skb) if (!skb)
...@@ -166,11 +219,15 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, ...@@ -166,11 +219,15 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
extra = WL1271_TKIP_IV_SPACE; extra = WL1271_TKIP_IV_SPACE;
if (info->control.hw_key) { if (info->control.hw_key) {
idx = info->control.hw_key->hw_key_idx; bool is_wep;
u8 idx = info->control.hw_key->hw_key_idx;
u32 cipher = info->control.hw_key->cipher;
is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
(cipher == WLAN_CIPHER_SUITE_WEP104);
/* FIXME: do we have to do this if we're not using WEP? */ if (unlikely(is_wep && wl->default_key != idx)) {
if (unlikely(wl->default_key != idx)) { ret = wl1271_set_default_wep_key(wl, idx);
ret = wl1271_cmd_set_default_wep_key(wl, idx);
if (ret < 0) if (ret < 0)
return ret; return ret;
wl->default_key = idx; wl->default_key = idx;
...@@ -303,7 +360,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl) ...@@ -303,7 +360,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
woken_up = true; woken_up = true;
wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates); wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
wl1271_acx_rate_policies(wl); wl1271_acx_sta_rate_policies(wl);
} }
while ((skb = wl1271_skb_dequeue(wl))) { while ((skb = wl1271_skb_dequeue(wl))) {
...@@ -521,3 +578,21 @@ void wl1271_tx_flush(struct wl1271 *wl) ...@@ -521,3 +578,21 @@ void wl1271_tx_flush(struct wl1271 *wl)
wl1271_warning("Unable to flush all TX buffers, timed out."); wl1271_warning("Unable to flush all TX buffers, timed out.");
} }
u32 wl1271_tx_min_rate_get(struct wl1271 *wl)
{
int i;
u32 rate = 0;
if (!wl->basic_rate_set) {
WARN_ON(1);
wl->basic_rate_set = wl->conf.tx.basic_rate;
}
for (i = 0; !rate; i++) {
if ((wl->basic_rate_set >> i) & 0x1)
rate = 1 << i;
}
return rate;
}
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define TX_HW_BLOCK_SIZE 252 #define TX_HW_BLOCK_SIZE 252
#define TX_HW_MGMT_PKT_LIFETIME_TU 2000 #define TX_HW_MGMT_PKT_LIFETIME_TU 2000
#define TX_HW_AP_MODE_PKT_LIFETIME_TU 8000
/* The chipset reference driver states, that the "aid" value 1 /* The chipset reference driver states, that the "aid" value 1
* is for infra-BSS, but is still always used */ * is for infra-BSS, but is still always used */
#define TX_HW_DEFAULT_AID 1 #define TX_HW_DEFAULT_AID 1
...@@ -77,8 +78,12 @@ struct wl1271_tx_hw_descr { ...@@ -77,8 +78,12 @@ struct wl1271_tx_hw_descr {
u8 id; u8 id;
/* The packet TID value (as User-Priority) */ /* The packet TID value (as User-Priority) */
u8 tid; u8 tid;
/* Identifier of the remote STA in IBSS, 1 in infra-BSS */ union {
u8 aid; /* STA - Identifier of the remote STA in IBSS, 1 in infra-BSS */
u8 aid;
/* AP - host link ID (HLID) */
u8 hlid;
} __packed;
u8 reserved; u8 reserved;
} __packed; } __packed;
...@@ -146,5 +151,6 @@ void wl1271_tx_reset(struct wl1271 *wl); ...@@ -146,5 +151,6 @@ void wl1271_tx_reset(struct wl1271 *wl);
void wl1271_tx_flush(struct wl1271 *wl); void wl1271_tx_flush(struct wl1271 *wl);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set);
u32 wl1271_tx_min_rate_get(struct wl1271 *wl);
#endif #endif
...@@ -38,6 +38,13 @@ ...@@ -38,6 +38,13 @@
#define DRIVER_NAME "wl1271" #define DRIVER_NAME "wl1271"
#define DRIVER_PREFIX DRIVER_NAME ": " #define DRIVER_PREFIX DRIVER_NAME ": "
/*
* FW versions support BA 11n
* versions marks x.x.x.50-60.x
*/
#define WL12XX_BA_SUPPORT_FW_COST_VER2_START 50
#define WL12XX_BA_SUPPORT_FW_COST_VER2_END 60
enum { enum {
DEBUG_NONE = 0, DEBUG_NONE = 0,
DEBUG_IRQ = BIT(0), DEBUG_IRQ = BIT(0),
...@@ -57,6 +64,8 @@ enum { ...@@ -57,6 +64,8 @@ enum {
DEBUG_SDIO = BIT(14), DEBUG_SDIO = BIT(14),
DEBUG_FILTERS = BIT(15), DEBUG_FILTERS = BIT(15),
DEBUG_ADHOC = BIT(16), DEBUG_ADHOC = BIT(16),
DEBUG_AP = BIT(17),
DEBUG_MASTER = (DEBUG_ADHOC | DEBUG_AP),
DEBUG_ALL = ~0, DEBUG_ALL = ~0,
}; };
...@@ -103,16 +112,27 @@ extern u32 wl12xx_debug_level; ...@@ -103,16 +112,27 @@ extern u32 wl12xx_debug_level;
true); \ true); \
} while (0) } while (0)
#define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ #define WL1271_DEFAULT_STA_RX_CONFIG (CFG_UNI_FILTER_EN | \
CFG_BSSID_FILTER_EN | \ CFG_BSSID_FILTER_EN | \
CFG_MC_FILTER_EN) CFG_MC_FILTER_EN)
#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \ #define WL1271_DEFAULT_STA_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \
CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \ CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \
CFG_RX_CTL_EN | CFG_RX_BCN_EN | \ CFG_RX_CTL_EN | CFG_RX_BCN_EN | \
CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN)
#define WL1271_DEFAULT_AP_RX_CONFIG 0
#define WL1271_DEFAULT_AP_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PREQ_EN | \
CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \
CFG_RX_CTL_EN | CFG_RX_AUTH_EN | \
CFG_RX_ASSOC_EN)
#define WL1271_FW_NAME "wl1271-fw.bin" #define WL1271_FW_NAME "wl1271-fw.bin"
#define WL1271_AP_FW_NAME "wl1271-fw-ap.bin"
#define WL1271_NVS_NAME "wl1271-nvs.bin" #define WL1271_NVS_NAME "wl1271-nvs.bin"
#define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
...@@ -129,6 +149,14 @@ extern u32 wl12xx_debug_level; ...@@ -129,6 +149,14 @@ extern u32 wl12xx_debug_level;
#define WL1271_DEFAULT_BEACON_INT 100 #define WL1271_DEFAULT_BEACON_INT 100
#define WL1271_DEFAULT_DTIM_PERIOD 1 #define WL1271_DEFAULT_DTIM_PERIOD 1
#define WL1271_AP_GLOBAL_HLID 0
#define WL1271_AP_BROADCAST_HLID 1
#define WL1271_AP_STA_HLID_START 2
#define WL1271_AP_BSS_INDEX 0
#define WL1271_AP_DEF_INACTIV_SEC 300
#define WL1271_AP_DEF_BEACON_EXP 20
#define ACX_TX_DESCRIPTORS 32 #define ACX_TX_DESCRIPTORS 32
#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) #define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
...@@ -161,10 +189,13 @@ struct wl1271_partition_set { ...@@ -161,10 +189,13 @@ struct wl1271_partition_set {
struct wl1271; struct wl1271;
#define WL12XX_NUM_FW_VER 5
/* FIXME: I'm not sure about this structure name */ /* FIXME: I'm not sure about this structure name */
struct wl1271_chip { struct wl1271_chip {
u32 id; u32 id;
char fw_ver[21]; char fw_ver_str[ETHTOOL_BUSINFO_LEN];
unsigned int fw_ver[WL12XX_NUM_FW_VER];
}; };
struct wl1271_stats { struct wl1271_stats {
...@@ -178,6 +209,11 @@ struct wl1271_stats { ...@@ -178,6 +209,11 @@ struct wl1271_stats {
#define NUM_TX_QUEUES 4 #define NUM_TX_QUEUES 4
#define NUM_RX_PKT_DESC 8 #define NUM_RX_PKT_DESC 8
#define AP_MAX_STATIONS 5
/* Broadcast and Global links + links to stations */
#define AP_MAX_LINKS (AP_MAX_STATIONS + 2)
/* FW status registers */ /* FW status registers */
struct wl1271_fw_status { struct wl1271_fw_status {
__le32 intr; __le32 intr;
...@@ -188,7 +224,18 @@ struct wl1271_fw_status { ...@@ -188,7 +224,18 @@ struct wl1271_fw_status {
__le32 rx_pkt_descs[NUM_RX_PKT_DESC]; __le32 rx_pkt_descs[NUM_RX_PKT_DESC];
__le32 tx_released_blks[NUM_TX_QUEUES]; __le32 tx_released_blks[NUM_TX_QUEUES];
__le32 fw_localtime; __le32 fw_localtime;
__le32 padding[2];
/* Next fields valid only in AP FW */
/*
* A bitmap (where each bit represents a single HLID)
* to indicate if the station is in PS mode.
*/
__le32 link_ps_bitmap;
/* Number of freed MBs per HLID */
u8 tx_lnk_free_blks[AP_MAX_LINKS];
u8 padding_1[1];
} __packed; } __packed;
struct wl1271_rx_mem_pool_addr { struct wl1271_rx_mem_pool_addr {
...@@ -218,6 +265,19 @@ struct wl1271_if_operations { ...@@ -218,6 +265,19 @@ struct wl1271_if_operations {
void (*disable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl);
}; };
#define MAX_NUM_KEYS 14
#define MAX_KEY_SIZE 32
struct wl1271_ap_key {
u8 id;
u8 key_type;
u8 key_size;
u8 key[MAX_KEY_SIZE];
u8 hlid;
u32 tx_seq_32;
u16 tx_seq_16;
};
struct wl1271 { struct wl1271 {
struct platform_device *plat_dev; struct platform_device *plat_dev;
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
...@@ -251,6 +311,7 @@ struct wl1271 { ...@@ -251,6 +311,7 @@ struct wl1271 {
#define WL1271_FLAG_PSPOLL_FAILURE (12) #define WL1271_FLAG_PSPOLL_FAILURE (12)
#define WL1271_FLAG_STA_STATE_SENT (13) #define WL1271_FLAG_STA_STATE_SENT (13)
#define WL1271_FLAG_FW_TX_BUSY (14) #define WL1271_FLAG_FW_TX_BUSY (14)
#define WL1271_FLAG_AP_STARTED (15)
unsigned long flags; unsigned long flags;
struct wl1271_partition_set part; struct wl1271_partition_set part;
...@@ -262,6 +323,7 @@ struct wl1271 { ...@@ -262,6 +323,7 @@ struct wl1271 {
u8 *fw; u8 *fw;
size_t fw_len; size_t fw_len;
u8 fw_bss_type;
struct wl1271_nvs_file *nvs; struct wl1271_nvs_file *nvs;
size_t nvs_len; size_t nvs_len;
...@@ -378,7 +440,6 @@ struct wl1271 { ...@@ -378,7 +440,6 @@ struct wl1271 {
int last_rssi_event; int last_rssi_event;
struct wl1271_stats stats; struct wl1271_stats stats;
struct dentry *rootdir;
__le32 buffer_32; __le32 buffer_32;
u32 buffer_cmd; u32 buffer_cmd;
...@@ -400,6 +461,23 @@ struct wl1271 { ...@@ -400,6 +461,23 @@ struct wl1271 {
/* Most recently reported noise in dBm */ /* Most recently reported noise in dBm */
s8 noise; s8 noise;
/* map for HLIDs of associated stations - when operating in AP mode */
unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)];
/* recoreded keys for AP-mode - set here before AP startup */
struct wl1271_ap_key *recorded_ap_keys[MAX_NUM_KEYS];
/* bands supported by this instance of wl12xx */
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
/* RX BA constraint value */
bool ba_support;
u8 ba_rx_bitmap;
};
struct wl1271_station {
u8 hlid;
}; };
int wl1271_plt_start(struct wl1271 *wl); int wl1271_plt_start(struct wl1271 *wl);
......
...@@ -138,13 +138,13 @@ struct wl12xx_arp_rsp_template { ...@@ -138,13 +138,13 @@ struct wl12xx_arp_rsp_template {
struct ieee80211_hdr_3addr hdr; struct ieee80211_hdr_3addr hdr;
u8 llc_hdr[sizeof(rfc1042_header)]; u8 llc_hdr[sizeof(rfc1042_header)];
u16 llc_type; __be16 llc_type;
struct arphdr arp_hdr; struct arphdr arp_hdr;
u8 sender_hw[ETH_ALEN]; u8 sender_hw[ETH_ALEN];
u32 sender_ip; __be32 sender_ip;
u8 target_hw[ETH_ALEN]; u8 target_hw[ETH_ALEN];
u32 target_ip; __be32 target_ip;
} __packed; } __packed;
...@@ -160,4 +160,9 @@ struct wl12xx_probe_resp_template { ...@@ -160,4 +160,9 @@ struct wl12xx_probe_resp_template {
struct wl12xx_ie_country country; struct wl12xx_ie_country country;
} __packed; } __packed;
struct wl12xx_disconn_template {
struct ieee80211_header header;
__le16 disconn_reason;
} __packed;
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment