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

Commit cbdbc5eb authored by John W. Linville's avatar John W. Linville
Browse files
parents 8d8d3fdc e5e2f24b
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -6742,12 +6742,12 @@ S: Maintained
F:	drivers/net/wireless/wl1251/*

WL1271 WIRELESS DRIVER
M:	Luciano Coelho <luciano.coelho@nokia.com>
M:	Luciano Coelho <coelho@ti.com>
L:	linux-wireless@vger.kernel.org
W:	http://wireless.kernel.org
W:	http://wireless.kernel.org/en/users/Drivers/wl12xx
T:	git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git
S:	Maintained
F:	drivers/net/wireless/wl12xx/wl1271*
F:	drivers/net/wireless/wl12xx/
F:	include/linux/wl12xx.h

WL3501 WIRELESS PCMCIA CARD DRIVER
+151 −9
Original line number Diff line number Diff line
@@ -751,10 +751,10 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
	return 0;
}

int wl1271_acx_rate_policies(struct wl1271 *wl)
int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
{
	struct acx_rate_policy *acx;
	struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf;
	struct acx_sta_rate_policy *acx;
	struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf;
	int idx = 0;
	int ret = 0;

@@ -794,6 +794,38 @@ int wl1271_acx_rate_policies(struct wl1271 *wl)
	return ret;
}

int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
		      u8 idx)
{
	struct acx_ap_rate_policy *acx;
	int ret = 0;

	wl1271_debug(DEBUG_ACX, "acx ap rate policy");

	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
	if (!acx) {
		ret = -ENOMEM;
		goto out;
	}

	acx->rate_policy.enabled_rates = cpu_to_le32(c->enabled_rates);
	acx->rate_policy.short_retry_limit = c->short_retry_limit;
	acx->rate_policy.long_retry_limit = c->long_retry_limit;
	acx->rate_policy.aflags = c->aflags;

	acx->rate_policy_idx = cpu_to_le32(idx);

	ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
	if (ret < 0) {
		wl1271_warning("Setting of ap rate policy failed: %d", ret);
		goto out;
	}

out:
	kfree(acx);
	return ret;
}

int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
		      u8 aifsn, u16 txop)
{
@@ -1233,6 +1265,7 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
	struct wl1271_acx_ht_capabilities *acx;
	u8 mac_address[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
	int ret = 0;
	u32 ht_capabilites = 0;

	wl1271_debug(DEBUG_ACX, "acx ht capabilities setting");

@@ -1244,16 +1277,16 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,

	/* Allow HT Operation ? */
	if (allow_ht_operation) {
		acx->ht_capabilites =
		ht_capabilites =
			WL1271_ACX_FW_CAP_HT_OPERATION;
		if (ht_cap->cap & IEEE80211_HT_CAP_GRN_FLD)
			acx->ht_capabilites |=
			ht_capabilites |=
				WL1271_ACX_FW_CAP_GREENFIELD_FRAME_FORMAT;
		if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
			acx->ht_capabilites |=
			ht_capabilites |=
				WL1271_ACX_FW_CAP_SHORT_GI_FOR_20MHZ_PACKETS;
		if (ht_cap->cap & IEEE80211_HT_CAP_LSIG_TXOP_PROT)
			acx->ht_capabilites |=
			ht_capabilites |=
				WL1271_ACX_FW_CAP_LSIG_TXOP_PROTECTION;

		/* get data from A-MPDU parameters field */
@@ -1261,10 +1294,10 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
		acx->ampdu_min_spacing = ht_cap->ampdu_density;

		memcpy(acx->mac_address, mac_address, ETH_ALEN);
	} else { /* HT operations are not allowed */
		acx->ht_capabilites = 0;
	}

	acx->ht_capabilites = cpu_to_le32(ht_capabilites);

	ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx));
	if (ret < 0) {
		wl1271_warning("acx ht capabilities setting failed: %d", ret);
@@ -1309,6 +1342,91 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl,
	return ret;
}

/* Configure BA session initiator/receiver parameters setting in the FW. */
int wl1271_acx_set_ba_session(struct wl1271 *wl,
			       enum ieee80211_back_parties direction,
			       u8 tid_index, u8 policy)
{
	struct wl1271_acx_ba_session_policy *acx;
	int ret;

	wl1271_debug(DEBUG_ACX, "acx ba session setting");

	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
	if (!acx) {
		ret = -ENOMEM;
		goto out;
	}

	/* ANY role */
	acx->role_id = 0xff;
	acx->tid = tid_index;
	acx->enable = policy;
	acx->ba_direction = direction;

	switch (direction) {
	case WLAN_BACK_INITIATOR:
		acx->win_size = wl->conf.ht.tx_ba_win_size;
		acx->inactivity_timeout = wl->conf.ht.inactivity_timeout;
		break;
	case WLAN_BACK_RECIPIENT:
		acx->win_size = RX_BA_WIN_SIZE;
		acx->inactivity_timeout = 0;
		break;
	default:
		wl1271_error("Incorrect acx command id=%x\n", direction);
		ret = -EINVAL;
		goto out;
	}

	ret = wl1271_cmd_configure(wl,
				   ACX_BA_SESSION_POLICY_CFG,
				   acx,
				   sizeof(*acx));
	if (ret < 0) {
		wl1271_warning("acx ba session setting failed: %d", ret);
		goto out;
	}

out:
	kfree(acx);
	return ret;
}

/* setup BA session receiver setting in the FW. */
int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
					bool enable)
{
	struct wl1271_acx_ba_receiver_setup *acx;
	int ret;

	wl1271_debug(DEBUG_ACX, "acx ba receiver session setting");

	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
	if (!acx) {
		ret = -ENOMEM;
		goto out;
	}

	/* Single link for now */
	acx->link_id = 1;
	acx->tid = tid_index;
	acx->enable = enable;
	acx->win_size = 0;
	acx->ssn = ssn;

	ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx,
				   sizeof(*acx));
	if (ret < 0) {
		wl1271_warning("acx ba receiver session failed: %d", ret);
		goto out;
	}

out:
	kfree(acx);
	return ret;
}

int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
{
	struct wl1271_acx_fw_tsf_information *tsf_info;
@@ -1334,3 +1452,27 @@ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
	kfree(tsf_info);
	return ret;
}

int wl1271_acx_max_tx_retry(struct wl1271 *wl)
{
	struct wl1271_acx_max_tx_retry *acx = NULL;
	int ret;

	wl1271_debug(DEBUG_ACX, "acx max tx retry");

	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
	if (!acx)
		return -ENOMEM;

	acx->max_tx_retry = cpu_to_le16(wl->conf.tx.ap_max_tx_retries);

	ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
	if (ret < 0) {
		wl1271_warning("acx max tx retry failed: %d", ret);
		goto out;
	}

out:
	kfree(acx);
	return ret;
}
+87 −4
Original line number Diff line number Diff line
@@ -747,13 +747,23 @@ struct acx_rate_class {
#define ACX_TX_BASIC_RATE      0
#define ACX_TX_AP_FULL_RATE    1
#define ACX_TX_RATE_POLICY_CNT 2
struct acx_rate_policy {
struct acx_sta_rate_policy {
	struct acx_header header;

	__le32 rate_class_cnt;
	struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES];
} __packed;


#define ACX_TX_AP_MODE_MGMT_RATE 4
#define ACX_TX_AP_MODE_BCST_RATE 5
struct acx_ap_rate_policy {
	struct acx_header header;

	__le32 rate_policy_idx;
	struct acx_rate_class rate_policy;
} __packed;

struct acx_ac_cfg {
	struct acx_header header;
	u8 ac;
@@ -1051,6 +1061,59 @@ struct wl1271_acx_ht_information {
	u8 padding[3];
} __packed;

#define RX_BA_WIN_SIZE 8

struct wl1271_acx_ba_session_policy {
	struct acx_header header;
	/*
	 * Specifies role Id, Range 0-7, 0xFF means ANY role.
	 * Future use. For now this field is irrelevant
	 */
	u8 role_id;
	/*
	 * Specifies Link Id, Range 0-31, 0xFF means ANY  Link Id.
	 * Not applicable if Role Id is set to ANY.
	 */
	u8 link_id;

	u8 tid;

	u8 enable;

	/* Windows size in number of packets */
	u16 win_size;

	/*
	 * As initiator inactivity timeout in time units(TU) of 1024us.
	 * As receiver reserved
	 */
	u16 inactivity_timeout;

	/* Initiator = 1/Receiver = 0 */
	u8 ba_direction;

	u8 padding[3];
} __packed;

struct wl1271_acx_ba_receiver_setup {
	struct acx_header header;

	/* Specifies Link Id, Range 0-31, 0xFF means ANY  Link Id */
	u8 link_id;

	u8 tid;

	u8 enable;

	u8 padding[1];

	/* Windows size in number of packets */
	u16 win_size;

	/* BA session starting sequence number.  RANGE 0-FFF */
	u16 ssn;
} __packed;

struct wl1271_acx_fw_tsf_information {
	struct acx_header header;

@@ -1062,6 +1125,17 @@ struct wl1271_acx_fw_tsf_information {
	u8 padding[3];
} __packed;

struct wl1271_acx_max_tx_retry {
	struct acx_header header;

	/*
	 * the number of frames transmission failures before
	 * issuing the aging event.
	 */
	__le16 max_tx_retry;
	u8 padding_1[2];
} __packed;

enum {
	ACX_WAKE_UP_CONDITIONS      = 0x0002,
	ACX_MEM_CFG                 = 0x0003,
@@ -1113,12 +1187,13 @@ enum {
	ACX_RSSI_SNR_WEIGHTS        = 0x0052,
	ACX_KEEP_ALIVE_MODE         = 0x0053,
	ACX_SET_KEEP_ALIVE_CONFIG   = 0x0054,
	ACX_BA_SESSION_RESPONDER_POLICY = 0x0055,
	ACX_BA_SESSION_INITIATOR_POLICY = 0x0056,
	ACX_BA_SESSION_POLICY_CFG   = 0x0055,
	ACX_BA_SESSION_RX_SETUP     = 0x0056,
	ACX_PEER_HT_CAP             = 0x0057,
	ACX_HT_BSS_OPERATION        = 0x0058,
	ACX_COEX_ACTIVITY           = 0x0059,
	ACX_SET_DCO_ITRIM_PARAMS    = 0x0061,
	ACX_MAX_TX_FAILURE          = 0x0072,
	DOT11_RX_MSDU_LIFE_TIME     = 0x1004,
	DOT11_CUR_TX_PWR            = 0x100D,
	DOT11_RX_DOT11_MODE         = 0x1012,
@@ -1160,7 +1235,9 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble);
int wl1271_acx_cts_protect(struct wl1271 *wl,
			   enum acx_ctsprotect_type ctsprotect);
int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
int wl1271_acx_rate_policies(struct wl1271 *wl);
int wl1271_acx_sta_rate_policies(struct wl1271 *wl);
int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
		      u8 idx);
int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
		      u8 aifsn, u16 txop);
int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
@@ -1185,6 +1262,12 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
				    bool allow_ht_operation);
int wl1271_acx_set_ht_information(struct wl1271 *wl,
				   u16 ht_operation_mode);
int wl1271_acx_set_ba_session(struct wl1271 *wl,
			      enum ieee80211_back_parties direction,
			      u8 tid_index, u8 policy);
int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
				       bool enable);
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
int wl1271_acx_max_tx_retry(struct wl1271 *wl);

#endif /* __WL1271_ACX_H__ */
+29 −6
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "boot.h"
#include "io.h"
#include "event.h"
#include "rx.h"

static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
	[PART_DOWN] = {
@@ -100,6 +101,22 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
	wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
}

static void wl1271_parse_fw_ver(struct wl1271 *wl)
{
	int ret;

	ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u",
		     &wl->chip.fw_ver[0], &wl->chip.fw_ver[1],
		     &wl->chip.fw_ver[2], &wl->chip.fw_ver[3],
		     &wl->chip.fw_ver[4]);

	if (ret != 5) {
		wl1271_warning("fw version incorrect value");
		memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
		return;
	}
}

static void wl1271_boot_fw_version(struct wl1271 *wl)
{
	struct wl1271_static_data static_data;
@@ -107,11 +124,13 @@ static void wl1271_boot_fw_version(struct wl1271 *wl)
	wl1271_read(wl, wl->cmd_box_addr, &static_data, sizeof(static_data),
		    false);

	strncpy(wl->chip.fw_ver, static_data.fw_version,
		sizeof(wl->chip.fw_ver));
	strncpy(wl->chip.fw_ver_str, static_data.fw_version,
		sizeof(wl->chip.fw_ver_str));

	/* make sure the string is NULL-terminated */
	wl->chip.fw_ver[sizeof(wl->chip.fw_ver) - 1] = '\0';
	wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';

	wl1271_parse_fw_ver(wl);
}

static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
@@ -231,7 +250,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
	 */
	if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
	    wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
		if (wl->nvs->general_params.dual_mode_select)
		/* for now 11a is unsupported in AP mode */
		if (wl->bss_type != BSS_TYPE_AP_BSS &&
		    wl->nvs->general_params.dual_mode_select)
			wl->enable_11a = true;
	}

@@ -431,6 +452,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
		PSPOLL_DELIVERY_FAILURE_EVENT_ID |
		SOFT_GEMINI_SENSE_EVENT_ID;

	if (wl->bss_type == BSS_TYPE_AP_BSS)
		wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID;

	ret = wl1271_event_unmask(wl);
	if (ret < 0) {
		wl1271_error("EVENT mask setting failed");
@@ -595,8 +619,7 @@ int wl1271_boot(struct wl1271 *wl)
	wl1271_boot_enable_interrupts(wl);

	/* set the wl1271 default filters */
	wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
	wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
	wl1271_set_default_filters(wl);

	wl1271_event_mbox_config(wl);

+296 −12
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include "wl12xx_80211.h"
#include "cmd.h"
#include "event.h"
#include "tx.h"

#define WL1271_CMD_FAST_POLL_COUNT       50

@@ -221,7 +222,7 @@ int wl1271_cmd_ext_radio_parms(struct wl1271 *wl)
 * Poll the mailbox event field until any of the bits in the mask is set or a
 * timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
 */
static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask)
{
	u32 events_vector, event;
	unsigned long timeout;
@@ -230,7 +231,8 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)

	do {
		if (time_after(jiffies, timeout)) {
			ieee80211_queue_work(wl->hw, &wl->recovery_work);
			wl1271_debug(DEBUG_CMD, "timeout waiting for event %d",
				     (int)mask);
			return -ETIMEDOUT;
		}

@@ -248,6 +250,19 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
	return 0;
}

static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
{
	int ret;

	ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask);
	if (ret != 0) {
		ieee80211_queue_work(wl->hw, &wl->recovery_work);
		return ret;
	}

	return 0;
}

int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
{
	struct wl1271_cmd_join *join;
@@ -490,8 +505,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
	cmd->len = cpu_to_le16(buf_len);
	cmd->template_type = template_id;
	cmd->enabled_rates = cpu_to_le32(rates);
	cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit;
	cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit;
	cmd->short_retry_limit = wl->conf.tx.tmpl_short_retry_limit;
	cmd->long_retry_limit = wl->conf.tx.tmpl_long_retry_limit;
	cmd->index = index;

	if (buf)
@@ -659,15 +674,15 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr)

	/* llc layer */
	memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header));
	tmpl.llc_type = htons(ETH_P_ARP);
	tmpl.llc_type = cpu_to_be16(ETH_P_ARP);

	/* arp header */
	arp_hdr = &tmpl.arp_hdr;
	arp_hdr->ar_hrd = htons(ARPHRD_ETHER);
	arp_hdr->ar_pro = htons(ETH_P_IP);
	arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);
	arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);
	arp_hdr->ar_hln = ETH_ALEN;
	arp_hdr->ar_pln = 4;
	arp_hdr->ar_op = htons(ARPOP_REPLY);
	arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);

	/* arp payload */
	memcpy(tmpl.sender_hw, wl->vif->addr, ETH_ALEN);
@@ -702,9 +717,9 @@ int wl1271_build_qos_null_data(struct wl1271 *wl)
				       wl->basic_rate);
}

int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id)
{
	struct wl1271_cmd_set_keys *cmd;
	struct wl1271_cmd_set_sta_keys *cmd;
	int ret = 0;

	wl1271_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id);
@@ -731,11 +746,42 @@ int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
	return ret;
}

int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id)
{
	struct wl1271_cmd_set_ap_keys *cmd;
	int ret = 0;

	wl1271_debug(DEBUG_CMD, "cmd set_ap_default_wep_key %d", id);

	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
	if (!cmd) {
		ret = -ENOMEM;
		goto out;
	}

	cmd->hlid = WL1271_AP_BROADCAST_HLID;
	cmd->key_id = id;
	cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
	cmd->key_action = cpu_to_le16(KEY_SET_ID);
	cmd->key_type = KEY_WEP;

	ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
	if (ret < 0) {
		wl1271_warning("cmd set_ap_default_wep_key failed: %d", ret);
		goto out;
	}

out:
	kfree(cmd);

	return ret;
}

int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
		       u8 key_size, const u8 *key, const u8 *addr,
		       u32 tx_seq_32, u16 tx_seq_16)
{
	struct wl1271_cmd_set_keys *cmd;
	struct wl1271_cmd_set_sta_keys *cmd;
	int ret = 0;

	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -788,6 +834,67 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
	return ret;
}

int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
			u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
			u16 tx_seq_16)
{
	struct wl1271_cmd_set_ap_keys *cmd;
	int ret = 0;
	u8 lid_type;

	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
	if (!cmd)
		return -ENOMEM;

	if (hlid == WL1271_AP_BROADCAST_HLID) {
		if (key_type == KEY_WEP)
			lid_type = WEP_DEFAULT_LID_TYPE;
		else
			lid_type = BROADCAST_LID_TYPE;
	} else {
		lid_type = UNICAST_LID_TYPE;
	}

	wl1271_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d"
		     " hlid: %d", (int)action, (int)id, (int)lid_type,
		     (int)key_type, (int)hlid);

	cmd->lid_key_type = lid_type;
	cmd->hlid = hlid;
	cmd->key_action = cpu_to_le16(action);
	cmd->key_size = key_size;
	cmd->key_type = key_type;
	cmd->key_id = id;
	cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16);
	cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32);

	if (key_type == KEY_TKIP) {
		/*
		 * We get the key in the following form:
		 * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
		 * but the target is expecting:
		 * TKIP - RX MIC - TX MIC
		 */
		memcpy(cmd->key, key, 16);
		memcpy(cmd->key + 16, key + 24, 8);
		memcpy(cmd->key + 24, key + 16, 8);
	} else {
		memcpy(cmd->key, key, key_size);
	}

	wl1271_dump(DEBUG_CRYPT, "TARGET AP KEY: ", cmd, sizeof(*cmd));

	ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
	if (ret < 0) {
		wl1271_warning("could not set ap keys");
		goto out;
	}

out:
	kfree(cmd);
	return ret;
}

int wl1271_cmd_disconnect(struct wl1271 *wl)
{
	struct wl1271_cmd_disconnect *cmd;
@@ -850,3 +957,180 @@ int wl1271_cmd_set_sta_state(struct wl1271 *wl)
out:
	return ret;
}

int wl1271_cmd_start_bss(struct wl1271 *wl)
{
	struct wl1271_cmd_bss_start *cmd;
	struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
	int ret;

	wl1271_debug(DEBUG_CMD, "cmd start bss");

	/*
	 * FIXME: We currently do not support hidden SSID. The real SSID
	 * should be fetched from mac80211 first.
	 */
	if (wl->ssid_len == 0) {
		wl1271_warning("Hidden SSID currently not supported for AP");
		ret = -EINVAL;
		goto out;
	}

	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
	if (!cmd) {
		ret = -ENOMEM;
		goto out;
	}

	memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN);

	cmd->aging_period = cpu_to_le16(WL1271_AP_DEF_INACTIV_SEC);
	cmd->bss_index = WL1271_AP_BSS_INDEX;
	cmd->global_hlid = WL1271_AP_GLOBAL_HLID;
	cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID;
	cmd->basic_rate_set = cpu_to_le32(wl->basic_rate_set);
	cmd->beacon_interval = cpu_to_le16(wl->beacon_int);
	cmd->dtim_interval = bss_conf->dtim_period;
	cmd->beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
	cmd->channel = wl->channel;
	cmd->ssid_len = wl->ssid_len;
	cmd->ssid_type = SSID_TYPE_PUBLIC;
	memcpy(cmd->ssid, wl->ssid, wl->ssid_len);

	switch (wl->band) {
	case IEEE80211_BAND_2GHZ:
		cmd->band = RADIO_BAND_2_4GHZ;
		break;
	case IEEE80211_BAND_5GHZ:
		cmd->band = RADIO_BAND_5GHZ;
		break;
	default:
		wl1271_warning("bss start - unknown band: %d", (int)wl->band);
		cmd->band = RADIO_BAND_2_4GHZ;
		break;
	}

	ret = wl1271_cmd_send(wl, CMD_BSS_START, cmd, sizeof(*cmd), 0);
	if (ret < 0) {
		wl1271_error("failed to initiate cmd start bss");
		goto out_free;
	}

out_free:
	kfree(cmd);

out:
	return ret;
}

int wl1271_cmd_stop_bss(struct wl1271 *wl)
{
	struct wl1271_cmd_bss_start *cmd;
	int ret;

	wl1271_debug(DEBUG_CMD, "cmd stop bss");

	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
	if (!cmd) {
		ret = -ENOMEM;
		goto out;
	}

	cmd->bss_index = WL1271_AP_BSS_INDEX;

	ret = wl1271_cmd_send(wl, CMD_BSS_STOP, cmd, sizeof(*cmd), 0);
	if (ret < 0) {
		wl1271_error("failed to initiate cmd stop bss");
		goto out_free;
	}

out_free:
	kfree(cmd);

out:
	return ret;
}

int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
{
	struct wl1271_cmd_add_sta *cmd;
	int ret;

	wl1271_debug(DEBUG_CMD, "cmd add sta %d", (int)hlid);

	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
	if (!cmd) {
		ret = -ENOMEM;
		goto out;
	}

	/* currently we don't support UAPSD */
	cmd->sp_len = 0;

	memcpy(cmd->addr, sta->addr, ETH_ALEN);
	cmd->bss_index = WL1271_AP_BSS_INDEX;
	cmd->aid = sta->aid;
	cmd->hlid = hlid;

	/*
	 * FIXME: Does STA support QOS? We need to propagate this info from
	 * hostapd. Currently not that important since this is only used for
	 * sending the correct flavor of null-data packet in response to a
	 * trigger.
	 */
	cmd->wmm = 0;

	cmd->supported_rates = cpu_to_le32(wl1271_tx_enabled_rates_get(wl,
						sta->supp_rates[wl->band]));

	wl1271_debug(DEBUG_CMD, "new sta rates: 0x%x", cmd->supported_rates);

	ret = wl1271_cmd_send(wl, CMD_ADD_STA, cmd, sizeof(*cmd), 0);
	if (ret < 0) {
		wl1271_error("failed to initiate cmd add sta");
		goto out_free;
	}

out_free:
	kfree(cmd);

out:
	return ret;
}

int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid)
{
	struct wl1271_cmd_remove_sta *cmd;
	int ret;

	wl1271_debug(DEBUG_CMD, "cmd remove sta %d", (int)hlid);

	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
	if (!cmd) {
		ret = -ENOMEM;
		goto out;
	}

	cmd->hlid = hlid;
	/* We never send a deauth, mac80211 is in charge of this */
	cmd->reason_opcode = 0;
	cmd->send_deauth_flag = 0;

	ret = wl1271_cmd_send(wl, CMD_REMOVE_STA, cmd, sizeof(*cmd), 0);
	if (ret < 0) {
		wl1271_error("failed to initiate cmd remove sta");
		goto out_free;
	}

	/*
	 * We are ok with a timeout here. The event is sometimes not sent
	 * due to a firmware bug.
	 */
	wl1271_cmd_wait_for_event_or_timeout(wl, STA_REMOVE_COMPLETE_EVENT_ID);

out_free:
	kfree(cmd);

out:
	return ret;
}
Loading