From 1c69cdfcef1068375f898b77f5620bd56d3bf755 Mon Sep 17 00:00:00 2001 From: Lincoln Tran Date: Tue, 11 May 2021 13:37:32 -0700 Subject: [PATCH] qcacmn: Add second current channel list For some concurrency scenarios, there is a need to have each session operate in independent power modes. To achieve this, add a second current channel list to store info for the secondary power mode. Also add the APIs to read from the secondary channel list. Change-Id: Ib1bd712645de05786ea6d4bbfe6163c385bdfeaa CRs-fixed: 2944483 --- .../regulatory/core/src/reg_build_chan_list.c | 77 +++++++++++-- .../regulatory/core/src/reg_build_chan_list.h | 21 ++++ umac/regulatory/core/src/reg_priv_objs.h | 5 + .../regulatory/core/src/reg_services_common.c | 101 ++++++++++++++++++ .../regulatory/core/src/reg_services_common.h | 53 +++++++++ .../dispatcher/inc/wlan_reg_services_api.h | 66 ++++++++++++ .../dispatcher/src/wlan_reg_services_api.c | 41 +++++++ 7 files changed, 356 insertions(+), 8 deletions(-) diff --git a/umac/regulatory/core/src/reg_build_chan_list.c b/umac/regulatory/core/src/reg_build_chan_list.c index d71603ffa3a2..0f517dd0ad53 100644 --- a/umac/regulatory/core/src/reg_build_chan_list.c +++ b/umac/regulatory/core/src/reg_build_chan_list.c @@ -1032,16 +1032,31 @@ reg_append_mas_chan_list_for_6g(struct wlan_regulatory_pdev_priv_obj return; } - master_chan_list_6g_client = pdev_priv_obj->mas_chan_list_6g_client - [pdev_priv_obj->reg_cur_6g_ap_pwr_type] + master_chan_list_6g_client = + pdev_priv_obj->mas_chan_list_6g_client[REG_INDOOR_AP] [pdev_priv_obj->reg_cur_6g_client_mobility_type]; - qdf_mem_copy(&pdev_priv_obj->mas_chan_list[MIN_6GHZ_CHANNEL], - master_chan_list_6g_client, - NUM_6GHZ_CHANNELS * - sizeof(struct regulatory_channel)); + qdf_mem_copy(&pdev_priv_obj->mas_chan_list[MIN_6GHZ_CHANNEL], + master_chan_list_6g_client, + NUM_6GHZ_CHANNELS * + sizeof(struct regulatory_channel)); } -#else + +static void +reg_populate_secondary_cur_chan_list(struct wlan_regulatory_pdev_priv_obj + *pdev_priv_obj) +{ + qdf_mem_copy(pdev_priv_obj->secondary_cur_chan_list, + pdev_priv_obj->mas_chan_list, + (NUM_CHANNELS - NUM_6GHZ_CHANNELS) * + sizeof(struct regulatory_channel)); + qdf_mem_copy(&pdev_priv_obj-> + secondary_cur_chan_list[MIN_6GHZ_CHANNEL], + pdev_priv_obj->mas_chan_list_6g_ap + [pdev_priv_obj->reg_cur_6g_ap_pwr_type], + NUM_6GHZ_CHANNELS * sizeof(struct regulatory_channel)); +} +#else /* CONFIG_REG_CLIENT */ static void reg_append_mas_chan_list_for_6g(struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) @@ -1057,6 +1072,12 @@ reg_append_mas_chan_list_for_6g(struct wlan_regulatory_pdev_priv_obj pdev_priv_obj->mas_chan_list_6g_ap[ap_pwr_type], NUM_6GHZ_CHANNELS * sizeof(struct regulatory_channel)); } + +static inline void +reg_populate_secondary_cur_chan_list(struct wlan_regulatory_pdev_priv_obj + *pdev_priv_obj) +{ +} #endif /* CONFIG_REG_CLIENT */ static void reg_copy_6g_cur_mas_chan_list_to_cmn( @@ -1065,7 +1086,7 @@ static void reg_copy_6g_cur_mas_chan_list_to_cmn( if (pdev_priv_obj->is_6g_channel_list_populated) reg_append_mas_chan_list_for_6g(pdev_priv_obj); } -#else +#else /* CONFIG_BAND_6GHZ */ static inline void reg_copy_6g_cur_mas_chan_list_to_cmn( struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) @@ -1077,6 +1098,23 @@ reg_append_mas_chan_list_for_6g( struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) { } + +#ifdef CONFIG_REG_CLIENT +static void +reg_populate_secondary_cur_chan_list(struct wlan_regulatory_pdev_priv_obj + *pdev_priv_obj) +{ + qdf_mem_copy(pdev_priv_obj->secondary_cur_chan_list, + pdev_priv_obj->mas_chan_list, + NUM_CHANNELS * sizeof(struct regulatory_channel)); +} +#else /* CONFIG_REG_CLIENT */ +static inline void +reg_populate_secondary_cur_chan_list(struct wlan_regulatory_pdev_priv_obj + *pdev_priv_obj) +{ +} +#endif /* CONFIG_REG_CLIENT */ #endif /* CONFIG_BAND_6GHZ */ void reg_compute_pdev_current_chan_list(struct wlan_regulatory_pdev_priv_obj @@ -1087,6 +1125,8 @@ void reg_compute_pdev_current_chan_list(struct wlan_regulatory_pdev_priv_obj qdf_mem_copy(pdev_priv_obj->cur_chan_list, pdev_priv_obj->mas_chan_list, NUM_CHANNELS * sizeof(struct regulatory_channel)); + reg_populate_secondary_cur_chan_list(pdev_priv_obj); + reg_modify_chan_list_for_freq_range(pdev_priv_obj->cur_chan_list, pdev_priv_obj->range_2g_low, pdev_priv_obj->range_2g_high, @@ -2157,3 +2197,24 @@ QDF_STATUS reg_get_current_chan_list(struct wlan_objmgr_pdev *pdev, return QDF_STATUS_SUCCESS; } + +#ifdef CONFIG_REG_CLIENT +QDF_STATUS +reg_get_secondary_current_chan_list(struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *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("reg pdev private obj is NULL"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_copy(chan_list, pdev_priv_obj->secondary_cur_chan_list, + NUM_CHANNELS * sizeof(struct regulatory_channel)); + + return QDF_STATUS_SUCCESS; +} +#endif diff --git a/umac/regulatory/core/src/reg_build_chan_list.h b/umac/regulatory/core/src/reg_build_chan_list.h index d47502e0f815..4245e3a73c9f 100644 --- a/umac/regulatory/core/src/reg_build_chan_list.h +++ b/umac/regulatory/core/src/reg_build_chan_list.h @@ -98,9 +98,30 @@ reg_process_master_chan_list_ext(struct cur_regulatory_info *reg_info); */ QDF_STATUS reg_process_master_chan_list(struct cur_regulatory_info *reg_info); +/** + * reg_get_current_chan_list() - provide the pdev current channel list + * @pdev: pdev pointer + * @chan_list: channel list pointer + * + * Return: QDF_STATUS + */ QDF_STATUS reg_get_current_chan_list(struct wlan_objmgr_pdev *pdev, struct regulatory_channel *chan_list); +#ifdef CONFIG_REG_CLIENT +/** + * reg_get_secondary_current_chan_list() - provide the pdev secondary current + * channel list + * @pdev: pdev pointer + * @chan_list: channel list pointer + * + * Return: QDF_STATUS + */ +QDF_STATUS +reg_get_secondary_current_chan_list(struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *chan_list); +#endif + /** * reg_update_nol_history_ch() - Set nol-history flag for the channels in the * list. diff --git a/umac/regulatory/core/src/reg_priv_objs.h b/umac/regulatory/core/src/reg_priv_objs.h index a8234ac29c6c..99b5e90983bb 100644 --- a/umac/regulatory/core/src/reg_priv_objs.h +++ b/umac/regulatory/core/src/reg_priv_objs.h @@ -175,6 +175,8 @@ struct wlan_regulatory_psoc_priv_obj { /** * struct wlan_regulatory_pdev_priv_obj - wlan regulatory pdev private object * @cur_chan_list: current channel list, includes 6G channels + * @secondary_cur_chan_list: secondary current channel list, for concurrency + * situations * @mas_chan_list: master channel list * @is_6g_channel_list_populated: indicates the channel lists are populated * @mas_chan_list_6g_ap: master channel list for 6G AP, includes all power types @@ -193,6 +195,9 @@ struct wlan_regulatory_psoc_priv_obj { */ struct wlan_regulatory_pdev_priv_obj { struct regulatory_channel cur_chan_list[NUM_CHANNELS]; +#ifdef CONFIG_REG_CLIENT + struct regulatory_channel secondary_cur_chan_list[NUM_CHANNELS]; +#endif struct regulatory_channel mas_chan_list[NUM_CHANNELS]; #ifdef CONFIG_BAND_6GHZ bool is_6g_channel_list_populated; diff --git a/umac/regulatory/core/src/reg_services_common.c b/umac/regulatory/core/src/reg_services_common.c index c69db53e3d34..758b1dff46f4 100644 --- a/umac/regulatory/core/src/reg_services_common.c +++ b/umac/regulatory/core/src/reg_services_common.c @@ -2515,6 +2515,35 @@ bool reg_is_freq_indoor(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq) REGULATORY_CHAN_INDOOR_ONLY); } +#ifdef CONFIG_REG_CLIENT +bool reg_is_freq_indoor_in_secondary_list(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + struct regulatory_channel *secondary_cur_chan_list; + struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; + enum channel_enum chan_enum; + + pdev_priv_obj = reg_get_pdev_obj(pdev); + + if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { + reg_err("reg pdev priv obj is NULL"); + return false; + } + + chan_enum = reg_get_chan_enum_for_freq(freq); + + if (chan_enum == INVALID_CHANNEL) { + reg_err_rl("Invalid chan enum %d", chan_enum); + return false; + } + + secondary_cur_chan_list = pdev_priv_obj->secondary_cur_chan_list; + + return (secondary_cur_chan_list[chan_enum].chan_flags & + REGULATORY_CHAN_INDOOR_ONLY); +} +#endif + #ifdef CONFIG_BAND_6GHZ bool reg_is_6ghz_chan_freq(uint16_t freq) { @@ -3259,6 +3288,54 @@ static uint32_t reg_get_channel_flags_for_freq(struct wlan_objmgr_pdev *pdev, return pdev_priv_obj->cur_chan_list[chan_enum].chan_flags; } +#ifdef CONFIG_REG_CLIENT +enum channel_state reg_get_channel_state_from_secondary_list_for_freq( + struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + enum channel_enum ch_idx; + struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; + + ch_idx = reg_get_chan_enum_for_freq(freq); + + if (ch_idx == INVALID_CHANNEL) + return CHANNEL_STATE_INVALID; + + pdev_priv_obj = reg_get_pdev_obj(pdev); + + if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { + reg_err("pdev reg obj is NULL"); + return CHANNEL_STATE_INVALID; + } + + return pdev_priv_obj->secondary_cur_chan_list[ch_idx].state; +} + +static uint32_t reg_get_channel_flags_from_secondary_list_for_freq( + struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + enum channel_enum chan_enum; + struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; + + chan_enum = reg_get_chan_enum_for_freq(freq); + + if (chan_enum == INVALID_CHANNEL) { + reg_err("chan freq is not valid"); + return REGULATORY_CHAN_INVALID; + } + + pdev_priv_obj = reg_get_pdev_obj(pdev); + + if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { + reg_err("pdev reg obj is NULL"); + return REGULATORY_CHAN_INVALID; + } + + return pdev_priv_obj->secondary_cur_chan_list[chan_enum].chan_flags; +} +#endif + /** * reg_get_5g_bonded_chan_array_for_freq()- Return the channel state for a * 5G or 6G channel frequency based on the bonded channel. @@ -3772,6 +3849,17 @@ bool reg_is_dfs_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq) } #ifdef CONFIG_REG_CLIENT +bool reg_is_dfs_in_secondary_list_for_freq(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + uint32_t chan_flags; + + chan_flags = reg_get_channel_flags_from_secondary_list_for_freq(pdev, + freq); + + return chan_flags & REGULATORY_CHAN_RADAR; +} + /** * reg_get_psoc_mas_chan_list () - Get psoc master channel list * @pdev: pointer to pdev object @@ -3952,6 +4040,19 @@ bool reg_is_disable_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq) return ch_state == CHANNEL_STATE_DISABLE; } +#ifdef CONFIG_REG_CLIENT +bool reg_is_disable_in_secondary_list_for_freq(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + enum channel_state ch_state; + + ch_state = reg_get_channel_state_from_secondary_list_for_freq(pdev, + freq); + + return ch_state == CHANNEL_STATE_DISABLE; +} +#endif + bool reg_is_passive_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq) { uint32_t chan_flags; diff --git a/umac/regulatory/core/src/reg_services_common.h b/umac/regulatory/core/src/reg_services_common.h index d7d04f7af58f..9ded64f298d0 100644 --- a/umac/regulatory/core/src/reg_services_common.h +++ b/umac/regulatory/core/src/reg_services_common.h @@ -588,6 +588,19 @@ bool reg_is_range_overlap_5g(qdf_freq_t low_freq, qdf_freq_t high_freq); */ bool reg_is_freq_indoor(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq); +#ifdef CONFIG_REG_CLIENT +/** + * reg_is_freq_indoor_in_secondary_list() - Check if the input frequency is + * an indoor frequency in the secondary channel list + * @pdev: Pointer to pdev. + * @freq: Channel frequency. + * + * Return: Return true if the input frequency is indoor, else false. + */ +bool reg_is_freq_indoor_in_secondary_list(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); +#endif + #ifdef CONFIG_BAND_6GHZ /** * reg_is_6ghz_chan_freq() - Check if the given channel frequency is 6GHz @@ -986,6 +999,20 @@ reg_get_channel_list_with_power_for_freq(struct wlan_objmgr_pdev *pdev, enum channel_state reg_get_channel_state_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq); +#ifdef CONFIG_REG_CLIENT +/** + * reg_get_channel_state_from_secondary_list_for_freq() - Get channel state + * from secondary regulatory current channel list + * @pdev: Pointer to pdev + * @freq: channel center frequency. + * + * Return: channel state + */ +enum channel_state reg_get_channel_state_from_secondary_list_for_freq( + struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); +#endif + /** * reg_get_5g_bonded_channel_state_for_freq() - Get channel state for * 5G bonded channel using the channel frequency @@ -1064,6 +1091,19 @@ void reg_update_nol_ch_for_freq(struct wlan_objmgr_pdev *pdev, */ bool reg_is_dfs_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq); +#ifdef CONFIG_REG_CLIENT +/** + * reg_is_dfs_in_secondary_list_for_freq() - Checks the channel state for DFS + * from the secondary channel list + * @pdev: pdev ptr + * @freq: Channel center frequency + * + * Return: true or false + */ +bool reg_is_dfs_in_secondary_list_for_freq(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); +#endif + /** * reg_chan_freq_is_49ghz() - Check if the input channel center frequency is * 4.9GHz @@ -1157,6 +1197,19 @@ reg_get_5g_bonded_channel_for_freq(struct wlan_objmgr_pdev *pdev, */ bool reg_is_disable_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq); +#ifdef CONFIG_REG_CLIENT +/** + * reg_is_disable_in_secondary_list_for_freq() - Check if the given channel + * frequency is in disable state + * @pdev: Pointer to pdev + * @freq: Channel frequency + * + * Return: True if channel state is disabled, else false + */ +bool reg_is_disable_in_secondary_list_for_freq(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); +#endif + /** * reg_is_passive_for_freq() - Check if the given channel frequency is in * passive state diff --git a/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h b/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h index 1d8d2b945c8b..860ab0422a9b 100644 --- a/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h +++ b/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h @@ -167,6 +167,19 @@ bool wlan_reg_is_range_overlap_5g(qdf_freq_t low_freq, qdf_freq_t high_freq); */ bool wlan_reg_is_freq_indoor(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq); +#ifdef CONFIG_REG_CLIENT +/** + * wlan_reg_is_freq_indoor_in_secondary_list() - Check if the input frequency is + * an indoor frequency in the secondary list + * @pdev: Pointer to pdev. + * @freq: Channel frequency. + * + * Return: Return true if a frequency is indoor, else false. + */ +bool wlan_reg_is_freq_indoor_in_secondary_list(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); +#endif + #ifdef CONFIG_BAND_6GHZ /** * wlan_reg_is_6ghz_chan_freq() - Check if the given channel frequency is 6GHz @@ -658,6 +671,20 @@ enum channel_state wlan_reg_get_bonded_channel_state( enum phy_ch_width bw, uint8_t sec_ch); #endif /* CONFIG_CHAN_NUM_API */ +#ifdef CONFIG_REG_CLIENT +/** + * wlan_reg_get_secondary_current_chan_list() - provide the pdev secondary + * current channel list + * @pdev: pdev pointer + * @chan_list: channel list pointer + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_reg_get_secondary_current_chan_list( + struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *chan_list); +#endif + /** * wlan_reg_get_bonded_channel_state_for_freq() - Get bonded channel freq state * @freq: channel frequency @@ -1280,6 +1307,20 @@ enum channel_state wlan_reg_get_channel_state_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq); +#ifdef CONFIG_REG_CLIENT +/** + * wlan_reg_get_channel_state_from_secondary_list_for_freq() - Get channel state + * from secondary regulatory current channel list + * @pdev: Pointer to pdev + * @freq: channel center frequency. + * + * Return: channel state + */ +enum channel_state wlan_reg_get_channel_state_from_secondary_list_for_freq( + struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); +#endif + /** * wlan_reg_set_channel_params_for_freq() - Sets channel parameteres for * given bandwidth @@ -1356,6 +1397,31 @@ bool wlan_reg_is_passive_or_disable_for_freq(struct wlan_objmgr_pdev *pdev, bool wlan_reg_is_disable_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq); +#ifdef CONFIG_REG_CLIENT +/** + * wlan_reg_is_disable_in_secondary_list_for_freq() - Checks in the secondary + * channel list to see if chan state is disabled + * @pdev: pdev ptr + * @freq: Channel center frequency + * + * Return: true or false + */ +bool wlan_reg_is_disable_in_secondary_list_for_freq( + struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); + +/** + * wlan_reg_is_dfs_in_secondary_list_for_freq() - hecks the channel state for + * DFS from the secondary channel list + * @pdev: pdev ptr + * @freq: Channel center frequency + * + * Return: true or false + */ +bool wlan_reg_is_dfs_in_secondary_list_for_freq(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq); +#endif + /** * wlan_reg_is_passive_for_freq() - Check the channel flags to see if the * passive flag is set diff --git a/umac/regulatory/dispatcher/src/wlan_reg_services_api.c b/umac/regulatory/dispatcher/src/wlan_reg_services_api.c index 8e8b853b52a3..99b51413c405 100644 --- a/umac/regulatory/dispatcher/src/wlan_reg_services_api.c +++ b/umac/regulatory/dispatcher/src/wlan_reg_services_api.c @@ -204,6 +204,15 @@ QDF_STATUS wlan_reg_get_current_chan_list(struct wlan_objmgr_pdev *pdev, qdf_export_symbol(wlan_reg_get_current_chan_list); +#ifdef CONFIG_REG_CLIENT +QDF_STATUS wlan_reg_get_secondary_current_chan_list( + struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *chan_list) +{ + return reg_get_secondary_current_chan_list(pdev, chan_list); +} +#endif + /** * wlan_reg_get_bw_value() - give bandwidth value * bw: bandwidth enum @@ -840,6 +849,14 @@ bool wlan_reg_is_freq_indoor(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq) return reg_is_freq_indoor(pdev, freq); } +#ifdef CONFIG_REG_CLIENT +bool wlan_reg_is_freq_indoor_in_secondary_list(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + return reg_is_freq_indoor_in_secondary_list(pdev, freq); +} +#endif + #ifdef CONFIG_BAND_6GHZ bool wlan_reg_is_6ghz_chan_freq(uint16_t freq) { @@ -1059,6 +1076,21 @@ bool wlan_reg_is_disable_for_freq(struct wlan_objmgr_pdev *pdev, return reg_is_disable_for_freq(pdev, freq); } +#ifdef CONFIG_REG_CLIENT +bool wlan_reg_is_disable_in_secondary_list_for_freq( + struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + return reg_is_disable_in_secondary_list_for_freq(pdev, freq); +} + +bool wlan_reg_is_dfs_in_secondary_list_for_freq(struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + return reg_is_dfs_in_secondary_list_for_freq(pdev, freq); +} +#endif + bool wlan_reg_is_passive_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq) { @@ -1121,6 +1153,15 @@ wlan_reg_get_channel_state_for_freq(struct wlan_objmgr_pdev *pdev, return reg_get_channel_state_for_freq(pdev, freq); } +#ifdef CONFIG_REG_CLIENT +enum channel_state wlan_reg_get_channel_state_from_secondary_list_for_freq( + struct wlan_objmgr_pdev *pdev, + qdf_freq_t freq) +{ + return reg_get_channel_state_from_secondary_list_for_freq(pdev, freq); +} +#endif + uint8_t wlan_reg_get_channel_reg_power_for_freq(struct wlan_objmgr_pdev *pdev, qdf_freq_t freq) {