diff --git a/drivers/net/wireless/rsi/Makefile b/drivers/net/wireless/rsi/Makefile index a475c813674a..ebb89965997a 100644 --- a/drivers/net/wireless/rsi/Makefile +++ b/drivers/net/wireless/rsi/Makefile @@ -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 diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index b0a7a1511aee..4addcc0826db 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -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)) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 193f9227fdb3..16a0fd0f519a 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -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; diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c index bb0febb17be0..3e1e80888d98 100644 --- a/drivers/net/wireless/rsi/rsi_91x_main.c +++ b/drivers/net/wireless/rsi/rsi_91x_main.c @@ -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; diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index e00d4edec2ba..f76b34679df8 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -17,6 +17,7 @@ #include #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__); diff --git a/drivers/net/wireless/rsi/rsi_91x_ps.c b/drivers/net/wireless/rsi/rsi_91x_ps.c new file mode 100644 index 000000000000..25e8f853837b --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_ps.c @@ -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 +#include +#include +#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; +} diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 6a8e8e7ed1fb..9aada0b73108 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -21,6 +21,10 @@ #include #include +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; diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 1060edcb2a96..c5d114d67c83 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -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; diff --git a/drivers/net/wireless/rsi/rsi_ps.h b/drivers/net/wireless/rsi/rsi_ps.h new file mode 100644 index 000000000000..d8475873df36 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_ps.h @@ -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