mirror of https://gitee.com/openkylin/linux.git
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:
parent
588349a1fe
commit
ce86893fa8
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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__);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue