@@ -184,9 +184,11 @@ int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func,
184
184
}
185
185
IWL_EXPORT_SYMBOL (iwl_acpi_get_dsm_u32 );
186
186
187
- union acpi_object * iwl_acpi_get_wifi_pkg (struct device * dev ,
188
- union acpi_object * data ,
189
- int data_size , int * tbl_rev )
187
+ union acpi_object * iwl_acpi_get_wifi_pkg_range (struct device * dev ,
188
+ union acpi_object * data ,
189
+ int min_data_size ,
190
+ int max_data_size ,
191
+ int * tbl_rev )
190
192
{
191
193
int i ;
192
194
union acpi_object * wifi_pkg ;
@@ -196,7 +198,7 @@ union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
196
198
* describes the domain, and one more entry, otherwise there's
197
199
* no point in reading it.
198
200
*/
199
- if (WARN_ON_ONCE (data_size < 2 ))
201
+ if (WARN_ON_ONCE (min_data_size < 2 || min_data_size > max_data_size ))
200
202
return ERR_PTR (- EINVAL );
201
203
202
204
/*
@@ -222,7 +224,8 @@ union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
222
224
223
225
/* skip entries that are not a package with the right size */
224
226
if (wifi_pkg -> type != ACPI_TYPE_PACKAGE ||
225
- wifi_pkg -> package .count != data_size )
227
+ wifi_pkg -> package .count < min_data_size ||
228
+ wifi_pkg -> package .count > max_data_size )
226
229
continue ;
227
230
228
231
domain = & wifi_pkg -> package .elements [0 ];
@@ -236,7 +239,7 @@ union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
236
239
found :
237
240
return wifi_pkg ;
238
241
}
239
- IWL_EXPORT_SYMBOL (iwl_acpi_get_wifi_pkg );
242
+ IWL_EXPORT_SYMBOL (iwl_acpi_get_wifi_pkg_range );
240
243
241
244
int iwl_acpi_get_tas (struct iwl_fw_runtime * fwrt ,
242
245
__le32 * block_list_array ,
@@ -707,49 +710,103 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt)
707
710
{
708
711
union acpi_object * wifi_pkg , * data ;
709
712
int i , j , k , ret , tbl_rev ;
710
- int idx = 1 ; /* start from one to skip the domain */
711
- u8 num_bands ;
713
+ u8 num_bands , num_profiles ;
714
+ static const struct {
715
+ u8 revisions ;
716
+ u8 bands ;
717
+ u8 profiles ;
718
+ u8 min_profiles ;
719
+ } rev_data [] = {
720
+ {
721
+ .revisions = BIT (3 ),
722
+ .bands = ACPI_GEO_NUM_BANDS_REV2 ,
723
+ .profiles = ACPI_NUM_GEO_PROFILES_REV3 ,
724
+ .min_profiles = 3 ,
725
+ },
726
+ {
727
+ .revisions = BIT (2 ),
728
+ .bands = ACPI_GEO_NUM_BANDS_REV2 ,
729
+ .profiles = ACPI_NUM_GEO_PROFILES ,
730
+ },
731
+ {
732
+ .revisions = BIT (0 ) | BIT (1 ),
733
+ .bands = ACPI_GEO_NUM_BANDS_REV0 ,
734
+ .profiles = ACPI_NUM_GEO_PROFILES ,
735
+ },
736
+ };
737
+ int idx ;
738
+ /* start from one to skip the domain */
739
+ int entry_idx = 1 ;
740
+
741
+ BUILD_BUG_ON (ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3 );
742
+ BUILD_BUG_ON (ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES );
712
743
713
744
data = iwl_acpi_get_object (fwrt -> dev , ACPI_WGDS_METHOD );
714
745
if (IS_ERR (data ))
715
746
return PTR_ERR (data );
716
747
717
- /* start by trying to read revision 2 */
718
- wifi_pkg = iwl_acpi_get_wifi_pkg (fwrt -> dev , data ,
719
- ACPI_WGDS_WIFI_DATA_SIZE_REV2 ,
720
- & tbl_rev );
721
- if (!IS_ERR (wifi_pkg )) {
722
- if (tbl_rev != 2 ) {
723
- ret = PTR_ERR (wifi_pkg );
724
- goto out_free ;
725
- }
726
-
727
- num_bands = ACPI_GEO_NUM_BANDS_REV2 ;
728
-
729
- goto read_table ;
730
- }
731
-
732
- /* then try revision 0 (which is the same as 1) */
733
- wifi_pkg = iwl_acpi_get_wifi_pkg (fwrt -> dev , data ,
734
- ACPI_WGDS_WIFI_DATA_SIZE_REV0 ,
735
- & tbl_rev );
736
- if (!IS_ERR (wifi_pkg )) {
737
- if (tbl_rev != 0 && tbl_rev != 1 ) {
738
- ret = PTR_ERR (wifi_pkg );
739
- goto out_free ;
748
+ /* read the highest revision we understand first */
749
+ for (idx = 0 ; idx < ARRAY_SIZE (rev_data ); idx ++ ) {
750
+ /* min_profiles != 0 requires num_profiles header */
751
+ u32 hdr_size = 1 + !!rev_data [idx ].min_profiles ;
752
+ u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *
753
+ rev_data [idx ].bands ;
754
+ u32 max_size = hdr_size + profile_size * rev_data [idx ].profiles ;
755
+ u32 min_size ;
756
+
757
+ if (!rev_data [idx ].min_profiles )
758
+ min_size = max_size ;
759
+ else
760
+ min_size = hdr_size +
761
+ profile_size * rev_data [idx ].min_profiles ;
762
+
763
+ wifi_pkg = iwl_acpi_get_wifi_pkg_range (fwrt -> dev , data ,
764
+ min_size , max_size ,
765
+ & tbl_rev );
766
+ if (!IS_ERR (wifi_pkg )) {
767
+ if (!(BIT (tbl_rev ) & rev_data [idx ].revisions ))
768
+ continue ;
769
+
770
+ num_bands = rev_data [idx ].bands ;
771
+ num_profiles = rev_data [idx ].profiles ;
772
+
773
+ if (rev_data [idx ].min_profiles ) {
774
+ /* read header that says # of profiles */
775
+ union acpi_object * entry ;
776
+
777
+ entry = & wifi_pkg -> package .elements [entry_idx ];
778
+ entry_idx ++ ;
779
+ if (entry -> type != ACPI_TYPE_INTEGER ||
780
+ entry -> integer .value > num_profiles ) {
781
+ ret = - EINVAL ;
782
+ goto out_free ;
783
+ }
784
+ num_profiles = entry -> integer .value ;
785
+
786
+ /*
787
+ * this also validates >= min_profiles since we
788
+ * otherwise wouldn't have gotten the data when
789
+ * looking up in ACPI
790
+ */
791
+ if (wifi_pkg -> package .count !=
792
+ min_size + profile_size * num_profiles ) {
793
+ ret = - EINVAL ;
794
+ goto out_free ;
795
+ }
796
+ }
797
+ goto read_table ;
740
798
}
741
-
742
- num_bands = ACPI_GEO_NUM_BANDS_REV0 ;
743
-
744
- goto read_table ;
745
799
}
746
800
747
- ret = PTR_ERR (wifi_pkg );
801
+ if (idx < ARRAY_SIZE (rev_data ))
802
+ ret = PTR_ERR (wifi_pkg );
803
+ else
804
+ ret = - ENOENT ;
748
805
goto out_free ;
749
806
750
807
read_table :
751
808
fwrt -> geo_rev = tbl_rev ;
752
- for (i = 0 ; i < ACPI_NUM_GEO_PROFILES ; i ++ ) {
809
+ for (i = 0 ; i < num_profiles ; i ++ ) {
753
810
for (j = 0 ; j < ACPI_GEO_NUM_BANDS_REV2 ; j ++ ) {
754
811
union acpi_object * entry ;
755
812
@@ -762,7 +819,8 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt)
762
819
fwrt -> geo_profiles [i ].bands [j ].max =
763
820
fwrt -> geo_profiles [i ].bands [1 ].max ;
764
821
} else {
765
- entry = & wifi_pkg -> package .elements [idx ++ ];
822
+ entry = & wifi_pkg -> package .elements [entry_idx ];
823
+ entry_idx ++ ;
766
824
if (entry -> type != ACPI_TYPE_INTEGER ||
767
825
entry -> integer .value > U8_MAX ) {
768
826
ret = - EINVAL ;
@@ -779,7 +837,8 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt)
779
837
fwrt -> geo_profiles [i ].bands [j ].chains [k ] =
780
838
fwrt -> geo_profiles [i ].bands [1 ].chains [k ];
781
839
} else {
782
- entry = & wifi_pkg -> package .elements [idx ++ ];
840
+ entry = & wifi_pkg -> package .elements [entry_idx ];
841
+ entry_idx ++ ;
783
842
if (entry -> type != ACPI_TYPE_INTEGER ||
784
843
entry -> integer .value > U8_MAX ) {
785
844
ret = - EINVAL ;
@@ -822,14 +881,15 @@ bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt)
822
881
IWL_EXPORT_SYMBOL (iwl_sar_geo_support );
823
882
824
883
int iwl_sar_geo_init (struct iwl_fw_runtime * fwrt ,
825
- struct iwl_per_chain_offset * table , u32 n_bands )
884
+ struct iwl_per_chain_offset * table ,
885
+ u32 n_bands , u32 n_profiles )
826
886
{
827
887
int i , j ;
828
888
829
889
if (!iwl_sar_geo_support (fwrt ))
830
890
return - EOPNOTSUPP ;
831
891
832
- for (i = 0 ; i < ACPI_NUM_GEO_PROFILES ; i ++ ) {
892
+ for (i = 0 ; i < n_profiles ; i ++ ) {
833
893
for (j = 0 ; j < n_bands ; j ++ ) {
834
894
struct iwl_per_chain_offset * chain =
835
895
& table [i * n_bands + j ];
0 commit comments