Commit 6a7c9bad authored by Jouni Malinen's avatar Jouni Malinen Committed by Kalle Valo

ath6kl: Add functionality for starting AP mode

Use cfg80211 add/del_beacon callbacks for starting/stopping AP mode and
set_beacon to update AP configuration (mainly, to update Beacon and
Probe Response IEs).
Signed-off-by: default avatarJouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 78fc4856
......@@ -1435,6 +1435,181 @@ static int ar6k_cfg80211_suspend(struct wiphy *wiphy,
}
#endif
static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type)
{
struct ath6kl *ar = ath6kl_priv(dev);
if (!ath6kl_cfg80211_ready(ar))
return -EIO;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
__func__, chan->center_freq, chan->hw_value);
ar->next_chan = chan->center_freq;
return 0;
}
static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *info, bool add)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ieee80211_mgmt *mgmt;
u8 *ies;
int ies_len;
struct wmi_connect_cmd p;
int res;
int i;
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
if (!ath6kl_cfg80211_ready(ar))
return -EIO;
if (ar->next_mode != AP_NETWORK)
return -EOPNOTSUPP;
if (info->beacon_ies) {
res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON,
info->beacon_ies,
info->beacon_ies_len);
if (res)
return res;
}
if (info->proberesp_ies) {
res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP,
info->proberesp_ies,
info->proberesp_ies_len);
if (res)
return res;
}
if (info->assocresp_ies) {
res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP,
info->assocresp_ies,
info->assocresp_ies_len);
if (res)
return res;
}
if (!add)
return 0;
/* TODO:
* info->interval
* info->dtim_period
*/
if (info->head == NULL)
return -EINVAL;
mgmt = (struct ieee80211_mgmt *) info->head;
ies = mgmt->u.beacon.variable;
if (ies > info->head + info->head_len)
return -EINVAL;
ies_len = info->head + info->head_len - ies;
if (info->ssid == NULL)
return -EINVAL;
memcpy(ar->ssid, info->ssid, info->ssid_len);
ar->ssid_len = info->ssid_len;
if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
return -EOPNOTSUPP; /* TODO */
ar->dot11_auth_mode = OPEN_AUTH;
memset(&p, 0, sizeof(p));
for (i = 0; i < info->crypto.n_akm_suites; i++) {
switch (info->crypto.akm_suites[i]) {
case WLAN_AKM_SUITE_8021X:
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
p.auth_mode |= WPA_AUTH;
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
p.auth_mode |= WPA2_AUTH;
break;
case WLAN_AKM_SUITE_PSK:
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
p.auth_mode |= WPA_PSK_AUTH;
if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
p.auth_mode |= WPA2_PSK_AUTH;
break;
}
}
if (p.auth_mode == 0)
p.auth_mode = NONE_AUTH;
ar->auth_mode = p.auth_mode;
for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
switch (info->crypto.ciphers_pairwise[i]) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
p.prwise_crypto_type |= WEP_CRYPT;
break;
case WLAN_CIPHER_SUITE_TKIP:
p.prwise_crypto_type |= TKIP_CRYPT;
break;
case WLAN_CIPHER_SUITE_CCMP:
p.prwise_crypto_type |= AES_CRYPT;
break;
}
}
if (p.prwise_crypto_type == 0)
p.prwise_crypto_type = NONE_CRYPT;
switch (info->crypto.cipher_group) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
p.grp_crypto_type = WEP_CRYPT;
break;
case WLAN_CIPHER_SUITE_TKIP:
p.grp_crypto_type = TKIP_CRYPT;
break;
case WLAN_CIPHER_SUITE_CCMP:
p.grp_crypto_type = AES_CRYPT;
break;
default:
p.grp_crypto_type = NONE_CRYPT;
break;
}
p.nw_type = AP_NETWORK;
ar->nw_type = ar->next_mode;
p.ssid_len = ar->ssid_len;
memcpy(p.ssid, ar->ssid, ar->ssid_len);
p.dot11_auth_mode = ar->dot11_auth_mode;
p.ch = cpu_to_le16(ar->next_chan);
return ath6kl_wmi_ap_profile_commit(ar->wmi, &p);
}
static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *info)
{
return ath6kl_ap_beacon(wiphy, dev, info, true);
}
static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *info)
{
return ath6kl_ap_beacon(wiphy, dev, info, false);
}
static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
{
struct ath6kl *ar = ath6kl_priv(dev);
if (ar->nw_type != AP_NETWORK)
return -EOPNOTSUPP;
if (!test_bit(CONNECTED, &ar->flag))
return -ENOTCONN;
ath6kl_wmi_disconnect_cmd(ar->wmi);
clear_bit(CONNECTED, &ar->flag);
return 0;
}
static struct cfg80211_ops ath6kl_cfg80211_ops = {
.change_virtual_intf = ath6kl_cfg80211_change_iface,
.scan = ath6kl_cfg80211_scan,
......@@ -1457,6 +1632,10 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
#ifdef CONFIG_PM
.suspend = ar6k_cfg80211_suspend,
#endif
.set_channel = ath6kl_set_channel,
.add_beacon = ath6kl_add_beacon,
.set_beacon = ath6kl_set_beacon,
.del_beacon = ath6kl_del_beacon,
};
struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
......
......@@ -468,6 +468,8 @@ struct ath6kl {
struct ath6kl_node_table scan_table;
struct dentry *debugfs_phy;
u16 next_chan;
};
static inline void *ath6kl_priv(struct net_device *dev)
......
......@@ -1399,6 +1399,8 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
enum htc_endpoint_id ep_id = wmi->ep_id;
int ret;
ath6kl_dbg(ATH6KL_DBG_WMI, "%s: cmd_id=%d\n", __func__, cmd_id);
if (WARN_ON(skb == NULL))
return -EINVAL;
......@@ -2392,6 +2394,29 @@ static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
}
/* AP mode functions */
int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p)
{
struct sk_buff *skb;
struct wmi_connect_cmd *cm;
int res;
skb = ath6kl_wmi_get_new_buf(sizeof(*cm));
if (!skb)
return -ENOMEM;
cm = (struct wmi_connect_cmd *) skb->data;
memcpy(cm, p, sizeof(*cm));
res = ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_CONFIG_COMMIT_CMDID,
NO_SYNC_WMIFLAG);
ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u "
"ctrl_flags=0x%x-> res=%d\n",
__func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch),
le32_to_cpu(p->ctrl_flags), res);
return res;
}
static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len)
{
struct wmi_pspoll_event *ev;
......@@ -2456,6 +2481,26 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver,
return ret;
}
int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
u8 ie_len)
{
struct sk_buff *skb;
struct wmi_set_appie_cmd *p;
skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len);
if (!skb)
return -ENOMEM;
ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u "
"ie_len=%u\n", mgmt_frm_type, ie_len);
p = (struct wmi_set_appie_cmd *) skb->data;
p->mgmt_frm_type = mgmt_frm_type;
p->ie_len = ie_len;
memcpy(p->ie_info, ie, ie_len);
return ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_APPIE_CMDID,
NO_SYNC_WMIFLAG);
}
static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
{
struct wmix_cmd_hdr *cmd;
......
......@@ -503,6 +503,15 @@ enum wmi_cmd_id {
WMI_SET_EXCESS_TX_RETRY_THRES_CMDID,
};
enum wmi_mgmt_frame_type {
WMI_FRAME_BEACON = 0,
WMI_FRAME_PROBE_REQ,
WMI_FRAME_PROBE_RESP,
WMI_FRAME_ASSOC_REQ,
WMI_FRAME_ASSOC_RESP,
WMI_NUM_MGMT_FRAME
};
/* WMI_CONNECT_CMDID */
enum network_type {
INFRA_NETWORK = 0x01,
......@@ -1642,6 +1651,12 @@ struct wmi_get_keepalive_cmd {
u8 keep_alive_intvl;
} __packed;
struct wmi_set_appie_cmd {
u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */
u8 ie_len;
u8 ie_info[0];
} __packed;
/* Notify the WSC registration status to the target */
#define WSC_REG_ACTIVE 1
#define WSC_REG_INACTIVE 0
......@@ -2006,11 +2021,16 @@ struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 *ssid,
void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss);
/* AP mode */
int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p);
int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag);
int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version,
bool rx_dot11_hdr, bool defrag_on_host);
int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
u8 ie_len);
void *ath6kl_wmi_init(struct ath6kl *devt);
void ath6kl_wmi_shutdown(struct wmi *wmi);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment