rsi: add support for legacy power save

This patch adds support for legacy power save. Necessary
configuration frames are downloaded to firmware when power save
is enabled/disabled

Signed-off-by: Karun Eagalapati <karun256@gmail.com>
Signed-off-by: Amitkumar Karwar <amit.karwar@redpinesignals.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
Karun Eagalapati 2017-08-03 19:58:59 +05:30 committed by Kalle Valo
parent 588349a1fe
commit ce86893fa8
9 changed files with 309 additions and 3 deletions

View File

@ -3,6 +3,7 @@ rsi_91x-y += rsi_91x_core.o
rsi_91x-y += rsi_91x_mac80211.o
rsi_91x-y += rsi_91x_mgmt.o
rsi_91x-y += rsi_91x_hal.o
rsi_91x-y += rsi_91x_ps.o
rsi_91x-$(CONFIG_RSI_DEBUGFS) += rsi_91x_debugfs.o
rsi_usb-y += rsi_91x_usb.o rsi_91x_usb_ops.o

View File

@ -111,6 +111,8 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
/* This function prepares descriptor for given data packet */
static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
{
struct rsi_hw *adapter = common->priv;
struct ieee80211_vif *vif;
struct ieee80211_hdr *wh = NULL;
struct ieee80211_tx_info *info;
struct skb_info *tx_params;
@ -148,6 +150,7 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ];
wh = (struct ieee80211_hdr *)&skb->data[header_size];
seq_num = (le16_to_cpu(wh->seq_ctrl) >> 4);
vif = adapter->vifs[0];
data_desc->xtend_desc_size = header_size - FRAME_DESC_SZ;
@ -156,6 +159,10 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
data_desc->mac_flags |= cpu_to_le16(RSI_QOS_ENABLE);
}
if ((vif->type == NL80211_IFTYPE_STATION) &&
(adapter->ps_state == PS_ENABLED))
wh->frame_control |= cpu_to_le16(RSI_SET_PS_ENABLE);
if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) &&
(common->secinfo.security_enable)) {
if (rsi_is_cipher_wep(common))

View File

@ -18,6 +18,7 @@
#include "rsi_debugfs.h"
#include "rsi_mgmt.h"
#include "rsi_common.h"
#include "rsi_ps.h"
static const struct ieee80211_channel rsi_2ghz_channels[] = {
{ .band = NL80211_BAND_2GHZ, .center_freq = 2412,
@ -467,6 +468,8 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw,
{
struct rsi_hw *adapter = hw->priv;
struct rsi_common *common = adapter->priv;
struct ieee80211_vif *vif = adapter->vifs[0];
struct ieee80211_conf *conf = &hw->conf;
int status = -EOPNOTSUPP;
mutex_lock(&common->mutex);
@ -480,6 +483,19 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw,
status = rsi_config_power(hw);
}
/* Power save parameters */
if ((changed & IEEE80211_CONF_CHANGE_PS) &&
(vif->type == NL80211_IFTYPE_STATION)) {
unsigned long flags;
spin_lock_irqsave(&adapter->ps_lock, flags);
if (conf->flags & IEEE80211_CONF_PS)
rsi_enable_ps(adapter);
else
rsi_disable_ps(adapter);
spin_unlock_irqrestore(&adapter->ps_lock, flags);
}
mutex_unlock(&common->mutex);
return status;
@ -522,6 +538,8 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
{
struct rsi_hw *adapter = hw->priv;
struct rsi_common *common = adapter->priv;
struct ieee80211_bss_conf *bss = &vif->bss_conf;
struct ieee80211_conf *conf = &hw->conf;
u16 rx_filter_word = 0;
mutex_lock(&common->mutex);
@ -540,6 +558,8 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
bss_conf->bssid,
bss_conf->qos,
bss_conf->aid);
adapter->ps_info.dtim_interval_duration = bss->dtim_period;
adapter->ps_info.listen_interval = conf->listen_interval;
}
if (changed & BSS_CHANGED_CQM) {
@ -1283,6 +1303,8 @@ int rsi_mac80211_attach(struct rsi_common *common)
ieee80211_hw_set(hw, SIGNAL_DBM);
ieee80211_hw_set(hw, HAS_RATE_CONTROL);
ieee80211_hw_set(hw, AMPDU_AGGREGATION);
ieee80211_hw_set(hw, SUPPORTS_PS);
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
hw->queues = MAX_HW_QUEUES;
hw->extra_tx_headroom = RSI_NEEDED_HEADROOM;

View File

@ -231,6 +231,8 @@ struct rsi_hw *rsi_91x_init(void)
goto err;
}
rsi_default_ps_params(adapter);
spin_lock_init(&adapter->ps_lock);
common->init_done = true;
return adapter;

View File

@ -17,6 +17,7 @@
#include <linux/etherdevice.h>
#include "rsi_mgmt.h"
#include "rsi_common.h"
#include "rsi_ps.h"
static struct bootup_params boot_params_20 = {
.magic_number = cpu_to_le16(0x5aa5),
@ -1396,6 +1397,58 @@ int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word)
return rsi_send_internal_mgmt_frame(common, skb);
}
int rsi_send_ps_request(struct rsi_hw *adapter, bool enable)
{
struct rsi_common *common = adapter->priv;
struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf;
struct rsi_request_ps *ps;
struct rsi_ps_info *ps_info;
struct sk_buff *skb;
int frame_len = sizeof(*ps);
skb = dev_alloc_skb(frame_len);
if (!skb)
return -ENOMEM;
memset(skb->data, 0, frame_len);
ps = (struct rsi_request_ps *)skb->data;
ps_info = &adapter->ps_info;
rsi_set_len_qno(&ps->desc.desc_dword0.len_qno,
(frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q);
ps->desc.desc_dword0.frame_type = WAKEUP_SLEEP_REQUEST;
if (enable) {
ps->ps_sleep.enable = RSI_PS_ENABLE;
ps->desc.desc_dword3.token = cpu_to_le16(RSI_SLEEP_REQUEST);
} else {
ps->ps_sleep.enable = RSI_PS_DISABLE;
ps->desc.desc_dword0.len_qno |= cpu_to_le16(RSI_PS_DISABLE_IND);
ps->desc.desc_dword3.token = cpu_to_le16(RSI_WAKEUP_REQUEST);
}
ps->ps_sleep.sleep_type = ps_info->sleep_type;
ps->ps_sleep.num_bcns_per_lis_int =
cpu_to_le16(ps_info->num_bcns_per_lis_int);
ps->ps_sleep.sleep_duration =
cpu_to_le32(ps_info->deep_sleep_wakeup_period);
if (bss->assoc)
ps->ps_sleep.connected_sleep = RSI_CONNECTED_SLEEP;
else
ps->ps_sleep.connected_sleep = RSI_DEEP_SLEEP;
ps->ps_listen_interval = cpu_to_le32(ps_info->listen_interval);
ps->ps_dtim_interval_duration =
cpu_to_le32(ps_info->dtim_interval_duration);
if (ps_info->listen_interval > ps_info->dtim_interval_duration)
ps->ps_listen_interval = cpu_to_le32(RSI_PS_DISABLE);
ps->ps_num_dtim_intervals = cpu_to_le16(ps_info->num_dtims_per_sleep);
skb_put(skb, frame_len);
return rsi_send_internal_mgmt_frame(common, skb);
}
/**
* rsi_set_antenna() - This fuction send antenna configuration request
* to device
@ -1569,7 +1622,9 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
return 0;
}
break;
case WAKEUP_SLEEP_REQUEST:
rsi_dbg(INFO_ZONE, "Wakeup/Sleep confirmation.\n");
return rsi_handle_ps_confirm(adapter, msg);
default:
rsi_dbg(INFO_ZONE, "%s: Invalid TA confirm pkt received\n",
__func__);

View File

@ -0,0 +1,129 @@
/**
* Copyright (c) 2014 Redpine Signals Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/etherdevice.h>
#include <linux/if.h>
#include <linux/version.h>
#include "rsi_debugfs.h"
#include "rsi_mgmt.h"
#include "rsi_common.h"
#include "rsi_ps.h"
char *str_psstate(enum ps_state state)
{
switch (state) {
case PS_NONE:
return "PS_NONE";
case PS_DISABLE_REQ_SENT:
return "PS_DISABLE_REQ_SENT";
case PS_ENABLE_REQ_SENT:
return "PS_ENABLE_REQ_SENT";
case PS_ENABLED:
return "PS_ENABLED";
default:
return "INVALID_STATE";
}
return "INVALID_STATE";
}
static inline void rsi_modify_ps_state(struct rsi_hw *adapter,
enum ps_state nstate)
{
rsi_dbg(INFO_ZONE, "PS state changed %s => %s\n",
str_psstate(adapter->ps_state),
str_psstate(nstate));
adapter->ps_state = nstate;
}
void rsi_default_ps_params(struct rsi_hw *adapter)
{
struct rsi_ps_info *ps_info = &adapter->ps_info;
ps_info->enabled = true;
ps_info->sleep_type = RSI_SLEEP_TYPE_LP;
ps_info->tx_threshold = 0;
ps_info->rx_threshold = 0;
ps_info->tx_hysterisis = 0;
ps_info->rx_hysterisis = 0;
ps_info->monitor_interval = 0;
ps_info->listen_interval = RSI_DEF_LISTEN_INTERVAL;
ps_info->num_bcns_per_lis_int = 0;
ps_info->dtim_interval_duration = 0;
ps_info->num_dtims_per_sleep = 0;
ps_info->deep_sleep_wakeup_period = RSI_DEF_DS_WAKEUP_PERIOD;
}
void rsi_enable_ps(struct rsi_hw *adapter)
{
if (adapter->ps_state != PS_NONE) {
rsi_dbg(ERR_ZONE,
"%s: Cannot accept enable PS in %s state\n",
__func__, str_psstate(adapter->ps_state));
return;
}
if (rsi_send_ps_request(adapter, true)) {
rsi_dbg(ERR_ZONE,
"%s: Failed to send PS request to device\n",
__func__);
return;
}
rsi_modify_ps_state(adapter, PS_ENABLE_REQ_SENT);
}
void rsi_disable_ps(struct rsi_hw *adapter)
{
if (adapter->ps_state != PS_ENABLED) {
rsi_dbg(ERR_ZONE,
"%s: Cannot accept disable PS in %s state\n",
__func__, str_psstate(adapter->ps_state));
return;
}
if (rsi_send_ps_request(adapter, false)) {
rsi_dbg(ERR_ZONE,
"%s: Failed to send PS request to device\n",
__func__);
return;
}
rsi_modify_ps_state(adapter, PS_DISABLE_REQ_SENT);
}
int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg)
{
u16 cfm_type = get_unaligned_le16(msg + PS_CONFIRM_INDEX);
switch (cfm_type) {
case RSI_SLEEP_REQUEST:
if (adapter->ps_state == PS_ENABLE_REQ_SENT)
rsi_modify_ps_state(adapter, PS_ENABLED);
break;
case RSI_WAKEUP_REQUEST:
if (adapter->ps_state == PS_DISABLE_REQ_SENT)
rsi_modify_ps_state(adapter, PS_NONE);
break;
default:
rsi_dbg(ERR_ZONE,
"Invalid PS confirm type %x in state %s\n",
cfm_type, str_psstate(adapter->ps_state));
return -1;
}
return 0;
}

View File

@ -21,6 +21,10 @@
#include <linux/skbuff.h>
#include <net/mac80211.h>
struct rsi_hw;
#include "rsi_ps.h"
#define ERR_ZONE BIT(0) /* For Error Msgs */
#define INFO_ZONE BIT(1) /* For General Status Msgs */
#define INIT_ZONE BIT(2) /* For Driver Init Seq Msgs */
@ -177,8 +181,6 @@ enum rsi_dfs_regions {
RSI_REGION_WORLD
};
struct rsi_hw;
struct rsi_common {
struct rsi_hw *priv;
struct vif_priv vif_info[RSI_MAX_VIFS];
@ -282,6 +284,9 @@ struct rsi_hw {
enum host_intf rsi_host_intf;
u16 block_size;
enum ps_state ps_state;
struct rsi_ps_info ps_info;
spinlock_t ps_lock; /*To protect power save config*/
u32 usb_buffer_status_reg;
#ifdef CONFIG_RSI_DEBUGFS
struct rsi_debugfs *dfsentry;

View File

@ -69,6 +69,7 @@
#define RSI_QOS_ENABLE BIT(12)
#define RSI_REKEY_PURPOSE BIT(13)
#define RSI_ENCRYPT_PKT BIT(15)
#define RSI_SET_PS_ENABLE BIT(12)
#define RSI_CMDDESC_40MHZ BIT(4)
#define RSI_CMDDESC_UPPER_20_ENABLE BIT(5)
@ -172,6 +173,14 @@
#define RSI_BEACON_INTERVAL 200
#define RSI_DTIM_COUNT 2
#define RSI_PS_DISABLE_IND BIT(15)
#define RSI_PS_ENABLE 1
#define RSI_PS_DISABLE 0
#define RSI_DEEP_SLEEP 1
#define RSI_CONNECTED_SLEEP 2
#define RSI_SLEEP_REQUEST 1
#define RSI_WAKEUP_REQUEST 2
enum opmode {
STA_OPMODE = 1,
AP_OPMODE = 2
@ -519,6 +528,18 @@ struct rsi_eeprom_read_frame {
__le16 reserved3;
} __packed;
struct rsi_request_ps {
struct rsi_cmd_desc desc;
struct ps_sleep_params ps_sleep;
u8 ps_mimic_support;
u8 ps_uapsd_acs;
u8 ps_uapsd_wakeup_period;
u8 reserved;
__le32 ps_listen_interval;
__le32 ps_dtim_interval_duration;
__le16 ps_num_dtim_intervals;
} __packed;
static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
{
return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12;

View File

@ -0,0 +1,64 @@
/**
* Copyright (c) 2017 Redpine Signals Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __RSI_PS_H__
#define __RSI_PS_H__
#define PS_CONFIRM_INDEX 12
#define RSI_DEF_DS_WAKEUP_PERIOD 200
#define RSI_DEF_LISTEN_INTERVAL 200
#define RSI_SLEEP_TYPE_LP 1
enum ps_state {
PS_NONE = 0,
PS_ENABLE_REQ_SENT = 1,
PS_DISABLE_REQ_SENT = 2,
PS_ENABLED = 3
};
struct ps_sleep_params {
u8 enable;
u8 sleep_type;
u8 connected_sleep;
u8 reserved1;
__le16 num_bcns_per_lis_int;
__le16 wakeup_type;
__le32 sleep_duration;
} __packed;
struct rsi_ps_info {
u8 enabled;
u8 sleep_type;
u8 tx_threshold;
u8 rx_threshold;
u8 tx_hysterisis;
u8 rx_hysterisis;
u16 monitor_interval;
u32 listen_interval;
u16 num_bcns_per_lis_int;
u32 dtim_interval_duration;
u16 num_dtims_per_sleep;
u32 deep_sleep_wakeup_period;
} __packed;
char *str_psstate(enum ps_state state);
void rsi_enable_ps(struct rsi_hw *adapter);
void rsi_disable_ps(struct rsi_hw *adapter);
int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg);
void rsi_default_ps_params(struct rsi_hw *hw);
int rsi_send_ps_request(struct rsi_hw *adapter, bool enable);
void rsi_conf_uapsd(struct rsi_hw *adapter);
#endif