@@ -5608,6 +5608,169 @@ static bool ieee80211_config_puncturing(struct ieee80211_link_data *link,
56085608 return true;
56095609}
56105610
5611+ static void ieee80211_ml_reconf_work (struct wiphy * wiphy ,
5612+ struct wiphy_work * work )
5613+ {
5614+ struct ieee80211_sub_if_data * sdata =
5615+ container_of (work , struct ieee80211_sub_if_data ,
5616+ u .mgd .ml_reconf_work .work );
5617+ u16 new_valid_links , new_active_links , new_dormant_links ;
5618+ int ret ;
5619+
5620+ sdata_lock (sdata );
5621+ if (!sdata -> u .mgd .removed_links ) {
5622+ sdata_unlock (sdata );
5623+ return ;
5624+ }
5625+
5626+ sdata_info (sdata ,
5627+ "MLO Reconfiguration: work: valid=0x%x, removed=0x%x\n" ,
5628+ sdata -> vif .valid_links , sdata -> u .mgd .removed_links );
5629+
5630+ new_valid_links = sdata -> vif .valid_links & ~sdata -> u .mgd .removed_links ;
5631+ if (new_valid_links == sdata -> vif .valid_links ) {
5632+ sdata_unlock (sdata );
5633+ return ;
5634+ }
5635+
5636+ if (!new_valid_links ||
5637+ !(new_valid_links & ~sdata -> vif .dormant_links )) {
5638+ sdata_info (sdata , "No valid links after reconfiguration\n" );
5639+ ret = - EINVAL ;
5640+ goto out ;
5641+ }
5642+
5643+ new_active_links = sdata -> vif .active_links & ~sdata -> u .mgd .removed_links ;
5644+ if (new_active_links != sdata -> vif .active_links ) {
5645+ if (!new_active_links )
5646+ new_active_links =
5647+ BIT (ffs (new_valid_links &
5648+ ~sdata -> vif .dormant_links ) - 1 );
5649+
5650+ ret = __ieee80211_set_active_links (& sdata -> vif ,
5651+ new_active_links );
5652+ if (ret ) {
5653+ sdata_info (sdata ,
5654+ "Failed setting active links\n" );
5655+ goto out ;
5656+ }
5657+ }
5658+
5659+ new_dormant_links = sdata -> vif .dormant_links & ~sdata -> u .mgd .removed_links ;
5660+
5661+ ret = ieee80211_vif_set_links (sdata , new_valid_links ,
5662+ new_dormant_links );
5663+ if (ret )
5664+ sdata_info (sdata , "Failed setting valid links\n" );
5665+
5666+ out :
5667+ if (!ret )
5668+ cfg80211_links_removed (sdata -> dev , sdata -> u .mgd .removed_links );
5669+ else
5670+ ___ieee80211_disconnect (sdata );
5671+
5672+ sdata -> u .mgd .removed_links = 0 ;
5673+
5674+ sdata_unlock (sdata );
5675+ }
5676+
5677+ static void ieee80211_ml_reconfiguration (struct ieee80211_sub_if_data * sdata ,
5678+ struct ieee802_11_elems * elems )
5679+ {
5680+ const struct ieee80211_multi_link_elem * ml ;
5681+ const struct element * sub ;
5682+ size_t ml_len ;
5683+ unsigned long removed_links = 0 ;
5684+ u16 link_removal_timeout [IEEE80211_MLD_MAX_NUM_LINKS ] = {};
5685+ u8 link_id ;
5686+ u32 delay ;
5687+
5688+ if (!ieee80211_vif_is_mld (& sdata -> vif ) || !elems -> ml_reconf )
5689+ return ;
5690+
5691+ ml_len = cfg80211_defragment_element (elems -> ml_reconf_elem ,
5692+ elems -> ie_start ,
5693+ elems -> total_len ,
5694+ elems -> scratch_pos ,
5695+ elems -> scratch + elems -> scratch_len -
5696+ elems -> scratch_pos ,
5697+ WLAN_EID_FRAGMENT );
5698+
5699+ elems -> ml_reconf = (const void * )elems -> scratch_pos ;
5700+ elems -> ml_reconf_len = ml_len ;
5701+ ml = elems -> ml_reconf ;
5702+
5703+ /* Directly parse the sub elements as the common information doesn't
5704+ * hold any useful information.
5705+ */
5706+ for_each_mle_subelement (sub , (u8 * )ml , ml_len ) {
5707+ struct ieee80211_mle_per_sta_profile * prof = (void * )sub -> data ;
5708+ u8 * pos = prof -> variable ;
5709+ u16 control ;
5710+
5711+ if (sub -> id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE )
5712+ continue ;
5713+
5714+ if (!ieee80211_mle_reconf_sta_prof_size_ok (sub -> data ,
5715+ sub -> datalen ))
5716+ return ;
5717+
5718+ control = le16_to_cpu (prof -> control );
5719+ link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID ;
5720+
5721+ removed_links |= BIT (link_id );
5722+
5723+ /* the MAC address should not be included, but handle it */
5724+ if (control &
5725+ IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT )
5726+ pos += 6 ;
5727+
5728+ /* According to Draft P802.11be_D3.0, the control should
5729+ * include the AP Removal Timer present. If the AP Removal Timer
5730+ * is not present assume immediate removal.
5731+ */
5732+ if (control &
5733+ IEEE80211_MLE_STA_RECONF_CONTROL_DELETE_TIMER_PRESENT )
5734+ link_removal_timeout [link_id ] = le16_to_cpu (* (__le16 * )pos );
5735+ }
5736+
5737+ removed_links &= sdata -> vif .valid_links ;
5738+ if (!removed_links ) {
5739+ /* In case the removal was cancelled, abort it */
5740+ if (sdata -> u .mgd .removed_links ) {
5741+ sdata -> u .mgd .removed_links = 0 ;
5742+ wiphy_delayed_work_cancel (sdata -> local -> hw .wiphy ,
5743+ & sdata -> u .mgd .ml_reconf_work );
5744+ }
5745+ return ;
5746+ }
5747+
5748+ delay = 0 ;
5749+ for_each_set_bit (link_id , & removed_links , IEEE80211_MLD_MAX_NUM_LINKS ) {
5750+ struct ieee80211_bss_conf * link_conf =
5751+ sdata_dereference (sdata -> vif .link_conf [link_id ], sdata );
5752+ u32 link_delay ;
5753+
5754+ if (!link_conf ) {
5755+ removed_links &= ~BIT (link_id );
5756+ continue ;
5757+ }
5758+
5759+ link_delay = link_conf -> beacon_int *
5760+ link_removal_timeout [link_id ];
5761+
5762+ if (!delay )
5763+ delay = link_delay ;
5764+ else
5765+ delay = min (delay , link_delay );
5766+ }
5767+
5768+ sdata -> u .mgd .removed_links = removed_links ;
5769+ wiphy_delayed_work_queue (sdata -> local -> hw .wiphy ,
5770+ & sdata -> u .mgd .ml_reconf_work ,
5771+ TU_TO_JIFFIES (delay ));
5772+ }
5773+
56115774static void ieee80211_rx_mgmt_beacon (struct ieee80211_link_data * link ,
56125775 struct ieee80211_hdr * hdr , size_t len ,
56135776 struct ieee80211_rx_status * rx_status )
@@ -5937,6 +6100,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
59376100 }
59386101 }
59396102
6103+ ieee80211_ml_reconfiguration (sdata , elems );
6104+
59406105 ieee80211_link_info_change_notify (sdata , link , changed );
59416106free :
59426107 kfree (elems );
@@ -6563,6 +6728,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
65636728 ieee80211_csa_connection_drop_work );
65646729 INIT_DELAYED_WORK (& ifmgd -> tdls_peer_del_work ,
65656730 ieee80211_tdls_peer_del_work );
6731+ wiphy_delayed_work_init (& ifmgd -> ml_reconf_work ,
6732+ ieee80211_ml_reconf_work );
65666733 timer_setup (& ifmgd -> timer , ieee80211_sta_timer , 0 );
65676734 timer_setup (& ifmgd -> bcn_mon_timer , ieee80211_sta_bcn_mon_timer , 0 );
65686735 timer_setup (& ifmgd -> conn_mon_timer , ieee80211_sta_conn_mon_timer , 0 );
@@ -7575,6 +7742,8 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
75757742 wiphy_work_cancel (sdata -> local -> hw .wiphy ,
75767743 & ifmgd -> csa_connection_drop_work );
75777744 cancel_delayed_work_sync (& ifmgd -> tdls_peer_del_work );
7745+ wiphy_delayed_work_cancel (sdata -> local -> hw .wiphy ,
7746+ & ifmgd -> ml_reconf_work );
75787747
75797748 sdata_lock (sdata );
75807749 if (ifmgd -> assoc_data )
0 commit comments