aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-04-14 04:09:24 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-04-22 16:57:14 -0400
commitf2753ddbadb0873a98421415882318251bbd9eaa (patch)
treedaad40232be13d91835b30234e5743dfdd582f19 /net
parenta1c555802a62c845520d2486d783c9bb1d5e68a9 (diff)
mac80211: add hardware restart function
Some hardware defects may require the hardware to be re-initialised completely from scratch. Drivers would need much information (for instance the current MAC address, crypto keys, beaconing information, etc.) stored duplicated from mac80211 to be able to do this, so let mac80211 help them. The new ieee80211_restart_hw() function requires the same code as resuming, so move that code into a new ieee80211_reconfig() function in util.c and leave only the suspend code in pm.c. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/ieee80211_i.h13
-rw-r--r--net/mac80211/main.c24
-rw-r--r--net/mac80211/pm.c110
-rw-r--r--net/mac80211/util.c118
4 files changed, 158 insertions, 107 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index cb80a80504e6..13d6f890ced4 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -748,6 +748,8 @@ struct ieee80211_local {
748 int user_power_level; /* in dBm */ 748 int user_power_level; /* in dBm */
749 int power_constr_level; /* in dBm */ 749 int power_constr_level; /* in dBm */
750 750
751 struct work_struct restart_work;
752
751#ifdef CONFIG_MAC80211_DEBUGFS 753#ifdef CONFIG_MAC80211_DEBUGFS
752 struct local_debugfsdentries { 754 struct local_debugfsdentries {
753 struct dentry *rcdir; 755 struct dentry *rcdir;
@@ -1036,15 +1038,22 @@ void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
1036 u16 capab_info, u8 *pwr_constr_elem, 1038 u16 capab_info, u8 *pwr_constr_elem,
1037 u8 pwr_constr_elem_len); 1039 u8 pwr_constr_elem_len);
1038 1040
1039/* Suspend/resume */ 1041/* Suspend/resume and hw reconfiguration */
1042int ieee80211_reconfig(struct ieee80211_local *local);
1043
1040#ifdef CONFIG_PM 1044#ifdef CONFIG_PM
1041int __ieee80211_suspend(struct ieee80211_hw *hw); 1045int __ieee80211_suspend(struct ieee80211_hw *hw);
1042int __ieee80211_resume(struct ieee80211_hw *hw); 1046
1047static inline int __ieee80211_resume(struct ieee80211_hw *hw)
1048{
1049 return ieee80211_reconfig(hw_to_local(hw));
1050}
1043#else 1051#else
1044static inline int __ieee80211_suspend(struct ieee80211_hw *hw) 1052static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
1045{ 1053{
1046 return 0; 1054 return 0;
1047} 1055}
1056
1048static inline int __ieee80211_resume(struct ieee80211_hw *hw) 1057static inline int __ieee80211_resume(struct ieee80211_hw *hw)
1049{ 1058{
1050 return 0; 1059 return 0;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index c1145be72da4..80c0e28bf549 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -696,6 +696,28 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
696} 696}
697EXPORT_SYMBOL(ieee80211_tx_status); 697EXPORT_SYMBOL(ieee80211_tx_status);
698 698
699static void ieee80211_restart_work(struct work_struct *work)
700{
701 struct ieee80211_local *local =
702 container_of(work, struct ieee80211_local, restart_work);
703
704 rtnl_lock();
705 ieee80211_reconfig(local);
706 rtnl_unlock();
707}
708
709void ieee80211_restart_hw(struct ieee80211_hw *hw)
710{
711 struct ieee80211_local *local = hw_to_local(hw);
712
713 /* use this reason, __ieee80211_resume will unblock it */
714 ieee80211_stop_queues_by_reason(hw,
715 IEEE80211_QUEUE_STOP_REASON_SUSPEND);
716
717 schedule_work(&local->restart_work);
718}
719EXPORT_SYMBOL(ieee80211_restart_hw);
720
699struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, 721struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
700 const struct ieee80211_ops *ops) 722 const struct ieee80211_ops *ops)
701{ 723{
@@ -768,6 +790,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
768 790
769 INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); 791 INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
770 792
793 INIT_WORK(&local->restart_work, ieee80211_restart_work);
794
771 INIT_WORK(&local->dynamic_ps_enable_work, 795 INIT_WORK(&local->dynamic_ps_enable_work,
772 ieee80211_dynamic_ps_enable_work); 796 ieee80211_dynamic_ps_enable_work);
773 INIT_WORK(&local->dynamic_ps_disable_work, 797 INIT_WORK(&local->dynamic_ps_disable_work,
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 2b4c95cd9daf..b38986c9deef 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -72,108 +72,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
72 return 0; 72 return 0;
73} 73}
74 74
75int __ieee80211_resume(struct ieee80211_hw *hw) 75/*
76{ 76 * __ieee80211_resume() is a static inline which just calls
77 struct ieee80211_local *local = hw_to_local(hw); 77 * ieee80211_reconfig(), which is also needed for hardware
78 struct ieee80211_sub_if_data *sdata; 78 * hang/firmware failure/etc. recovery.
79 struct ieee80211_if_init_conf conf; 79 */
80 struct sta_info *sta;
81 unsigned long flags;
82 int res;
83
84 /* restart hardware */
85 if (local->open_count) {
86 res = local->ops->start(hw);
87
88 ieee80211_led_radio(local, hw->conf.radio_enabled);
89 }
90
91 /* add interfaces */
92 list_for_each_entry(sdata, &local->interfaces, list) {
93 if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
94 sdata->vif.type != NL80211_IFTYPE_MONITOR &&
95 netif_running(sdata->dev)) {
96 conf.vif = &sdata->vif;
97 conf.type = sdata->vif.type;
98 conf.mac_addr = sdata->dev->dev_addr;
99 res = local->ops->add_interface(hw, &conf);
100 }
101 }
102
103 /* add STAs back */
104 if (local->ops->sta_notify) {
105 spin_lock_irqsave(&local->sta_lock, flags);
106 list_for_each_entry(sta, &local->sta_list, list) {
107 if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
108 sdata = container_of(sdata->bss,
109 struct ieee80211_sub_if_data,
110 u.ap);
111
112 local->ops->sta_notify(hw, &sdata->vif,
113 STA_NOTIFY_ADD, &sta->sta);
114 }
115 spin_unlock_irqrestore(&local->sta_lock, flags);
116 }
117
118 /* Clear Suspend state so that ADDBA requests can be processed */
119
120 rcu_read_lock();
121
122 if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
123 list_for_each_entry_rcu(sta, &local->sta_list, list) {
124 clear_sta_flags(sta, WLAN_STA_SUSPEND);
125 }
126 }
127
128 rcu_read_unlock();
129
130 /* setup RTS threshold */
131 if (local->ops->set_rts_threshold)
132 local->ops->set_rts_threshold(hw, local->rts_threshold);
133
134 /* reconfigure hardware */
135 ieee80211_hw_config(local, ~0);
136
137 netif_addr_lock_bh(local->mdev);
138 ieee80211_configure_filter(local);
139 netif_addr_unlock_bh(local->mdev);
140
141 /* Finally also reconfigure all the BSS information */
142 list_for_each_entry(sdata, &local->interfaces, list) {
143 u32 changed = ~0;
144 if (!netif_running(sdata->dev))
145 continue;
146 switch (sdata->vif.type) {
147 case NL80211_IFTYPE_STATION:
148 /* disable beacon change bits */
149 changed &= ~IEEE80211_IFCC_BEACON;
150 /* fall through */
151 case NL80211_IFTYPE_ADHOC:
152 case NL80211_IFTYPE_AP:
153 case NL80211_IFTYPE_MESH_POINT:
154 WARN_ON(ieee80211_if_config(sdata, changed));
155 ieee80211_bss_info_change_notify(sdata, ~0);
156 break;
157 case NL80211_IFTYPE_WDS:
158 break;
159 case NL80211_IFTYPE_AP_VLAN:
160 case NL80211_IFTYPE_MONITOR:
161 /* ignore virtual */
162 break;
163 case NL80211_IFTYPE_UNSPECIFIED:
164 case __NL80211_IFTYPE_AFTER_LAST:
165 WARN_ON(1);
166 break;
167 }
168 }
169
170 /* add back keys */
171 list_for_each_entry(sdata, &local->interfaces, list)
172 if (netif_running(sdata->dev))
173 ieee80211_enable_keys(sdata);
174
175 ieee80211_wake_queues_by_reason(hw,
176 IEEE80211_QUEUE_STOP_REASON_SUSPEND);
177
178 return 0;
179}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 1ff83532120f..b361e2acfce9 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -28,6 +28,7 @@
28#include "rate.h" 28#include "rate.h"
29#include "mesh.h" 29#include "mesh.h"
30#include "wme.h" 30#include "wme.h"
31#include "led.h"
31 32
32/* privid for wiphys to determine whether they belong to us or not */ 33/* privid for wiphys to determine whether they belong to us or not */
33void *mac80211_wiphy_privid = &mac80211_wiphy_privid; 34void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
@@ -966,3 +967,120 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
966 } 967 }
967 return supp_rates; 968 return supp_rates;
968} 969}
970
971int ieee80211_reconfig(struct ieee80211_local *local)
972{
973 struct ieee80211_hw *hw = &local->hw;
974 struct ieee80211_sub_if_data *sdata;
975 struct ieee80211_if_init_conf conf;
976 struct sta_info *sta;
977 unsigned long flags;
978 int res;
979
980 /* restart hardware */
981 if (local->open_count) {
982 res = local->ops->start(hw);
983
984 ieee80211_led_radio(local, hw->conf.radio_enabled);
985 }
986
987 /* add interfaces */
988 list_for_each_entry(sdata, &local->interfaces, list) {
989 if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
990 sdata->vif.type != NL80211_IFTYPE_MONITOR &&
991 netif_running(sdata->dev)) {
992 conf.vif = &sdata->vif;
993 conf.type = sdata->vif.type;
994 conf.mac_addr = sdata->dev->dev_addr;
995 res = local->ops->add_interface(hw, &conf);
996 }
997 }
998
999 /* add STAs back */
1000 if (local->ops->sta_notify) {
1001 spin_lock_irqsave(&local->sta_lock, flags);
1002 list_for_each_entry(sta, &local->sta_list, list) {
1003 if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
1004 sdata = container_of(sdata->bss,
1005 struct ieee80211_sub_if_data,
1006 u.ap);
1007
1008 local->ops->sta_notify(hw, &sdata->vif,
1009 STA_NOTIFY_ADD, &sta->sta);
1010 }
1011 spin_unlock_irqrestore(&local->sta_lock, flags);
1012 }
1013
1014 /* Clear Suspend state so that ADDBA requests can be processed */
1015
1016 rcu_read_lock();
1017
1018 if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
1019 list_for_each_entry_rcu(sta, &local->sta_list, list) {
1020 clear_sta_flags(sta, WLAN_STA_SUSPEND);
1021 }
1022 }
1023
1024 rcu_read_unlock();
1025
1026 /* setup RTS threshold */
1027 if (local->ops->set_rts_threshold)
1028 local->ops->set_rts_threshold(hw, local->rts_threshold);
1029
1030 /* reconfigure hardware */
1031 ieee80211_hw_config(local, ~0);
1032
1033 netif_addr_lock_bh(local->mdev);
1034 ieee80211_configure_filter(local);
1035 netif_addr_unlock_bh(local->mdev);
1036
1037 /* Finally also reconfigure all the BSS information */
1038 list_for_each_entry(sdata, &local->interfaces, list) {
1039 u32 changed = ~0;
1040 if (!netif_running(sdata->dev))
1041 continue;
1042 switch (sdata->vif.type) {
1043 case NL80211_IFTYPE_STATION:
1044 /* disable beacon change bits */
1045 changed &= ~IEEE80211_IFCC_BEACON;
1046 /* fall through */
1047 case NL80211_IFTYPE_ADHOC:
1048 case NL80211_IFTYPE_AP:
1049 case NL80211_IFTYPE_MESH_POINT:
1050 /*
1051 * Driver's config_interface can fail if rfkill is
1052 * enabled. Accommodate this return code.
1053 * FIXME: When mac80211 has knowledge of rfkill
1054 * state the code below can change back to:
1055 * WARN(ieee80211_if_config(sdata, changed));
1056 * ieee80211_bss_info_change_notify(sdata, ~0);
1057 */
1058 if (ieee80211_if_config(sdata, changed))
1059 printk(KERN_DEBUG "%s: failed to configure interface during resume\n",
1060 sdata->dev->name);
1061 else
1062 ieee80211_bss_info_change_notify(sdata, ~0);
1063 break;
1064 case NL80211_IFTYPE_WDS:
1065 break;
1066 case NL80211_IFTYPE_AP_VLAN:
1067 case NL80211_IFTYPE_MONITOR:
1068 /* ignore virtual */
1069 break;
1070 case NL80211_IFTYPE_UNSPECIFIED:
1071 case __NL80211_IFTYPE_AFTER_LAST:
1072 WARN_ON(1);
1073 break;
1074 }
1075 }
1076
1077 /* add back keys */
1078 list_for_each_entry(sdata, &local->interfaces, list)
1079 if (netif_running(sdata->dev))
1080 ieee80211_enable_keys(sdata);
1081
1082 ieee80211_wake_queues_by_reason(hw,
1083 IEEE80211_QUEUE_STOP_REASON_SUSPEND);
1084
1085 return 0;
1086}