Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mwifiex: add WOWLAN support

Currently 'magic-packet' and 'patterns' options in 'iw wowlan'
command are supported.

Appropriate packet filters for wowlan are configured in firmware
based on provided patterns and/or magic-packet option.

For examples,

wake-on ARP request for 192.168.0.100:
iw phy0 wowlan enable patterns ff:ff:ff:ff:ff:ff 20+08:06
46+c0:a8:00:64

wake-on RX packets sent from IP address 192.168.0.88:
iw phy0 wowlan enable patterns 34+c0:a8:00:58

wake-on RX packets with TCP destination port 80
iw phy0 wowlan enable patterns 44+50

wake-on MagicPacket:
iw phy0 wowlan enable magic-packet

wake-on MagicPacket or patterns:
iw phy0 wowlan enable magic-packet patterns 12+00:11:22:33:44:55
18+00:50:43:21

wake-on IPv4 multicast packets:
iw phy0 wowlan enable patterns 01:00:5e

wake-on IPv6 multicast packets:
iw phy0 wowlan enable patterns 33:33

disable all wowlan options
iw phy0 wowlan disable

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>

authored by

Amitkumar Karwar and committed by
John W. Linville
7da060c1 0d7f53e3

+292
+156
drivers/net/wireless/mwifiex/cfg80211.c
··· 2294 2294 } 2295 2295 EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); 2296 2296 2297 + #ifdef CONFIG_PM 2298 + static bool 2299 + mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat, 2300 + s8 *byte_seq) 2301 + { 2302 + int j, k, valid_byte_cnt = 0; 2303 + bool dont_care_byte = false; 2304 + 2305 + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { 2306 + for (k = 0; k < 8; k++) { 2307 + if (pat->mask[j] & 1 << k) { 2308 + memcpy(byte_seq + valid_byte_cnt, 2309 + &pat->pattern[j * 8 + k], 1); 2310 + valid_byte_cnt++; 2311 + if (dont_care_byte) 2312 + return false; 2313 + } else { 2314 + if (valid_byte_cnt) 2315 + dont_care_byte = true; 2316 + } 2317 + 2318 + if (valid_byte_cnt > MAX_BYTESEQ) 2319 + return false; 2320 + } 2321 + } 2322 + 2323 + byte_seq[MAX_BYTESEQ] = valid_byte_cnt; 2324 + 2325 + return true; 2326 + } 2327 + 2328 + static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, 2329 + struct cfg80211_wowlan *wowlan) 2330 + { 2331 + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); 2332 + struct mwifiex_ds_mef_cfg mef_cfg; 2333 + struct mwifiex_mef_entry *mef_entry; 2334 + int i, filt_num = 0, ret; 2335 + bool first_pat = true; 2336 + u8 byte_seq[MAX_BYTESEQ + 1]; 2337 + const u8 ipv4_mc_mac[] = {0x33, 0x33}; 2338 + const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; 2339 + struct mwifiex_private *priv = 2340 + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); 2341 + 2342 + if (!wowlan) { 2343 + dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n"); 2344 + return 0; 2345 + } 2346 + 2347 + if (!priv->media_connected) { 2348 + dev_warn(adapter->dev, 2349 + "Can not configure WOWLAN in disconnected state\n"); 2350 + return 0; 2351 + } 2352 + 2353 + memset(&mef_cfg, 0, sizeof(mef_cfg)); 2354 + mef_cfg.num_entries = 1; 2355 + mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL); 2356 + mef_cfg.mef_entry = mef_entry; 2357 + mef_entry->mode = MEF_MODE_HOST_SLEEP; 2358 + mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; 2359 + 2360 + for (i = 0; i < wowlan->n_patterns; i++) { 2361 + memset(byte_seq, 0, sizeof(byte_seq)); 2362 + if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], 2363 + byte_seq)) { 2364 + wiphy_err(wiphy, "Pattern not supported\n"); 2365 + kfree(mef_entry); 2366 + return -EOPNOTSUPP; 2367 + } 2368 + 2369 + if (!wowlan->patterns[i].pkt_offset) { 2370 + if (!(byte_seq[0] & 0x01) && 2371 + (byte_seq[MAX_BYTESEQ] == 1)) { 2372 + mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; 2373 + continue; 2374 + } else if (is_broadcast_ether_addr(byte_seq)) { 2375 + mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST; 2376 + continue; 2377 + } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && 2378 + (byte_seq[MAX_BYTESEQ] == 2)) || 2379 + (!memcmp(byte_seq, ipv6_mc_mac, 3) && 2380 + (byte_seq[MAX_BYTESEQ] == 3))) { 2381 + mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST; 2382 + continue; 2383 + } 2384 + } 2385 + 2386 + mef_entry->filter[filt_num].repeat = 1; 2387 + mef_entry->filter[filt_num].offset = 2388 + wowlan->patterns[i].pkt_offset; 2389 + memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, 2390 + sizeof(byte_seq)); 2391 + mef_entry->filter[filt_num].filt_type = TYPE_EQ; 2392 + 2393 + if (first_pat) 2394 + first_pat = false; 2395 + else 2396 + mef_entry->filter[filt_num].filt_action = TYPE_AND; 2397 + 2398 + filt_num++; 2399 + } 2400 + 2401 + if (wowlan->magic_pkt) { 2402 + mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; 2403 + mef_entry->filter[filt_num].repeat = 16; 2404 + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, 2405 + ETH_ALEN); 2406 + mef_entry->filter[filt_num].byte_seq[MAX_BYTESEQ] = ETH_ALEN; 2407 + mef_entry->filter[filt_num].offset = 14; 2408 + mef_entry->filter[filt_num].filt_type = TYPE_EQ; 2409 + if (filt_num) 2410 + mef_entry->filter[filt_num].filt_action = TYPE_OR; 2411 + } 2412 + 2413 + if (!mef_cfg.criteria) 2414 + mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | 2415 + MWIFIEX_CRITERIA_UNICAST | 2416 + MWIFIEX_CRITERIA_MULTICAST; 2417 + 2418 + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MEF_CFG, 2419 + HostCmd_ACT_GEN_SET, 0, 2420 + &mef_cfg); 2421 + 2422 + kfree(mef_entry); 2423 + return ret; 2424 + } 2425 + 2426 + static int mwifiex_cfg80211_resume(struct wiphy *wiphy) 2427 + { 2428 + return 0; 2429 + } 2430 + 2431 + static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy, 2432 + bool enabled) 2433 + { 2434 + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); 2435 + 2436 + device_set_wakeup_enable(adapter->dev, enabled); 2437 + } 2438 + #endif 2439 + 2297 2440 /* station cfg80211 operations */ 2298 2441 static struct cfg80211_ops mwifiex_cfg80211_ops = { 2299 2442 .add_virtual_intf = mwifiex_add_virtual_intf, ··· 2465 2322 .change_beacon = mwifiex_cfg80211_change_beacon, 2466 2323 .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, 2467 2324 .set_antenna = mwifiex_cfg80211_set_antenna, 2325 + #ifdef CONFIG_PM 2326 + .suspend = mwifiex_cfg80211_suspend, 2327 + .resume = mwifiex_cfg80211_resume, 2328 + .set_wakeup = mwifiex_cfg80211_set_wakeup, 2329 + #endif 2468 2330 }; 2469 2331 2470 2332 /* ··· 2527 2379 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; 2528 2380 2529 2381 wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom); 2382 + 2383 + #ifdef CONFIG_PM 2384 + wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT; 2385 + wiphy->wowlan.n_patterns = MWIFIEX_MAX_FILTERS; 2386 + wiphy->wowlan.pattern_min_len = 1; 2387 + wiphy->wowlan.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN; 2388 + wiphy->wowlan.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN; 2389 + #endif 2530 2390 2531 2391 wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | 2532 2392 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
+32
drivers/net/wireless/mwifiex/fw.h
··· 300 300 #define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f 301 301 #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 302 302 #define HostCmd_CMD_VERSION_EXT 0x0097 303 + #define HostCmd_CMD_MEF_CFG 0x009a 303 304 #define HostCmd_CMD_RSSI_INFO 0x00a4 304 305 #define HostCmd_CMD_FUNC_INIT 0x00a9 305 306 #define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa ··· 473 472 474 473 #define EVENT_GET_BSS_TYPE(event_cause) \ 475 474 (((event_cause) >> 24) & 0x00ff) 475 + 476 + #define MWIFIEX_MAX_PATTERN_LEN 20 477 + #define MWIFIEX_MAX_OFFSET_LEN 50 478 + #define STACK_NBYTES 100 479 + #define TYPE_DNUM 1 480 + #define TYPE_BYTESEQ 2 481 + #define MAX_OPERAND 0x40 482 + #define TYPE_EQ (MAX_OPERAND+1) 483 + #define TYPE_EQ_DNUM (MAX_OPERAND+2) 484 + #define TYPE_EQ_BIT (MAX_OPERAND+3) 485 + #define TYPE_AND (MAX_OPERAND+4) 486 + #define TYPE_OR (MAX_OPERAND+5) 487 + #define MEF_MODE_HOST_SLEEP 1 488 + #define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 489 + #define MWIFIEX_CRITERIA_BROADCAST BIT(0) 490 + #define MWIFIEX_CRITERIA_UNICAST BIT(1) 491 + #define MWIFIEX_CRITERIA_MULTICAST BIT(3) 476 492 477 493 struct mwifiex_ie_types_header { 478 494 __le16 type; ··· 1521 1503 __le16 use_g_rate_protect; 1522 1504 } __packed; 1523 1505 1506 + struct mwifiex_fw_mef_entry { 1507 + u8 mode; 1508 + u8 action; 1509 + __le16 exprsize; 1510 + u8 expr[0]; 1511 + } __packed; 1512 + 1513 + struct host_cmd_ds_mef_cfg { 1514 + __le32 criteria; 1515 + __le16 num_entries; 1516 + struct mwifiex_fw_mef_entry mef_entry[0]; 1517 + } __packed; 1518 + 1524 1519 #define CONNECTION_TYPE_INFRA 0 1525 1520 #define CONNECTION_TYPE_ADHOC 1 1526 1521 #define CONNECTION_TYPE_AP 2 ··· 1638 1607 struct host_cmd_ds_remain_on_chan roc_cfg; 1639 1608 struct host_cmd_ds_p2p_mode_cfg mode_cfg; 1640 1609 struct host_cmd_ds_802_11_ibss_status ibss_coalescing; 1610 + struct host_cmd_ds_mef_cfg mef_cfg; 1641 1611 struct host_cmd_ds_mac_reg_access mac_reg; 1642 1612 struct host_cmd_ds_bbp_reg_access bbp_reg; 1643 1613 struct host_cmd_ds_rf_reg_access rf_reg;
+23
drivers/net/wireless/mwifiex/ioctl.h
··· 354 354 struct subsc_evt_cfg bcn_h_rssi_cfg; 355 355 }; 356 356 357 + #define MAX_BYTESEQ 6 /* non-adjustable */ 358 + #define MWIFIEX_MAX_FILTERS 10 359 + 360 + struct mwifiex_mef_filter { 361 + u16 repeat; 362 + u16 offset; 363 + s8 byte_seq[MAX_BYTESEQ + 1]; 364 + u8 filt_type; 365 + u8 filt_action; 366 + }; 367 + 368 + struct mwifiex_mef_entry { 369 + u8 mode; 370 + u8 action; 371 + struct mwifiex_mef_filter filter[MWIFIEX_MAX_FILTERS]; 372 + }; 373 + 374 + struct mwifiex_ds_mef_cfg { 375 + u32 criteria; 376 + u16 num_entries; 377 + struct mwifiex_mef_entry *mef_entry; 378 + }; 379 + 357 380 #define MWIFIEX_MAX_VSIE_LEN (256) 358 381 #define MWIFIEX_MAX_VSIE_NUM (8) 359 382 #define MWIFIEX_VSIE_MASK_CLEAR 0x00
+2
drivers/net/wireless/mwifiex/main.h
··· 1098 1098 1099 1099 void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config); 1100 1100 1101 + int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter); 1102 + 1101 1103 int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, 1102 1104 struct cfg80211_beacon_data *data); 1103 1105 int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
+77
drivers/net/wireless/mwifiex/sta_cmd.c
··· 1059 1059 return 0; 1060 1060 } 1061 1061 1062 + static int 1063 + mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, 1064 + struct mwifiex_mef_entry *mef_entry, 1065 + u8 **buffer) 1066 + { 1067 + struct mwifiex_mef_filter *filter = mef_entry->filter; 1068 + int i, byte_len; 1069 + u8 *stack_ptr = *buffer; 1070 + 1071 + for (i = 0; i < MWIFIEX_MAX_FILTERS; i++) { 1072 + filter = &mef_entry->filter[i]; 1073 + if (!filter->filt_type) 1074 + break; 1075 + *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat); 1076 + stack_ptr += 4; 1077 + *stack_ptr = TYPE_DNUM; 1078 + stack_ptr += 1; 1079 + 1080 + byte_len = filter->byte_seq[MAX_BYTESEQ]; 1081 + memcpy(stack_ptr, filter->byte_seq, byte_len); 1082 + stack_ptr += byte_len; 1083 + *stack_ptr = byte_len; 1084 + stack_ptr += 1; 1085 + *stack_ptr = TYPE_BYTESEQ; 1086 + stack_ptr += 1; 1087 + 1088 + *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset); 1089 + stack_ptr += 4; 1090 + *stack_ptr = TYPE_DNUM; 1091 + stack_ptr += 1; 1092 + 1093 + *stack_ptr = filter->filt_type; 1094 + stack_ptr += 1; 1095 + 1096 + if (filter->filt_action) { 1097 + *stack_ptr = filter->filt_action; 1098 + stack_ptr += 1; 1099 + } 1100 + 1101 + if (stack_ptr - *buffer > STACK_NBYTES) 1102 + return -1; 1103 + } 1104 + 1105 + *buffer = stack_ptr; 1106 + return 0; 1107 + } 1108 + 1109 + static int 1110 + mwifiex_cmd_mef_cfg(struct mwifiex_private *priv, 1111 + struct host_cmd_ds_command *cmd, 1112 + struct mwifiex_ds_mef_cfg *mef) 1113 + { 1114 + struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg; 1115 + u8 *pos = (u8 *)mef_cfg; 1116 + 1117 + cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG); 1118 + 1119 + mef_cfg->criteria = cpu_to_le32(mef->criteria); 1120 + mef_cfg->num_entries = cpu_to_le16(mef->num_entries); 1121 + pos += sizeof(*mef_cfg); 1122 + mef_cfg->mef_entry->mode = mef->mef_entry->mode; 1123 + mef_cfg->mef_entry->action = mef->mef_entry->action; 1124 + pos += sizeof(*(mef_cfg->mef_entry)); 1125 + 1126 + if (mwifiex_cmd_append_rpn_expression(priv, mef->mef_entry, &pos)) 1127 + return -1; 1128 + 1129 + mef_cfg->mef_entry->exprsize = 1130 + cpu_to_le16(pos - mef_cfg->mef_entry->expr); 1131 + cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN); 1132 + 1133 + return 0; 1134 + } 1135 + 1062 1136 /* 1063 1137 * This function prepares the commands before sending them to the firmware. 1064 1138 * ··· 1346 1272 break; 1347 1273 case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: 1348 1274 ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf); 1275 + break; 1276 + case HostCmd_CMD_MEF_CFG: 1277 + ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf); 1349 1278 break; 1350 1279 default: 1351 1280 dev_err(priv->adapter->dev,
+2
drivers/net/wireless/mwifiex/sta_cmdresp.c
··· 976 976 case HostCmd_CMD_UAP_BSS_STOP: 977 977 priv->bss_started = 0; 978 978 break; 979 + case HostCmd_CMD_MEF_CFG: 980 + break; 979 981 default: 980 982 dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", 981 983 resp->command);