diff options
-rw-r--r-- | include/net/mac80211.h | 14 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 13 | ||||
-rw-r--r-- | net/mac80211/main.c | 24 | ||||
-rw-r--r-- | net/mac80211/pm.c | 110 | ||||
-rw-r--r-- | net/mac80211/util.c | 118 |
5 files changed, 172 insertions, 107 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 2c6f976831b5..a593bedcfeda 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -1575,6 +1575,20 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw); | |||
1575 | */ | 1575 | */ |
1576 | void ieee80211_free_hw(struct ieee80211_hw *hw); | 1576 | void ieee80211_free_hw(struct ieee80211_hw *hw); |
1577 | 1577 | ||
1578 | /** | ||
1579 | * ieee80211_restart_hw - restart hardware completely | ||
1580 | * | ||
1581 | * Call this function when the hardware was restarted for some reason | ||
1582 | * (hardware error, ...) and the driver is unable to restore its state | ||
1583 | * by itself. mac80211 assumes that at this point the driver/hardware | ||
1584 | * is completely uninitialised and stopped, it starts the process by | ||
1585 | * calling the ->start() operation. The driver will need to reset all | ||
1586 | * internal state that it has prior to calling this function. | ||
1587 | * | ||
1588 | * @hw: the hardware to restart | ||
1589 | */ | ||
1590 | void ieee80211_restart_hw(struct ieee80211_hw *hw); | ||
1591 | |||
1578 | /* trick to avoid symbol clashes with the ieee80211 subsystem */ | 1592 | /* trick to avoid symbol clashes with the ieee80211 subsystem */ |
1579 | void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, | 1593 | void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, |
1580 | struct ieee80211_rx_status *status); | 1594 | struct ieee80211_rx_status *status); |
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 */ |
1042 | int ieee80211_reconfig(struct ieee80211_local *local); | ||
1043 | |||
1040 | #ifdef CONFIG_PM | 1044 | #ifdef CONFIG_PM |
1041 | int __ieee80211_suspend(struct ieee80211_hw *hw); | 1045 | int __ieee80211_suspend(struct ieee80211_hw *hw); |
1042 | int __ieee80211_resume(struct ieee80211_hw *hw); | 1046 | |
1047 | static inline int __ieee80211_resume(struct ieee80211_hw *hw) | ||
1048 | { | ||
1049 | return ieee80211_reconfig(hw_to_local(hw)); | ||
1050 | } | ||
1043 | #else | 1051 | #else |
1044 | static inline int __ieee80211_suspend(struct ieee80211_hw *hw) | 1052 | static inline int __ieee80211_suspend(struct ieee80211_hw *hw) |
1045 | { | 1053 | { |
1046 | return 0; | 1054 | return 0; |
1047 | } | 1055 | } |
1056 | |||
1048 | static inline int __ieee80211_resume(struct ieee80211_hw *hw) | 1057 | static 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 | } |
697 | EXPORT_SYMBOL(ieee80211_tx_status); | 697 | EXPORT_SYMBOL(ieee80211_tx_status); |
698 | 698 | ||
699 | static 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 | |||
709 | void 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 | } | ||
719 | EXPORT_SYMBOL(ieee80211_restart_hw); | ||
720 | |||
699 | struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | 721 | struct 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 | ||
75 | int __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 */ |
33 | void *mac80211_wiphy_privid = &mac80211_wiphy_privid; | 34 | void *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 | |||
971 | int 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 | } | ||