static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
			      struct cfg80211_ap_settings *params)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct beacon_data *old;
	struct ieee80211_sub_if_data *vlan;
	u32 changed = BSS_CHANGED_BEACON_INT |
		      BSS_CHANGED_BEACON_ENABLED |
		      BSS_CHANGED_BEACON |
		      BSS_CHANGED_SSID |
		      BSS_CHANGED_P2P_PS |
		      BSS_CHANGED_TXPOWER |
		      BSS_CHANGED_TWT;
	int err;
	int prev_beacon_int;

	old = sdata_dereference(sdata->u.ap.beacon, sdata);
	if (old)
		return -EALREADY;

	switch (params->smps_mode) {
	case NL80211_SMPS_OFF:
		sdata->smps_mode = IEEE80211_SMPS_OFF;
		break;
	case NL80211_SMPS_STATIC:
		sdata->smps_mode = IEEE80211_SMPS_STATIC;
		break;
	case NL80211_SMPS_DYNAMIC:
		sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
		break;
	default:
		return -EINVAL;
	}
	sdata->u.ap.req_smps = sdata->smps_mode;

	sdata->needed_rx_chains = sdata->local->rx_chains;

	prev_beacon_int = sdata->vif.bss_conf.beacon_int;
	sdata->vif.bss_conf.beacon_int = params->beacon_interval;

	if (params->he_cap)
		sdata->vif.bss_conf.he_support = true;

	mutex_lock(&local->mtx);
	err = ieee80211_vif_use_channel(sdata, &params->chandef,
					IEEE80211_CHANCTX_SHARED);
	if (!err)
		ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
	mutex_unlock(&local->mtx);
	if (err) {
		sdata->vif.bss_conf.beacon_int = prev_beacon_int;
		return err;
	}

	/*
	 * Apply control port protocol, this allows us to
	 * not encrypt dynamic WEP control frames.
	 */
	sdata->control_port_protocol = params->crypto.control_port_ethertype;
	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
	sdata->control_port_over_nl80211 =
				params->crypto.control_port_over_nl80211;
	sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local,
							&params->crypto,
							sdata->vif.type);

	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
		vlan->control_port_protocol =
			params->crypto.control_port_ethertype;
		vlan->control_port_no_encrypt =
			params->crypto.control_port_no_encrypt;
		vlan->control_port_over_nl80211 =
			params->crypto.control_port_over_nl80211;
		vlan->encrypt_headroom =
			ieee80211_cs_headroom(sdata->local,
					      &params->crypto,
					      vlan->vif.type);
	}

	sdata->vif.bss_conf.dtim_period = params->dtim_period;
	sdata->vif.bss_conf.enable_beacon = true;
	sdata->vif.bss_conf.allow_p2p_go_ps = sdata->vif.p2p;
	sdata->vif.bss_conf.twt_responder = params->twt_responder;

	sdata->vif.bss_conf.ssid_len = params->ssid_len;
	if (params->ssid_len)
		memcpy(sdata->vif.bss_conf.ssid, params->ssid,
		       params->ssid_len);
	sdata->vif.bss_conf.hidden_ssid =
		(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);

	memset(&sdata->vif.bss_conf.p2p_noa_attr, 0,
	       sizeof(sdata->vif.bss_conf.p2p_noa_attr));
	sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow =
		params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
	if (params->p2p_opp_ps)
		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
					IEEE80211_P2P_OPPPS_ENABLE_BIT;

	err = ieee80211_assign_beacon(sdata, &params->beacon, NULL);
	if (err < 0) {
		ieee80211_vif_release_channel(sdata);
		return err;
	}
	changed |= err;

	err = drv_start_ap(sdata->local, sdata);
	if (err) {
		old = sdata_dereference(sdata->u.ap.beacon, sdata);

		if (old)
			kfree_rcu(old, rcu_head);
		RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
		ieee80211_vif_release_channel(sdata);
		return err;
	}

	ieee80211_recalc_dtim(local, sdata);
	ieee80211_bss_info_change_notify(sdata, changed);

	netif_carrier_on(dev);
	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
		netif_carrier_on(vlan->dev);

	return 0;
}