| // SPDX-License-Identifier: GPL-2.0 |
| /* Implement 802.11d. */ |
| |
| #include "dot11d.h" |
| |
| void rtl8192u_dot11d_init(struct ieee80211_device *ieee) |
| { |
| struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(ieee); |
| |
| dot11d_info->dot11d_enabled = false; |
| |
| dot11d_info->state = DOT11D_STATE_NONE; |
| dot11d_info->country_ie_len = 0; |
| memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); |
| memset(dot11d_info->max_tx_pwr_dbm_list, 0xFF, MAX_CHANNEL_NUMBER + 1); |
| RESET_CIE_WATCHDOG(ieee); |
| |
| netdev_info(ieee->dev, "rtl8192u_dot11d_init()\n"); |
| } |
| EXPORT_SYMBOL(rtl8192u_dot11d_init); |
| |
| /* Reset to the state as we are just entering a regulatory domain. */ |
| void dot11d_reset(struct ieee80211_device *ieee) |
| { |
| u32 i; |
| struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(ieee); |
| /* Clear old channel map */ |
| memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); |
| memset(dot11d_info->max_tx_pwr_dbm_list, 0xFF, MAX_CHANNEL_NUMBER + 1); |
| /* Set new channel map */ |
| for (i = 1; i <= 11; i++) |
| (dot11d_info->channel_map)[i] = 1; |
| |
| for (i = 12; i <= 14; i++) |
| (dot11d_info->channel_map)[i] = 2; |
| |
| dot11d_info->state = DOT11D_STATE_NONE; |
| dot11d_info->country_ie_len = 0; |
| RESET_CIE_WATCHDOG(ieee); |
| } |
| EXPORT_SYMBOL(dot11d_reset); |
| |
| /* |
| * Update country IE from Beacon or Probe Resopnse and configure PHY for |
| * operation in the regulatory domain. |
| * |
| * TODO: Configure Tx power. |
| * Assumption: |
| * 1. IS_DOT11D_ENABLE() is TRUE. |
| * 2. Input IE is an valid one. |
| */ |
| void dot11d_update_country_ie(struct ieee80211_device *dev, u8 *pTaddr, |
| u16 CoutryIeLen, u8 *pCoutryIe) |
| { |
| struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); |
| u8 i, j, NumTriples, MaxChnlNum; |
| struct chnl_txpower_triple *pTriple; |
| |
| memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); |
| memset(dot11d_info->max_tx_pwr_dbm_list, 0xFF, MAX_CHANNEL_NUMBER + 1); |
| MaxChnlNum = 0; |
| NumTriples = (CoutryIeLen - 3) / 3; /* skip 3-byte country string. */ |
| pTriple = (struct chnl_txpower_triple *)(pCoutryIe + 3); |
| for (i = 0; i < NumTriples; i++) { |
| if (MaxChnlNum >= pTriple->first_channel) { |
| /* It is not in a monotonically increasing order, so |
| * stop processing. |
| */ |
| netdev_err(dev->dev, "dot11d_update_country_ie(): Invalid country IE, skip it........1\n"); |
| return; |
| } |
| if (MAX_CHANNEL_NUMBER < (pTriple->first_channel + pTriple->num_channels)) { |
| /* It is not a valid set of channel id, so stop |
| * processing. |
| */ |
| netdev_err(dev->dev, "dot11d_update_country_ie(): Invalid country IE, skip it........2\n"); |
| return; |
| } |
| |
| for (j = 0; j < pTriple->num_channels; j++) { |
| dot11d_info->channel_map[pTriple->first_channel + j] = 1; |
| dot11d_info->max_tx_pwr_dbm_list[pTriple->first_channel + j] = pTriple->max_tx_pwr_dbm; |
| MaxChnlNum = pTriple->first_channel + j; |
| } |
| |
| pTriple = (struct chnl_txpower_triple *)((u8 *)pTriple + 3); |
| } |
| netdev_info(dev->dev, "Channel List:"); |
| for (i = 1; i <= MAX_CHANNEL_NUMBER; i++) |
| if (dot11d_info->channel_map[i] > 0) |
| netdev_info(dev->dev, " %d", i); |
| netdev_info(dev->dev, "\n"); |
| |
| UPDATE_CIE_SRC(dev, pTaddr); |
| |
| dot11d_info->country_ie_len = CoutryIeLen; |
| memcpy(dot11d_info->country_ie_buf, pCoutryIe, CoutryIeLen); |
| dot11d_info->state = DOT11D_STATE_LEARNED; |
| } |
| EXPORT_SYMBOL(dot11d_update_country_ie); |
| |
| u8 dot11d_get_max_tx_pwr_in_dbm(struct ieee80211_device *dev, u8 Channel) |
| { |
| struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); |
| u8 MaxTxPwrInDbm = 255; |
| |
| if (Channel > MAX_CHANNEL_NUMBER) { |
| netdev_err(dev->dev, "dot11d_get_max_tx_pwr_in_dbm(): Invalid Channel\n"); |
| return MaxTxPwrInDbm; |
| } |
| if (dot11d_info->channel_map[Channel]) |
| MaxTxPwrInDbm = dot11d_info->max_tx_pwr_dbm_list[Channel]; |
| |
| return MaxTxPwrInDbm; |
| } |
| EXPORT_SYMBOL(dot11d_get_max_tx_pwr_in_dbm); |
| |
| void dot11d_scan_complete(struct ieee80211_device *dev) |
| { |
| struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); |
| |
| switch (dot11d_info->state) { |
| case DOT11D_STATE_LEARNED: |
| dot11d_info->state = DOT11D_STATE_DONE; |
| break; |
| |
| case DOT11D_STATE_DONE: |
| if (GET_CIE_WATCHDOG(dev) == 0) { |
| /* Reset country IE if previous one is gone. */ |
| dot11d_reset(dev); |
| } |
| break; |
| case DOT11D_STATE_NONE: |
| break; |
| } |
| } |
| EXPORT_SYMBOL(dot11d_scan_complete); |
| |
| int is_legal_channel(struct ieee80211_device *dev, u8 channel) |
| { |
| struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); |
| |
| if (channel > MAX_CHANNEL_NUMBER) { |
| netdev_err(dev->dev, "is_legal_channel(): Invalid Channel\n"); |
| return 0; |
| } |
| if (dot11d_info->channel_map[channel] > 0) |
| return 1; |
| return 0; |
| } |
| EXPORT_SYMBOL(is_legal_channel); |
| |
| int to_legal_channel(struct ieee80211_device *dev, u8 channel) |
| { |
| struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); |
| u8 default_chn = 0; |
| u32 i = 0; |
| |
| for (i = 1; i <= MAX_CHANNEL_NUMBER; i++) { |
| if (dot11d_info->channel_map[i] > 0) { |
| default_chn = i; |
| break; |
| } |
| } |
| |
| if (channel > MAX_CHANNEL_NUMBER) { |
| netdev_err(dev->dev, "is_legal_channel(): Invalid Channel\n"); |
| return default_chn; |
| } |
| |
| if (dot11d_info->channel_map[channel] > 0) |
| return channel; |
| |
| return default_chn; |
| } |
| EXPORT_SYMBOL(to_legal_channel); |