diff options
author | Jouni Malinen <j@w1.fi> | 2008-10-30 13:59:05 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-11-10 15:17:41 -0500 |
commit | fc6971d491517ba15e800540ff88caa55dc65b01 (patch) | |
tree | fb435b91e098f8d9a10b8fb217e3d5416d0d5288 /drivers/net/wireless/mac80211_hwsim.c | |
parent | fbf189273926d83d71c4c321f1215162569506ac (diff) |
mac80211_hwsim: Add support for client PS mode
This introduces a debugfs file (ieee80211/phy#/hwsim/ps) that can be
used to force a simulated radio into power save mode. Following values
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.
Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/mac80211_hwsim.c')
-rw-r--r-- | drivers/net/wireless/mac80211_hwsim.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index bc9da9393760..b9230da925ee 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/if_arp.h> | 21 | #include <linux/if_arp.h> |
22 | #include <linux/rtnetlink.h> | 22 | #include <linux/rtnetlink.h> |
23 | #include <linux/etherdevice.h> | 23 | #include <linux/etherdevice.h> |
24 | #include <linux/debugfs.h> | ||
24 | 25 | ||
25 | MODULE_AUTHOR("Jouni Malinen"); | 26 | MODULE_AUTHOR("Jouni Malinen"); |
26 | MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211"); | 27 | MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211"); |
@@ -32,6 +33,9 @@ MODULE_PARM_DESC(radios, "Number of simulated radios"); | |||
32 | 33 | ||
33 | struct hwsim_vif_priv { | 34 | struct hwsim_vif_priv { |
34 | u32 magic; | 35 | u32 magic; |
36 | u8 bssid[ETH_ALEN]; | ||
37 | bool assoc; | ||
38 | u16 aid; | ||
35 | }; | 39 | }; |
36 | 40 | ||
37 | #define HWSIM_VIF_MAGIC 0x69537748 | 41 | #define HWSIM_VIF_MAGIC 0x69537748 |
@@ -132,6 +136,12 @@ struct mac80211_hwsim_data { | |||
132 | unsigned int rx_filter; | 136 | unsigned int rx_filter; |
133 | int started; | 137 | int started; |
134 | struct timer_list beacon_timer; | 138 | struct timer_list beacon_timer; |
139 | enum ps_mode { | ||
140 | PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL | ||
141 | } ps; | ||
142 | bool ps_poll_pending; | ||
143 | struct dentry *debugfs; | ||
144 | struct dentry *debugfs_ps; | ||
135 | }; | 145 | }; |
136 | 146 | ||
137 | 147 | ||
@@ -196,6 +206,34 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, | |||
196 | } | 206 | } |
197 | 207 | ||
198 | 208 | ||
209 | static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, | ||
210 | struct sk_buff *skb) | ||
211 | { | ||
212 | switch (data->ps) { | ||
213 | case PS_DISABLED: | ||
214 | return true; | ||
215 | case PS_ENABLED: | ||
216 | return false; | ||
217 | case PS_AUTO_POLL: | ||
218 | /* TODO: accept (some) Beacons by default and other frames only | ||
219 | * if pending PS-Poll has been sent */ | ||
220 | return true; | ||
221 | case PS_MANUAL_POLL: | ||
222 | /* Allow unicast frames to own address if there is a pending | ||
223 | * PS-Poll */ | ||
224 | if (data->ps_poll_pending && | ||
225 | memcmp(data->hw->wiphy->perm_addr, skb->data + 4, | ||
226 | ETH_ALEN) == 0) { | ||
227 | data->ps_poll_pending = false; | ||
228 | return true; | ||
229 | } | ||
230 | return false; | ||
231 | } | ||
232 | |||
233 | return true; | ||
234 | } | ||
235 | |||
236 | |||
199 | static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | 237 | static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, |
200 | struct sk_buff *skb) | 238 | struct sk_buff *skb) |
201 | { | 239 | { |
@@ -212,6 +250,9 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | |||
212 | rx_status.rate_idx = info->control.rates[0].idx; | 250 | rx_status.rate_idx = info->control.rates[0].idx; |
213 | /* TODO: simulate signal strength (and optional packet drop) */ | 251 | /* TODO: simulate signal strength (and optional packet drop) */ |
214 | 252 | ||
253 | if (data->ps != PS_DISABLED) | ||
254 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); | ||
255 | |||
215 | /* Copy skb to all enabled radios that are on the current frequency */ | 256 | /* Copy skb to all enabled radios that are on the current frequency */ |
216 | spin_lock(&hwsim_radio_lock); | 257 | spin_lock(&hwsim_radio_lock); |
217 | list_for_each_entry(data2, &hwsim_radios, list) { | 258 | list_for_each_entry(data2, &hwsim_radios, list) { |
@@ -221,6 +262,7 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | |||
221 | continue; | 262 | continue; |
222 | 263 | ||
223 | if (!data2->started || !data2->radio_enabled || | 264 | if (!data2->started || !data2->radio_enabled || |
265 | !hwsim_ps_rx_ok(data2, skb) || | ||
224 | data->channel->center_freq != data2->channel->center_freq) | 266 | data->channel->center_freq != data2->channel->center_freq) |
225 | continue; | 267 | continue; |
226 | 268 | ||
@@ -404,7 +446,16 @@ static int mac80211_hwsim_config_interface(struct ieee80211_hw *hw, | |||
404 | struct ieee80211_vif *vif, | 446 | struct ieee80211_vif *vif, |
405 | struct ieee80211_if_conf *conf) | 447 | struct ieee80211_if_conf *conf) |
406 | { | 448 | { |
449 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
450 | |||
407 | hwsim_check_magic(vif); | 451 | hwsim_check_magic(vif); |
452 | if (conf->changed & IEEE80211_IFCC_BSSID) { | ||
453 | DECLARE_MAC_BUF(mac); | ||
454 | printk(KERN_DEBUG "%s:%s: BSSID changed: %s\n", | ||
455 | wiphy_name(hw->wiphy), __func__, | ||
456 | print_mac(mac, conf->bssid)); | ||
457 | memcpy(vp->bssid, conf->bssid, ETH_ALEN); | ||
458 | } | ||
408 | return 0; | 459 | return 0; |
409 | } | 460 | } |
410 | 461 | ||
@@ -413,6 +464,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, | |||
413 | struct ieee80211_bss_conf *info, | 464 | struct ieee80211_bss_conf *info, |
414 | u32 changed) | 465 | u32 changed) |
415 | { | 466 | { |
467 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
468 | |||
416 | hwsim_check_magic(vif); | 469 | hwsim_check_magic(vif); |
417 | 470 | ||
418 | printk(KERN_DEBUG "%s:%s(changed=0x%x)\n", | 471 | printk(KERN_DEBUG "%s:%s(changed=0x%x)\n", |
@@ -421,6 +474,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, | |||
421 | if (changed & BSS_CHANGED_ASSOC) { | 474 | if (changed & BSS_CHANGED_ASSOC) { |
422 | printk(KERN_DEBUG " %s: ASSOC: assoc=%d aid=%d\n", | 475 | printk(KERN_DEBUG " %s: ASSOC: assoc=%d aid=%d\n", |
423 | wiphy_name(hw->wiphy), info->assoc, info->aid); | 476 | wiphy_name(hw->wiphy), info->assoc, info->aid); |
477 | vp->assoc = info->assoc; | ||
478 | vp->aid = info->aid; | ||
424 | } | 479 | } |
425 | 480 | ||
426 | if (changed & BSS_CHANGED_ERP_CTS_PROT) { | 481 | if (changed & BSS_CHANGED_ERP_CTS_PROT) { |
@@ -518,6 +573,8 @@ static void mac80211_hwsim_free(void) | |||
518 | spin_unlock_bh(&hwsim_radio_lock); | 573 | spin_unlock_bh(&hwsim_radio_lock); |
519 | 574 | ||
520 | list_for_each_entry(data, &tmplist, list) { | 575 | list_for_each_entry(data, &tmplist, list) { |
576 | debugfs_remove(data->debugfs_ps); | ||
577 | debugfs_remove(data->debugfs); | ||
521 | ieee80211_unregister_hw(data->hw); | 578 | ieee80211_unregister_hw(data->hw); |
522 | device_unregister(data->dev); | 579 | device_unregister(data->dev); |
523 | ieee80211_free_hw(data->hw); | 580 | ieee80211_free_hw(data->hw); |
@@ -543,6 +600,127 @@ static void hwsim_mon_setup(struct net_device *dev) | |||
543 | } | 600 | } |
544 | 601 | ||
545 | 602 | ||
603 | static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) | ||
604 | { | ||
605 | struct mac80211_hwsim_data *data = dat; | ||
606 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
607 | DECLARE_MAC_BUF(buf); | ||
608 | struct sk_buff *skb; | ||
609 | struct ieee80211_pspoll *pspoll; | ||
610 | |||
611 | if (!vp->assoc) | ||
612 | return; | ||
613 | |||
614 | printk(KERN_DEBUG "%s:%s: send PS-Poll to %s for aid %d\n", | ||
615 | wiphy_name(data->hw->wiphy), __func__, | ||
616 | print_mac(buf, vp->bssid), vp->aid); | ||
617 | |||
618 | skb = dev_alloc_skb(sizeof(*pspoll)); | ||
619 | if (!skb) | ||
620 | return; | ||
621 | pspoll = (void *) skb_put(skb, sizeof(*pspoll)); | ||
622 | pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | | ||
623 | IEEE80211_STYPE_PSPOLL | | ||
624 | IEEE80211_FCTL_PM); | ||
625 | pspoll->aid = cpu_to_le16(0xc000 | vp->aid); | ||
626 | memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); | ||
627 | memcpy(pspoll->ta, mac, ETH_ALEN); | ||
628 | if (data->radio_enabled && | ||
629 | !mac80211_hwsim_tx_frame(data->hw, skb)) | ||
630 | printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__); | ||
631 | dev_kfree_skb(skb); | ||
632 | } | ||
633 | |||
634 | |||
635 | static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, | ||
636 | struct ieee80211_vif *vif, int ps) | ||
637 | { | ||
638 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
639 | DECLARE_MAC_BUF(buf); | ||
640 | struct sk_buff *skb; | ||
641 | struct ieee80211_hdr *hdr; | ||
642 | |||
643 | if (!vp->assoc) | ||
644 | return; | ||
645 | |||
646 | printk(KERN_DEBUG "%s:%s: send data::nullfunc to %s ps=%d\n", | ||
647 | wiphy_name(data->hw->wiphy), __func__, | ||
648 | print_mac(buf, vp->bssid), ps); | ||
649 | |||
650 | skb = dev_alloc_skb(sizeof(*hdr)); | ||
651 | if (!skb) | ||
652 | return; | ||
653 | hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN); | ||
654 | hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
655 | IEEE80211_STYPE_NULLFUNC | | ||
656 | (ps ? IEEE80211_FCTL_PM : 0)); | ||
657 | hdr->duration_id = cpu_to_le16(0); | ||
658 | memcpy(hdr->addr1, vp->bssid, ETH_ALEN); | ||
659 | memcpy(hdr->addr2, mac, ETH_ALEN); | ||
660 | memcpy(hdr->addr3, vp->bssid, ETH_ALEN); | ||
661 | if (data->radio_enabled && | ||
662 | !mac80211_hwsim_tx_frame(data->hw, skb)) | ||
663 | printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__); | ||
664 | dev_kfree_skb(skb); | ||
665 | } | ||
666 | |||
667 | |||
668 | static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, | ||
669 | struct ieee80211_vif *vif) | ||
670 | { | ||
671 | struct mac80211_hwsim_data *data = dat; | ||
672 | hwsim_send_nullfunc(data, mac, vif, 1); | ||
673 | } | ||
674 | |||
675 | |||
676 | static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, | ||
677 | struct ieee80211_vif *vif) | ||
678 | { | ||
679 | struct mac80211_hwsim_data *data = dat; | ||
680 | hwsim_send_nullfunc(data, mac, vif, 0); | ||
681 | } | ||
682 | |||
683 | |||
684 | static int hwsim_fops_ps_read(void *dat, u64 *val) | ||
685 | { | ||
686 | struct mac80211_hwsim_data *data = dat; | ||
687 | *val = data->ps; | ||
688 | return 0; | ||
689 | } | ||
690 | |||
691 | static int hwsim_fops_ps_write(void *dat, u64 val) | ||
692 | { | ||
693 | struct mac80211_hwsim_data *data = dat; | ||
694 | enum ps_mode old_ps; | ||
695 | |||
696 | if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && | ||
697 | val != PS_MANUAL_POLL) | ||
698 | return -EINVAL; | ||
699 | |||
700 | old_ps = data->ps; | ||
701 | data->ps = val; | ||
702 | |||
703 | if (val == PS_MANUAL_POLL) { | ||
704 | ieee80211_iterate_active_interfaces(data->hw, | ||
705 | hwsim_send_ps_poll, data); | ||
706 | data->ps_poll_pending = true; | ||
707 | } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { | ||
708 | ieee80211_iterate_active_interfaces(data->hw, | ||
709 | hwsim_send_nullfunc_ps, | ||
710 | data); | ||
711 | } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { | ||
712 | ieee80211_iterate_active_interfaces(data->hw, | ||
713 | hwsim_send_nullfunc_no_ps, | ||
714 | data); | ||
715 | } | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, | ||
721 | "%llu\n"); | ||
722 | |||
723 | |||
546 | static int __init init_mac80211_hwsim(void) | 724 | static int __init init_mac80211_hwsim(void) |
547 | { | 725 | { |
548 | int i, err = 0; | 726 | int i, err = 0; |
@@ -634,6 +812,12 @@ static int __init init_mac80211_hwsim(void) | |||
634 | wiphy_name(hw->wiphy), | 812 | wiphy_name(hw->wiphy), |
635 | hw->wiphy->perm_addr); | 813 | hw->wiphy->perm_addr); |
636 | 814 | ||
815 | data->debugfs = debugfs_create_dir("hwsim", | ||
816 | hw->wiphy->debugfsdir); | ||
817 | data->debugfs_ps = debugfs_create_file("ps", 0666, | ||
818 | data->debugfs, data, | ||
819 | &hwsim_fops_ps); | ||
820 | |||
637 | setup_timer(&data->beacon_timer, mac80211_hwsim_beacon, | 821 | setup_timer(&data->beacon_timer, mac80211_hwsim_beacon, |
638 | (unsigned long) hw); | 822 | (unsigned long) hw); |
639 | 823 | ||