mwifiex: add packet coalesce support

Coalesce filters are configured in firmware based on settings
received from cfg80211.

Packet type which is required by firmware is determined based on
provided patterns in a rule:

Unicast: if pattern '01' with offset 0 is found
Multicast: if pattern '33:33' or '01:00:5e' with offset 0 is found
Broadcast: if pattern 'ff:ff:ff:ff' with offset 0 is found

Some example coalesce configuration files:

1) Coalesce Rx data packets from 192.168.0.88
mac address of our device is 00:50:43:21:53:7A
Source IP address offset comes out as 52 after following
calculations:
    32 bytes of HW 802.11 header + 8 bytes LLC +
    12 bytes in IPV4 header till source IP address
Destination mac is at offset 6 in HW header.

delay=100
condition=1
patterns=01,6+00:50:43:22,10+53:7A,52+c0:a8:00:58

2) Coalesce all broadcast and multicast packets(Multiple packet
types are not allowed in a single rule. Hence created separate
rules)

delay=400
condition=1
patterns=33:33
delay=400
condition=1
patterns=ff:ff:ff:ff

Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Amitkumar Karwar 2013-08-05 18:52:00 -07:00 committed by John W. Linville
parent afd84de473
commit 562fc5b30f
5 changed files with 254 additions and 0 deletions

View File

@ -2455,6 +2455,119 @@ static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy,
} }
#endif #endif
static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq)
{
const u8 ipv4_mc_mac[] = {0x33, 0x33};
const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff};
if ((byte_seq[0] & 0x01) &&
(byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1))
return PACKET_TYPE_UNICAST;
else if (!memcmp(byte_seq, bc_mac, 4))
return PACKET_TYPE_BROADCAST;
else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) ||
(!memcmp(byte_seq, ipv6_mc_mac, 3) &&
byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3))
return PACKET_TYPE_MULTICAST;
return 0;
}
static int
mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv,
struct cfg80211_coalesce_rules *crule,
struct mwifiex_coalesce_rule *mrule)
{
u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1];
struct filt_field_param *param;
int i;
mrule->max_coalescing_delay = crule->delay;
param = mrule->params;
for (i = 0; i < crule->n_patterns; i++) {
memset(byte_seq, 0, sizeof(byte_seq));
if (!mwifiex_is_pattern_supported(&crule->patterns[i],
byte_seq,
MWIFIEX_COALESCE_MAX_BYTESEQ)) {
dev_err(priv->adapter->dev, "Pattern not supported\n");
return -EOPNOTSUPP;
}
if (!crule->patterns[i].pkt_offset) {
u8 pkt_type;
pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq);
if (pkt_type && mrule->pkt_type) {
dev_err(priv->adapter->dev,
"Multiple packet types not allowed\n");
return -EOPNOTSUPP;
} else if (pkt_type) {
mrule->pkt_type = pkt_type;
continue;
}
}
if (crule->condition == NL80211_COALESCE_CONDITION_MATCH)
param->operation = RECV_FILTER_MATCH_TYPE_EQ;
else
param->operation = RECV_FILTER_MATCH_TYPE_NE;
param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ];
memcpy(param->operand_byte_stream, byte_seq,
param->operand_len);
param->offset = crule->patterns[i].pkt_offset;
param++;
mrule->num_of_fields++;
}
if (!mrule->pkt_type) {
dev_err(priv->adapter->dev,
"Packet type can not be determined\n");
return -EOPNOTSUPP;
}
return 0;
}
static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
struct cfg80211_coalesce *coalesce)
{
struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
int i, ret;
struct mwifiex_ds_coalesce_cfg coalesce_cfg;
struct mwifiex_private *priv =
mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
memset(&coalesce_cfg, 0, sizeof(coalesce_cfg));
if (!coalesce) {
dev_dbg(adapter->dev,
"Disable coalesce and reset all previous rules\n");
return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
HostCmd_ACT_GEN_SET, 0,
&coalesce_cfg);
}
coalesce_cfg.num_of_rules = coalesce->n_rules;
for (i = 0; i < coalesce->n_rules; i++) {
ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i],
&coalesce_cfg.rule[i]);
if (ret) {
dev_err(priv->adapter->dev,
"Recheck the patterns provided for rule %d\n",
i + 1);
return ret;
}
}
return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
HostCmd_ACT_GEN_SET, 0, &coalesce_cfg);
}
/* station cfg80211 operations */ /* station cfg80211 operations */
static struct cfg80211_ops mwifiex_cfg80211_ops = { static struct cfg80211_ops mwifiex_cfg80211_ops = {
.add_virtual_intf = mwifiex_add_virtual_intf, .add_virtual_intf = mwifiex_add_virtual_intf,
@ -2488,6 +2601,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.suspend = mwifiex_cfg80211_suspend, .suspend = mwifiex_cfg80211_suspend,
.resume = mwifiex_cfg80211_resume, .resume = mwifiex_cfg80211_resume,
.set_wakeup = mwifiex_cfg80211_set_wakeup, .set_wakeup = mwifiex_cfg80211_set_wakeup,
.set_coalesce = mwifiex_cfg80211_set_coalesce,
#endif #endif
}; };
@ -2512,6 +2626,15 @@ static bool mwifiex_is_valid_alpha2(const char *alpha2)
return false; return false;
} }
static const struct wiphy_coalesce_support mwifiex_coalesce_support = {
.n_rules = MWIFIEX_COALESCE_MAX_RULES,
.max_delay = MWIFIEX_MAX_COALESCING_DELAY,
.n_patterns = MWIFIEX_COALESCE_MAX_FILTERS,
.pattern_min_len = 1,
.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
};
/* /*
* This function registers the device with CFG802.11 subsystem. * This function registers the device with CFG802.11 subsystem.
* *
@ -2573,6 +2696,8 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
wiphy->wowlan = &mwifiex_wowlan_support; wiphy->wowlan = &mwifiex_wowlan_support;
#endif #endif
wiphy->coalesce = &mwifiex_coalesce_support;
wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;

View File

@ -153,6 +153,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123) #define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123)
#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) #define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145)
#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) #define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146)
#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154)
#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048
@ -294,6 +295,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed
#define HostCmd_CMD_SET_BSS_MODE 0x00f7 #define HostCmd_CMD_SET_BSS_MODE 0x00f7
#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa #define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa
#define HostCmd_CMD_COALESCE_CFG 0x010a
#define HostCmd_CMD_MGMT_FRAME_REG 0x010c #define HostCmd_CMD_MGMT_FRAME_REG 0x010c
#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d #define HostCmd_CMD_REMAIN_ON_CHAN 0x010d
#define HostCmd_CMD_11AC_CFG 0x0112 #define HostCmd_CMD_11AC_CFG 0x0112
@ -1596,6 +1598,27 @@ struct host_cmd_ds_802_11_cfg_data {
__le16 data_len; __le16 data_len;
} __packed; } __packed;
struct coalesce_filt_field_param {
u8 operation;
u8 operand_len;
__le16 offset;
u8 operand_byte_stream[4];
};
struct coalesce_receive_filt_rule {
struct mwifiex_ie_types_header header;
u8 num_of_fields;
u8 pkt_type;
__le16 max_coalescing_delay;
struct coalesce_filt_field_param params[0];
} __packed;
struct host_cmd_ds_coalesce_cfg {
__le16 action;
__le16 num_of_rules;
struct coalesce_receive_filt_rule rule[0];
} __packed;
struct host_cmd_ds_command { struct host_cmd_ds_command {
__le16 command; __le16 command;
__le16 size; __le16 size;
@ -1656,6 +1679,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_sta_deauth sta_deauth; struct host_cmd_ds_sta_deauth sta_deauth;
struct host_cmd_11ac_vht_cfg vht_cfg; struct host_cmd_11ac_vht_cfg vht_cfg;
struct host_cmd_ds_802_11_cfg_data cfg_data; struct host_cmd_ds_802_11_cfg_data cfg_data;
struct host_cmd_ds_coalesce_cfg coalesce_cfg;
} params; } params;
} __packed; } __packed;

View File

@ -397,4 +397,39 @@ enum {
MWIFIEX_FUNC_SHUTDOWN, MWIFIEX_FUNC_SHUTDOWN,
}; };
enum COALESCE_OPERATION {
RECV_FILTER_MATCH_TYPE_EQ = 0x80,
RECV_FILTER_MATCH_TYPE_NE,
};
enum COALESCE_PACKET_TYPE {
PACKET_TYPE_UNICAST = 1,
PACKET_TYPE_MULTICAST = 2,
PACKET_TYPE_BROADCAST = 3
};
#define MWIFIEX_COALESCE_MAX_RULES 8
#define MWIFIEX_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */
#define MWIFIEX_COALESCE_MAX_FILTERS 4
#define MWIFIEX_MAX_COALESCING_DELAY 100 /* in msecs */
struct filt_field_param {
u8 operation;
u8 operand_len;
u16 offset;
u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ];
};
struct mwifiex_coalesce_rule {
u16 max_coalescing_delay;
u8 num_of_fields;
u8 pkt_type;
struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS];
};
struct mwifiex_ds_coalesce_cfg {
u16 num_of_rules;
struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES];
};
#endif /* !_MWIFIEX_IOCTL_H_ */ #endif /* !_MWIFIEX_IOCTL_H_ */

View File

@ -1184,6 +1184,70 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
return 0; return 0;
} }
static int
mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
u16 cmd_action, void *data_buf)
{
struct host_cmd_ds_coalesce_cfg *coalesce_cfg =
&cmd->params.coalesce_cfg;
struct mwifiex_ds_coalesce_cfg *cfg = data_buf;
struct coalesce_filt_field_param *param;
u16 cnt, idx, length;
struct coalesce_receive_filt_rule *rule;
cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG);
cmd->size = cpu_to_le16(S_DS_GEN);
coalesce_cfg->action = cpu_to_le16(cmd_action);
coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules);
rule = coalesce_cfg->rule;
for (cnt = 0; cnt < cfg->num_of_rules; cnt++) {
rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE);
rule->max_coalescing_delay =
cpu_to_le16(cfg->rule[cnt].max_coalescing_delay);
rule->pkt_type = cfg->rule[cnt].pkt_type;
rule->num_of_fields = cfg->rule[cnt].num_of_fields;
length = 0;
param = rule->params;
for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) {
param->operation = cfg->rule[cnt].params[idx].operation;
param->operand_len =
cfg->rule[cnt].params[idx].operand_len;
param->offset =
cpu_to_le16(cfg->rule[cnt].params[idx].offset);
memcpy(param->operand_byte_stream,
cfg->rule[cnt].params[idx].operand_byte_stream,
param->operand_len);
length += sizeof(struct coalesce_filt_field_param);
param++;
}
/* Total rule length is sizeof max_coalescing_delay(u16),
* num_of_fields(u8), pkt_type(u8) and total length of the all
* params
*/
rule->header.len = cpu_to_le16(length + sizeof(u16) +
sizeof(u8) + sizeof(u8));
/* Add the rule length to the command size*/
le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) +
sizeof(struct mwifiex_ie_types_header));
rule = (void *)((u8 *)rule->params + length);
}
/* Add sizeof action, num_of_rules to total command length */
le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16));
return 0;
}
/* /*
* This function prepares the commands before sending them to the firmware. * This function prepares the commands before sending them to the firmware.
* *
@ -1407,6 +1471,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_MEF_CFG: case HostCmd_CMD_MEF_CFG:
ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf); ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf);
break; break;
case HostCmd_CMD_COALESCE_CFG:
ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action,
data_buf);
break;
default: default:
dev_err(priv->adapter->dev, dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd- %#x\n", cmd_no); "PREP_CMD: unknown cmd- %#x\n", cmd_no);

View File

@ -997,6 +997,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
break; break;
case HostCmd_CMD_MEF_CFG: case HostCmd_CMD_MEF_CFG:
break; break;
case HostCmd_CMD_COALESCE_CFG:
break;
default: default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
resp->command); resp->command);