Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+179 −0
Original line number Diff line number Diff line
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -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)
+45 −0
Original line number Diff line number Diff line
@@ -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;
+20 −0
Original line number Diff line number Diff line
@@ -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);