mac80211_hwsim: Add support for client PS mode testing
RFC 1/1 mac80211_hwsim: Add support for client PS mode testing
This introduces a debugfs file (mac80211_hwsim/phy#/ps) that can be used to force a simulated radio into power save mode. Following value can be written into this file to change PS mode:
0 = power save disabled (constantly awake)
1 = power save enabled (drop all frames; do not send PS-Poll) 2 = power save enabled (send PS-Poll frames automatically to receive
buffered unicast frames); not yet fully implemented
3 = manual PS-Poll trigger (send a single PS-Poll frame)
Two different behavior for power save mode processing can be tested:
- move between modes 1 and 0 (i.e., receive all buffered frames at a time)
- move to mode 1 and use manual PS-Poll frames (write 3 to the 'ps' debugfs file) to fetch power save buffered frames one at a time
Mode 2 (automatic PS-Poll) does not yet parse Beacon frames, but eventually, it should take a look at TIM IE and send PS-Poll if a traffic bit is set for our AID.
drivers/net/wireless/mac80211_hwsim.c +190 -0
@@ -21,6 +21,7 @@#include <linux/if_arp.h> #include <linux/rtnetlink.h> #include <linux/etherdevice.h> +#include <linux/debugfs.h> MODULE_AUTHOR ("Jouni Malinen" );MODULE_DESCRIPTION ("Software simulator of 802.11 radio(s) for mac80211" );
@@ -30,8 +31,13 @@module_param (radios, int,0444 );MODULE_PARM_DESC (radios,"Number of simulated radios" ); +static struct dentry *hwsim_debugfs; +struct hwsim_vif_priv {u32 magic; +u8 bssid[ETH_ALEN ]; +bool assoc; +u16 aid; };#define HWSIM_VIF_MAGIC 0x69537748
@@ -132,6 +138,12 @@unsigned int rx_filter;int started;struct timer_list beacon_timer; +enum ps_mode { +PS_DISABLED ,PS_ENABLED ,PS_AUTO_POLL ,PS_MANUAL_POLL + } ps; +bool ps_poll_pending; +struct dentry *debugfs; +struct dentry *debugfs_ps; };
@@ -196,6 +208,34 @@ } +static bool hwsim_ps_rx_ok (struct mac80211_hwsim_data *data, +struct sk_buff *skb) +{ +switch (data->ps) { +case PS_DISABLED : +return true; +case PS_ENABLED : +return false; +case PS_AUTO_POLL : +/* TODO: accept (some) Beacons by default and other frames only +* if pending PS-Poll has been sent */ +return true; +case PS_MANUAL_POLL : +/* Allow unicast frames to own address if there is a pending +* PS-Poll */ +if (data->ps_poll_pending && +memcmp (data->hw->wiphy->perm_addr, skb->data +4 , +ETH_ALEN ) ==0 ) { + data->ps_poll_pending = false; +return true; + } +return false; + } + +return true; +} + +static bool mac80211_hwsim_tx_frame (struct ieee80211_hw *hw,struct sk_buff *skb) {
@@ -212,6 +252,9 @@ rx_status.rate_idx = info->control.rates[0 ].idx;/* TODO: simulate signal strength (and optional packet drop) */ +if (data->ps !=PS_DISABLED ) + hdr->frame_control |=cpu_to_le16 (IEEE80211_FCTL_PM ); +/* Copy skb to all enabled radios that are on the current frequency */ spin_lock (&hwsim_radio_lock);list_for_each_entry (data2, &hwsim_radios, list) {
@@ -221,6 +264,7 @@continue ;if (!data2->started || !data2->radio_enabled || + !hwsim_ps_rx_ok (data2, skb) || data->channel->center_freq != data2->channel->center_freq)continue ;
@@ -406,7 +450,16 @@struct ieee80211_vif *vif, struct ieee80211_if_conf *conf) { + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; +hwsim_check_magic (vif); +if (conf->changed &IEEE80211_IFCC_BSSID ) { +DECLARE_MAC_BUF (mac); +printk (KERN_DEBUG "%s:%s: BSSID changed: %s\n" , +wiphy_name (hw->wiphy), __func__, +print_mac (mac, conf->bssid)); +memcpy (vp->bssid, conf->bssid,ETH_ALEN ); + }return 0 ; }
@@ -415,6 +468,8 @@ struct ieee80211_bss_conf *info, u32 changed) { +struct hwsim_vif_priv *vp = (void *)vif->drv_priv; +hwsim_check_magic (vif); printk(KERN_DEBUG "%s:%s(changed=0x%x)\n",
@@ -423,6 +478,8 @@if (changed &BSS_CHANGED_ASSOC ) {printk (KERN_DEBUG " %s: ASSOC: assoc=%d aid=%d\n" ,wiphy_name (hw->wiphy), info->assoc, info->aid); + vp->assoc = info->assoc; + vp->aid = info->aid; }if (changed &BSS_CHANGED_ERP_CTS_PROT ) {
@@ -520,11 +577,15 @@spin_unlock_bh (&hwsim_radio_lock);list_for_each_entry (data, &tmplist, list) { +debugfs_remove (data->debugfs_ps); +debugfs_remove (data->debugfs);ieee80211_unregister_hw (data->hw);device_unregister (data->dev);ieee80211_free_hw (data->hw); }class_destroy (hwsim_class); + +debugfs_remove (hwsim_debugfs); }
@@ -545,6 +606,127 @@ } +static void hwsim_send_ps_poll (void *dat,u8 *mac,struct ieee80211_vif *vif) +{ +struct mac80211_hwsim_data *data = dat; +struct hwsim_vif_priv *vp = (void *)vif->drv_priv; +DECLARE_MAC_BUF (buf); +struct sk_buff *skb; +struct ieee80211_pspoll *pspoll; + +if (!vp->assoc) +return ; + +printk (KERN_DEBUG "%s:%s: send PS-Poll to %s for aid %d\n" , +wiphy_name (data->hw->wiphy), __func__, +print_mac (buf, vp->bssid), vp->aid); + + skb =dev_alloc_skb (sizeof (*pspoll)); +if (!skb) +return ; + pspoll = (void *)skb_put (skb,sizeof (*pspoll)); + pspoll->frame_control =cpu_to_le16 (IEEE80211_FTYPE_CTL | +IEEE80211_STYPE_PSPOLL | +IEEE80211_FCTL_PM ); + pspoll->aid =cpu_to_le16 (0xc000 | vp->aid); +memcpy (pspoll->bssid, vp->bssid,ETH_ALEN ); +memcpy (pspoll->ta, mac,ETH_ALEN ); +if (data->radio_enabled && + !mac80211_hwsim_tx_frame (data->hw, skb)) +printk (KERN_DEBUG "%s: PS-Poll frame not ack'ed\n" , __func__); +dev_kfree_skb (skb); +} + + +static void hwsim_send_nullfunc (struct mac80211_hwsim_data *data,u8 *mac, +struct ieee80211_vif *vif,int ps) +{ +struct hwsim_vif_priv *vp = (void *)vif->drv_priv; +DECLARE_MAC_BUF (buf); +struct sk_buff *skb; +struct ieee80211_hdr *hdr; + +if (!vp->assoc) +return ; + +printk (KERN_DEBUG "%s:%s: send data::nullfunc to %s ps=%d\n" , +wiphy_name (data->hw->wiphy), __func__, +print_mac (buf, vp->bssid), ps); + + skb =dev_alloc_skb (sizeof (*hdr)); +if (!skb) +return ; + hdr = (void *)skb_put (skb,sizeof (*hdr) -ETH_ALEN ); + hdr->frame_control =cpu_to_le16 (IEEE80211_FTYPE_DATA | +IEEE80211_STYPE_NULLFUNC | + (ps ?IEEE80211_FCTL_PM :0 )); + hdr->duration_id =cpu_to_le16 (0 ); +memcpy (hdr->addr1, vp->bssid,ETH_ALEN ); +memcpy (hdr->addr2, mac,ETH_ALEN ); +memcpy (hdr->addr3, vp->bssid,ETH_ALEN ); +if (data->radio_enabled && + !mac80211_hwsim_tx_frame (data->hw, skb)) +printk (KERN_DEBUG "%s: nullfunc frame not ack'ed\n" , __func__); +dev_kfree_skb (skb); +} + + +static void hwsim_send_nullfunc_ps (void *dat,u8 *mac, +struct ieee80211_vif *vif) +{ +struct mac80211_hwsim_data *data = dat; +hwsim_send_nullfunc (data, mac, vif,1 ); +} + + +static void hwsim_send_nullfunc_no_ps (void *dat,u8 *mac, +struct ieee80211_vif *vif) +{ +struct mac80211_hwsim_data *data = dat; +hwsim_send_nullfunc (data, mac, vif,0 ); +} + + +static int hwsim_fops_ps_read (void *dat,u64 *val) +{ +struct mac80211_hwsim_data *data = dat; + *val = data->ps; +return 0 ; +} + +static int hwsim_fops_ps_write (void *dat,u64 val) +{ +struct mac80211_hwsim_data *data = dat; +enum ps_mode old_ps; + +if (val !=PS_DISABLED && val !=PS_ENABLED && val !=PS_AUTO_POLL && + val !=PS_MANUAL_POLL ) +return -EINVAL ; + + old_ps = data->ps; + data->ps = val; + +if (val ==PS_MANUAL_POLL ) { +ieee80211_iterate_active_interfaces (data->hw, + hwsim_send_ps_poll, data); + data->ps_poll_pending = true; + }else if (data->ps ==PS_DISABLED && val !=PS_DISABLED ) { +ieee80211_iterate_active_interfaces (data->hw, + hwsim_send_nullfunc_ps, + data); + }else if (data->ps !=PS_DISABLED && val ==PS_DISABLED ) { +ieee80211_iterate_active_interfaces (data->hw, + hwsim_send_nullfunc_no_ps, + data); + } + +return 0 ; +} + +DEFINE_SIMPLE_ATTRIBUTE (hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, +"%llu\n" ); + +static int __init init_mac80211_hwsim (void ) {int i, err =0 ;
@@ -563,6 +745,8 @@if (IS_ERR (hwsim_class))return PTR_ERR (hwsim_class); + hwsim_debugfs =debugfs_create_dir ("mac80211_hwsim" ,NULL ); +memset (addr,0 ,ETH_ALEN ); addr[0 ] =0x02 ;
@@ -637,6 +821,12 @@wiphy_name (hw->wiphy), print_mac(mac, hw->wiphy->perm_addr)); + data->debugfs =debugfs_create_dir (wiphy_name (hw->wiphy), + hwsim_debugfs); + data->debugfs_ps =debugfs_create_file ("ps" ,0666 , + data->debugfs, data, + &hwsim_fops_ps); +setup_timer (&data->beacon_timer, mac80211_hwsim_beacon, (unsigned long ) hw);
include/linux/ieee80211.h +7 -0
@@ -666,6 +666,13 @@u8 ra[6 ]; } __attribute__ ((packed)); +struct ieee80211_pspoll { +__le16 frame_control; +__le16 aid; +u8 bssid[6 ]; +u8 ta[6 ]; +} __attribute__ ((packed)); +/** * struct ieee80211_bar - HT Block Ack Request *
Signature · 1 line
Jouni Malinen PGP id EFC895FA
Johannes Berg
maintainerQuoted 2 lines · Jouni Malinen
This introduces a debugfs file (mac80211_hwsim/phy#/ps) that can be used
Why not use the cfg80211-provided debugfs dir /ieee80211/phy#/ and maybe put it below hwsim/ in there because mac80211 has things in there too? That way you save all the code setting up the directory etc.
Quoted 3 lines
Mode 2 (automatic PS-Poll) does not yet parse Beacon frames, but eventually, it should take a look at TIM IE and send PS-Poll if a traffic bit is set for our AID.
Out of curiosity, is there any hw that implements this? b43 surely would need such code too, and then it could be in mac80211? I can see setting the PM bit be a decision of the driver, but if more than one driver ends up needing ps-poll, maybe mac80211 should be handling it?
johannes
Quoted 7 lines · Johannes Berg
Quoted 2 lines · Jouni Malinen
This introduces a debugfs file (mac80211_hwsim/phy#/ps) that can be used
Why not use the cfg80211-provided debugfs dir /ieee80211/phy#/ and maybe put it below hwsim/ in there because mac80211 has things in there too? That way you save all the code setting up the directory etc.
Good point.. I did actually find out about that some time ago, but did not remember anymore. I'll move the files there.
Quoted 8 lines
Quoted 3 lines
Mode 2 (automatic PS-Poll) does not yet parse Beacon frames, but eventually, it should take a look at TIM IE and send PS-Poll if a traffic bit is set for our AID.
Out of curiosity, is there any hw that implements this? b43 surely would need such code too, and then it could be in mac80211? I can see setting the PM bit be a decision of the driver, but if more than one driver ends up needing ps-poll, maybe mac80211 should be handling it?
Yes, or well, firmware more likely. Anyway, AP will have to support it and the main purpose of this hwsim code is indeed to allow AP PS buffering code to be tested. Anyway, should there be other drivers that would like to do the same, this could of course be eventually moved to mac80211. I'm just not sure whether I would expect this (Beacon parsing) to happen in driver. Rather, I would expect the hardware to provide helper here (i.e., process Beacon frames without indicating them to host, generate an interrupt on Beacon frame that has TIM IE with our bit set). Sending the PS-Poll could potentially be done in mac80211. I would need to take a closer look at how this is done in modern wlan designs.. Then again, many vendors choose not to use PS-Poll at all even.
As far as setting FC PwrMgt bit is concerned, that could possible be in mac80211. Though, there is some timing consideration and likely potential race conditions that would need to be understood based on the exact hardware design.
Signature · 1 line
Jouni Malinen PGP id EFC895FA