diff --git a/umac/regulatory/core/src/reg_opclass.c b/umac/regulatory/core/src/reg_opclass.c index d856f1397941..79505cea4fff 100644 --- a/umac/regulatory/core/src/reg_opclass.c +++ b/umac/regulatory/core/src/reg_opclass.c @@ -590,8 +590,8 @@ uint16_t reg_chan_opclass_to_freq(uint8_t chan, op_class_tbl->channels[i]); i++) { if (op_class_tbl->channels[i] == chan) { chan = op_class_tbl->channels[i]; - return (op_class_tbl->start_freq + - (chan * FREQ_TO_CHAN_SCALE)); + return op_class_tbl->start_freq + + (chan * FREQ_TO_CHAN_SCALE); } } reg_err_rl("Channel not found"); @@ -602,4 +602,122 @@ uint16_t reg_chan_opclass_to_freq(uint8_t chan, reg_err_rl("Invalid opclass given as input"); return 0; } + +static void +reg_get_op_class_tbl_by_chan_map(const struct + reg_dmn_op_class_map_t **op_class_tbl) +{ + if (channel_map == channel_map_us) + *op_class_tbl = us_op_class; + else if (channel_map == channel_map_eu) + *op_class_tbl = euro_op_class; + else if (channel_map == channel_map_china) + *op_class_tbl = us_op_class; + else if (channel_map == channel_map_jp) + *op_class_tbl = japan_op_class; + else + *op_class_tbl = global_op_class; +} + +/** + * reg_get_channels_from_opclassmap()- Get channels from the opclass map + * @pdev: Pointer to pdev + * @reg_ap_cap: Pointer to reg_ap_cap + * @index: Pointer to index of reg_ap_cap + * @op_class_tbl: Pointer to op_class_tbl + * @is_opclass_operable: Set true if opclass is operable, else set false + * + * Populate channels from opclass map to reg_ap_cap as supported and + * non-supported channels. + * + * Return: void. + */ +static void +reg_get_channels_from_opclassmap( + struct wlan_objmgr_pdev *pdev, + struct regdmn_ap_cap_opclass_t *reg_ap_cap, + uint8_t index, + const struct reg_dmn_op_class_map_t *op_class_tbl, + bool *is_opclass_operable) +{ + uint8_t op_cls_chan; + qdf_freq_t search_freq; + bool is_freq_present; + uint8_t chan_idx = 0, n_sup_chans = 0, n_unsup_chans = 0; + + while (op_class_tbl->channels[chan_idx]) { + op_cls_chan = op_class_tbl->channels[chan_idx]; + search_freq = op_class_tbl->start_freq + + (FREQ_TO_CHAN_SCALE * op_cls_chan); + is_freq_present = + reg_is_freq_present_in_cur_chan_list(pdev, search_freq); + + if (!is_freq_present) { + reg_ap_cap[index]. + non_sup_chan_list[n_unsup_chans++] = + op_class_tbl->channels[chan_idx]; + reg_ap_cap[index].num_non_supported_chan++; + } else { + reg_ap_cap[index].sup_chan_list[n_sup_chans++] = + op_class_tbl->channels[chan_idx]; + reg_ap_cap[index].num_supported_chan++; + } + + chan_idx++; + } + + if (reg_ap_cap[index].num_supported_chan >= 1) + *is_opclass_operable = true; +} + +QDF_STATUS reg_get_opclass_details(struct wlan_objmgr_pdev *pdev, + struct regdmn_ap_cap_opclass_t *reg_ap_cap, + uint8_t *n_opclasses, + uint8_t max_supp_op_class, + bool global_tbl_lookup) +{ + uint8_t max_reg_power = 0; + const struct reg_dmn_op_class_map_t *op_class_tbl; + uint8_t index = 0; + + if (global_tbl_lookup) + op_class_tbl = global_op_class; + else + reg_get_op_class_tbl_by_chan_map(&op_class_tbl); + + max_reg_power = reg_get_max_tx_power(pdev); + + while (op_class_tbl->op_class && (index < max_supp_op_class)) { + bool is_opclass_operable = false; + + qdf_mem_zero(reg_ap_cap[index].sup_chan_list, + REG_MAX_CHANNELS_PER_OPERATING_CLASS); + reg_ap_cap[index].num_supported_chan = 0; + qdf_mem_zero(reg_ap_cap[index].non_sup_chan_list, + REG_MAX_CHANNELS_PER_OPERATING_CLASS); + reg_ap_cap[index].num_non_supported_chan = 0; + reg_get_channels_from_opclassmap(pdev, + reg_ap_cap, + index, + op_class_tbl, + &is_opclass_operable); + if (is_opclass_operable) { + reg_ap_cap[index].op_class = op_class_tbl->op_class; + reg_ap_cap[index].ch_width = + op_class_tbl->chan_spacing; + reg_ap_cap[index].start_freq = + op_class_tbl->start_freq; + reg_ap_cap[index].max_tx_pwr_dbm = max_reg_power; + reg_ap_cap[index].behav_limit = + op_class_tbl->behav_limit; + index++; + } + + op_class_tbl++; + } + + *n_opclasses = index; + + return QDF_STATUS_SUCCESS; +} #endif diff --git a/umac/regulatory/core/src/reg_opclass.h b/umac/regulatory/core/src/reg_opclass.h index 834f3d789ad5..3b5c8dcf3446 100644 --- a/umac/regulatory/core/src/reg_opclass.h +++ b/umac/regulatory/core/src/reg_opclass.h @@ -89,6 +89,21 @@ uint16_t reg_dmn_set_curr_opclasses(uint8_t num_classes, uint8_t *class); */ uint16_t reg_dmn_get_curr_opclasses(uint8_t *num_classes, uint8_t *class); +/** + * reg_get_opclass_details() - Get details about the current opclass table. + * @pdev: Pointer to pdev. + * @reg_ap_cap: Pointer to reg_ap_cap. + * @n_opclasses: Pointer to number of opclasses. + * @max_supp_op_class: Maximum number of operating classes supported. + * @global_tbl_lookup: Whether to lookup global op class table. + * + * Return: QDF_STATUS_SUCCESS if success, else return QDF_STATUS_FAILURE. + */ +QDF_STATUS reg_get_opclass_details(struct wlan_objmgr_pdev *pdev, + struct regdmn_ap_cap_opclass_t *reg_ap_cap, + uint8_t *n_opclasses, + uint8_t max_supp_op_class, + bool global_tbl_lookup); #ifdef CONFIG_CHAN_FREQ_API /** @@ -195,6 +210,16 @@ static inline void reg_dmn_print_channels_in_opclass(uint8_t *country, { } +static inline +QDF_STATUS reg_get_opclass_details(struct wlan_objmgr_pdev *pdev, + struct regdmn_ap_cap_opclass_t *reg_ap_cap, + uint8_t *n_opclasses, + uint8_t max_supp_op_class, + bool global_tbl_lookup) +{ + return QDF_STATUS_E_FAILURE; +} + #ifdef CONFIG_CHAN_FREQ_API static inline void diff --git a/umac/regulatory/core/src/reg_services_common.c b/umac/regulatory/core/src/reg_services_common.c index 1b61ab3fb577..9f6fb306da54 100644 --- a/umac/regulatory/core/src/reg_services_common.c +++ b/umac/regulatory/core/src/reg_services_common.c @@ -2853,6 +2853,36 @@ enum channel_enum reg_get_chan_enum_for_freq(qdf_freq_t freq) return INVALID_CHANNEL; } +bool +reg_is_freq_present_in_cur_chan_list(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + enum channel_enum chan_enum; + struct regulatory_channel *cur_chan_list; + struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; + + pdev_priv_obj = reg_get_pdev_obj(pdev); + + if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { + reg_err_rl("pdev reg obj is NULL"); + return false; + } + + cur_chan_list = pdev_priv_obj->cur_chan_list; + + for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) + if (cur_chan_list[chan_enum].center_freq == freq) + if ((cur_chan_list[chan_enum].state != + CHANNEL_STATE_DISABLE) && + !(cur_chan_list[chan_enum].chan_flags & + REGULATORY_CHAN_DISABLED)) + return true; + + reg_debug_rl("Channel center frequency %d not found", freq); + + return false; +} + enum channel_state reg_get_channel_state_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq) { @@ -3474,8 +3504,38 @@ enum reg_wifi_band reg_freq_to_band(qdf_freq_t freq) return REG_BAND_6G; return REG_BAND_UNKNOWN; } + #endif /* CONFIG_CHAN_FREQ_API */ +uint8_t reg_get_max_tx_power(struct wlan_objmgr_pdev *pdev) +{ + struct regulatory_channel *cur_chan_list; + struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; + uint8_t i, max_tx_power = 0; + + pdev_priv_obj = reg_get_pdev_obj(pdev); + + if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { + reg_err("reg pdev private obj is NULL"); + return QDF_STATUS_E_FAILURE; + } + + cur_chan_list = pdev_priv_obj->cur_chan_list; + + for (i = 0; i < NUM_CHANNELS; i++) { + if (cur_chan_list[i].state != CHANNEL_STATE_DISABLE && + cur_chan_list[i].chan_flags != REGULATORY_CHAN_DISABLED) { + if (cur_chan_list[i].tx_power > max_tx_power) + max_tx_power = cur_chan_list[i].tx_power; + } + } + + if (!max_tx_power) + reg_err_rl("max_tx_power is zero"); + + return max_tx_power; +} + QDF_STATUS reg_set_ignore_fw_reg_offload_ind(struct wlan_objmgr_psoc *psoc) { struct wlan_regulatory_psoc_priv_obj *psoc_reg; diff --git a/umac/regulatory/core/src/reg_services_common.h b/umac/regulatory/core/src/reg_services_common.h index d8e9d99ac19a..ffd0091ebeb4 100644 --- a/umac/regulatory/core/src/reg_services_common.h +++ b/umac/regulatory/core/src/reg_services_common.h @@ -741,6 +741,21 @@ QDF_STATUS reg_disable_chan_coex(struct wlan_objmgr_pdev *pdev, #endif #ifdef CONFIG_CHAN_FREQ_API +/** + * reg_is_freq_present_in_cur_chan_list() - Check the input frequency + * @pdev: Pointer to pdev + * @freq: Channel center frequency in MHz + * + * Check if the input channel center frequency is present in the current + * channel list + * + * Return: Return true if channel center frequency is present in the current + * channel list, else return false. + */ +bool +reg_is_freq_present_in_cur_chan_list(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); + /** * reg_get_chan_enum_for_freq() - Get channel enum for given channel frequency * @freq: Channel Frequency @@ -935,6 +950,15 @@ reg_get_5g_bonded_channel_for_freq(struct wlan_objmgr_pdev *pdev, **bonded_chan_ptr_ptr); #endif /* CONFIG_CHAN_FREQ_API */ +/** + * reg_get_max_tx_power() - Get maximum tx power from the current channel list + * @pdev: Pointer to pdev + * + * Return: return the value of the maximum tx power in the current channel list + * + */ +uint8_t reg_get_max_tx_power(struct wlan_objmgr_pdev *pdev); + /** * reg_set_ignore_fw_reg_offload_ind() - Set if regdb offload indication * needs to be ignored diff --git a/umac/regulatory/dispatcher/inc/reg_services_public_struct.h b/umac/regulatory/dispatcher/inc/reg_services_public_struct.h index 1ee124461e67..2400e696f935 100644 --- a/umac/regulatory/dispatcher/inc/reg_services_public_struct.h +++ b/umac/regulatory/dispatcher/inc/reg_services_public_struct.h @@ -597,6 +597,30 @@ struct reg_dmn_op_class_map_t { uint8_t channels[REG_MAX_CHANNELS_PER_OPERATING_CLASS]; }; +/** + * struct regdmn_ap_cap_opclass_t: AP Cap operation class table + * @op_class: operating class number + * @ch_width: channel width in MHz + * @start_freq: Starting Frequency in MHz + * @behav_limit: OR of bitmaps of enum behav_limit + * @max_tx_pwr_dbm: Maximum tx power in dbm + * @num_supported_chan: Number of supported channels + * @num_non_supported_chan: Number of non-supported channels + * @sup_chan_list: Array of supported channel numbers + * @non_sup_chan_list: Array of non supported channel numbers + */ +struct regdmn_ap_cap_opclass_t { + uint8_t op_class; + uint8_t ch_width; + qdf_freq_t start_freq; + uint16_t behav_limit; + uint8_t max_tx_pwr_dbm; + uint8_t num_supported_chan; + uint8_t num_non_supported_chan; + uint8_t sup_chan_list[REG_MAX_CHANNELS_PER_OPERATING_CLASS]; + uint8_t non_sup_chan_list[REG_MAX_CHANNELS_PER_OPERATING_CLASS]; +}; + /** * struct reg_dmn_supp_op_classes: operating classes * @num_classes: number of classes diff --git a/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h b/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h index dc892342f6b7..dca8d3b58138 100644 --- a/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h +++ b/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h @@ -605,6 +605,23 @@ uint16_t wlan_reg_dmn_get_curr_opclasses(uint8_t *num_classes, uint8_t *class); +/** + * wlan_reg_get_opclass_details() - Get details about the current opclass table. + * @pdev: Pointer to pdev. + * @reg_ap_cap: Pointer to reg_ap_cap. + * @n_opclasses: Pointer to number of opclasses. + * @max_supp_op_class: Maximum number of operating classes supported. + * @global_tbl_lookup: Whether to lookup global op class tbl. + * + * Return: QDF_STATUS_SUCCESS if success, else return QDF_STATUS_FAILURE. + */ +QDF_STATUS +wlan_reg_get_opclass_details(struct wlan_objmgr_pdev *pdev, + struct regdmn_ap_cap_opclass_t *reg_ap_cap, + uint8_t *n_opclasses, + uint8_t max_supp_op_class, + bool global_tbl_lookup); + /** * wlan_regulatory_init() - init regulatory component * diff --git a/umac/regulatory/dispatcher/src/wlan_reg_services_api.c b/umac/regulatory/dispatcher/src/wlan_reg_services_api.c index 22f2482c6ac6..b21ec96fcf84 100644 --- a/umac/regulatory/dispatcher/src/wlan_reg_services_api.c +++ b/umac/regulatory/dispatcher/src/wlan_reg_services_api.c @@ -302,6 +302,18 @@ uint16_t wlan_reg_dmn_get_curr_opclasses(uint8_t *num_classes, return reg_dmn_get_curr_opclasses(num_classes, class); } +QDF_STATUS +wlan_reg_get_opclass_details(struct wlan_objmgr_pdev *pdev, + struct regdmn_ap_cap_opclass_t *reg_ap_cap, + uint8_t *n_opclasses, + uint8_t max_supp_op_class, + bool global_tbl_lookup) +{ + return reg_get_opclass_details(pdev, reg_ap_cap, n_opclasses, + max_supp_op_class, + global_tbl_lookup); +} + QDF_STATUS wlan_regulatory_init(void) { QDF_STATUS status;