@@ -5608,6 +5608,169 @@ static bool ieee80211_config_puncturing(struct ieee80211_link_data *link,
5608
5608
return true;
5609
5609
}
5610
5610
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
+
5611
5774
static void ieee80211_rx_mgmt_beacon (struct ieee80211_link_data * link ,
5612
5775
struct ieee80211_hdr * hdr , size_t len ,
5613
5776
struct ieee80211_rx_status * rx_status )
@@ -5937,6 +6100,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
5937
6100
}
5938
6101
}
5939
6102
6103
+ ieee80211_ml_reconfiguration (sdata , elems );
6104
+
5940
6105
ieee80211_link_info_change_notify (sdata , link , changed );
5941
6106
free :
5942
6107
kfree (elems );
@@ -6563,6 +6728,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
6563
6728
ieee80211_csa_connection_drop_work );
6564
6729
INIT_DELAYED_WORK (& ifmgd -> tdls_peer_del_work ,
6565
6730
ieee80211_tdls_peer_del_work );
6731
+ wiphy_delayed_work_init (& ifmgd -> ml_reconf_work ,
6732
+ ieee80211_ml_reconf_work );
6566
6733
timer_setup (& ifmgd -> timer , ieee80211_sta_timer , 0 );
6567
6734
timer_setup (& ifmgd -> bcn_mon_timer , ieee80211_sta_bcn_mon_timer , 0 );
6568
6735
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)
7575
7742
wiphy_work_cancel (sdata -> local -> hw .wiphy ,
7576
7743
& ifmgd -> csa_connection_drop_work );
7577
7744
cancel_delayed_work_sync (& ifmgd -> tdls_peer_del_work );
7745
+ wiphy_delayed_work_cancel (sdata -> local -> hw .wiphy ,
7746
+ & ifmgd -> ml_reconf_work );
7578
7747
7579
7748
sdata_lock (sdata );
7580
7749
if (ifmgd -> assoc_data )
0 commit comments