mac80211: add explicit AP/GO driver operations
authorJohannes Berg <johannes.berg@intel.com>
Fri, 19 Oct 2012 13:44:42 +0000 (15:44 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 26 Oct 2012 10:57:06 +0000 (12:57 +0200)
Depending on the driver, a lot of setup may be
necessary to start operating as an AP, some of
which may fail. Add an explicit AP start driver
method to make such failures easier to handle,
and add an AP stop driver method for symmetry.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/driver-ops.h
net/mac80211/pm.c
net/mac80211/trace.h
net/mac80211/util.c

index 00b7204708bd681d7bb8baf241b23557460f1a09..5f5327452c9a03122af32b2bb75c67c978b23d8a 100644 (file)
@@ -2381,6 +2381,13 @@ enum ieee80211_rate_control_changed {
  *     to vif. Possible use is for hw queue remapping.
  * @unassign_vif_chanctx: Notifies device driver about channel context being
  *     unbound from vif.
+ * @start_ap: Start operation on the AP interface, this is called after all the
+ *     information in bss_conf is set and beacon can be retrieved. A channel
+ *     context is bound before this is called. Note that if the driver uses
+ *     software scan or ROC, this (and @stop_ap) isn't called when the AP is
+ *     just "paused" for scanning/ROC, which is indicated by the beacon being
+ *     disabled/enabled via @bss_info_changed.
+ * @stop_ap: Stop operation on the AP interface.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -2406,6 +2413,9 @@ struct ieee80211_ops {
                                 struct ieee80211_bss_conf *info,
                                 u32 changed);
 
+       int (*start_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+       void (*stop_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+
        u64 (*prepare_multicast)(struct ieee80211_hw *hw,
                                 struct netdev_hw_addr_list *mc_list);
        void (*configure_filter)(struct ieee80211_hw *hw,
index 95bf3d5d009fdfb1b5dcbc539e8ca0c5b2b4ac41..34fd3eba30909393de773514efcfa54c0e8ad328 100644 (file)
@@ -922,6 +922,15 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                return err;
        changed |= err;
 
+       err = drv_start_ap(sdata->local, sdata);
+       if (err) {
+               old = rtnl_dereference(sdata->u.ap.beacon);
+               if (old)
+                       kfree_rcu(old, rcu_head);
+               RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+               return err;
+       }
+
        ieee80211_bss_info_change_notify(sdata, changed);
 
        netif_carrier_on(dev);
@@ -979,6 +988,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        sta_info_flush(local, sdata);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
 
+       drv_stop_ap(sdata->local, sdata);
+
        /* free all potentially still buffered bcast frames */
        local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
        skb_queue_purge(&sdata->u.ap.ps.bc_buf);
index 77407b31e1ffb7120927f872f24ce5276904c5fe..1701ad7013a442e0fb717a4c88200bb9309caa77 100644 (file)
@@ -936,4 +936,29 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline int drv_start_ap(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata)
+{
+       int ret = 0;
+
+       check_sdata_in_driver(sdata);
+
+       trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);
+       if (local->ops->start_ap)
+               ret = local->ops->start_ap(&local->hw, &sdata->vif);
+       trace_drv_return_int(local, ret);
+       return ret;
+}
+
+static inline void drv_stop_ap(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata)
+{
+       check_sdata_in_driver(sdata);
+
+       trace_drv_stop_ap(local, sdata);
+       if (local->ops->stop_ap)
+               local->ops->stop_ap(&local->hw, &sdata->vif);
+       trace_drv_return_void(local);
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index 9f404ac901ab60cb3d9ee69e0eb9d7516e8484f5..0f1c434638bcb26eb85fd4afbd335a4da75ac391 100644 (file)
@@ -135,6 +135,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                ieee80211_bss_info_change_notify(sdata,
                        BSS_CHANGED_BEACON_ENABLED);
 
+               if (sdata->vif.type == NL80211_IFTYPE_AP &&
+                   rcu_access_pointer(sdata->u.ap.beacon))
+                       drv_stop_ap(local, sdata);
+
                /* the interface is leaving the channel and is removed */
                ieee80211_vif_release_channel(sdata);
                drv_remove_interface(local, sdata);
index 0638541b625f155d0299998a8edbd457858f36e0..5e74e77cba9a0caf7242a62d3fd139206846fc68 100644 (file)
@@ -1396,6 +1396,43 @@ DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx,
        TP_ARGS(local, sdata, ctx)
 );
 
+TRACE_EVENT(drv_start_ap,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_bss_conf *info),
+
+       TP_ARGS(local, sdata, info),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(u8, dtimper)
+               __field(u16, bcnint)
+               __dynamic_array(u8, ssid, info->ssid_len);
+               __field(bool, hidden_ssid);
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->dtimper = info->dtim_period;
+               __entry->bcnint = info->beacon_int;
+               memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
+               __entry->hidden_ssid = info->hidden_ssid;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT  VIF_PR_FMT,
+               LOCAL_PR_ARG, VIF_PR_ARG
+       )
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+       TP_ARGS(local, sdata)
+);
+
 /*
  * Tracing for API calls that drivers call.
  */
index ea8a6744a9dbffeb9ac71307bab763d2d8eb4fbd..dd0e6f20fc51f713348d138a18cda88f2a2a8f94 100644 (file)
@@ -1467,9 +1467,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                case NL80211_IFTYPE_AP:
                        changed |= BSS_CHANGED_SSID;
 
-                       if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       if (sdata->vif.type == NL80211_IFTYPE_AP) {
                                changed |= BSS_CHANGED_AP_PROBE_RESP;
 
+                               if (rcu_access_pointer(sdata->u.ap.beacon))
+                                       drv_start_ap(local, sdata);
+                       }
+
                        /* fall through */
                case NL80211_IFTYPE_MESH_POINT:
                        changed |= BSS_CHANGED_BEACON |