ath6kl: add support for changing contry code

To make it possible to change the country code from user space via nl80211
add handler for reg_notifier. The feature is only enabled when built
time option CONFIG_ATH6KL_REGDOMAIN is enabled, which again depends on
CFG80211_CERTIFICATION_ONUS for certication purposes.

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
Kalle Valo 2012-07-19 16:00:56 +03:00
parent 11f0bfcf73
commit 84841ba29b
5 changed files with 82 additions and 0 deletions

View File

@ -30,3 +30,12 @@ config ATH6KL_DEBUG
depends on ATH6KL depends on ATH6KL
---help--- ---help---
Enables debug support Enables debug support
config ATH6KL_REGDOMAIN
bool "Atheros ath6kl regdomain support"
depends on ATH6KL
depends on CFG80211_CERTIFICATION_ONUS
---help---
Enabling this makes it possible to change the regdomain in
the firmware. This can be only enabled if regulatory requirements
are taken into account.

View File

@ -3458,6 +3458,49 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
ath6kl_cfg80211_stop(vif); ath6kl_cfg80211_stop(vif);
} }
static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy,
struct regulatory_request *request)
{
struct ath6kl *ar = wiphy_priv(wiphy);
u32 rates[IEEE80211_NUM_BANDS];
int ret, i;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"cfg reg_notify %c%c%s%s initiator %d\n",
request->alpha2[0], request->alpha2[1],
request->intersect ? " intersect" : "",
request->processed ? " processed" : "",
request->initiator);
ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2);
if (ret) {
ath6kl_err("failed to set regdomain: %d\n", ret);
return ret;
}
/*
* Firmware will apply the regdomain change only after a scan is
* issued and it will send a WMI_REGDOMAIN_EVENTID when it has been
* changed.
*/
for (i = 0; i < IEEE80211_NUM_BANDS; i++)
if (wiphy->bands[i])
rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
ret = ath6kl_wmi_beginscan_cmd(ar->wmi, 0, WMI_LONG_SCAN, false,
false, 0, ATH6KL_FG_SCAN_INTERVAL,
0, NULL, false, rates);
if (ret) {
ath6kl_err("failed to start scan for a regdomain change: %d\n",
ret);
return ret;
}
return 0;
}
static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
{ {
vif->aggr_cntxt = aggr_init(vif); vif->aggr_cntxt = aggr_init(vif);
@ -3590,6 +3633,10 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
BIT(NL80211_IFTYPE_P2P_CLIENT); BIT(NL80211_IFTYPE_P2P_CLIENT);
} }
if (config_enabled(CONFIG_ATH6KL_REGDOMAIN) &&
test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities))
wiphy->reg_notifier = ath6kl_cfg80211_reg_notify;
/* max num of ssids that can be probed during scanning */ /* max num of ssids that can be probed during scanning */
wiphy->max_scan_ssids = MAX_PROBED_SSIDS; wiphy->max_scan_ssids = MAX_PROBED_SSIDS;

View File

@ -124,6 +124,9 @@ enum ath6kl_fw_capability {
/* Firmware supports TX error rate notification */ /* Firmware supports TX error rate notification */
ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY,
/* supports WMI_SET_REGDOMAIN_CMDID command */
ATH6KL_FW_CAPABILITY_REGDOMAIN,
/* this needs to be last */ /* this needs to be last */
ATH6KL_FW_CAPABILITY_MAX, ATH6KL_FW_CAPABILITY_MAX,
}; };

View File

@ -3216,6 +3216,23 @@ int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enhance)
return ret; return ret;
} }
int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2)
{
struct sk_buff *skb;
struct wmi_set_regdomain_cmd *cmd;
skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
if (!skb)
return -ENOMEM;
cmd = (struct wmi_set_regdomain_cmd *) skb->data;
memcpy(cmd->iso_name, alpha2, 2);
return ath6kl_wmi_cmd_send(wmi, 0, skb,
WMI_SET_REGDOMAIN_CMDID,
NO_SYNC_WMIFLAG);
}
s32 ath6kl_wmi_get_rate(s8 rate_index) s32 ath6kl_wmi_get_rate(s8 rate_index)
{ {
if (rate_index == RATE_AUTO) if (rate_index == RATE_AUTO)

View File

@ -1042,6 +1042,11 @@ struct wmi_sta_bmiss_enhance_cmd {
u8 enable; u8 enable;
} __packed; } __packed;
struct wmi_set_regdomain_cmd {
u8 length;
u8 iso_name[2];
} __packed;
/* WMI_SET_POWER_MODE_CMDID */ /* WMI_SET_POWER_MODE_CMDID */
enum wmi_power_mode { enum wmi_power_mode {
REC_POWER = 0x01, REC_POWER = 0x01,
@ -2640,6 +2645,7 @@ int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx,
int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable); int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable);
int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx, int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx,
u32 rate, u32 pkts, u32 intvl); u32 rate, u32 pkts, u32 intvl);
int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2);
/* AP mode uAPSD */ /* AP mode uAPSD */
int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable);