diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-23 11:47:02 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-23 11:47:02 -0700 | 
| commit | 5f05647dd81c11a6a165ccc8f0c1370b16f3bcb0 (patch) | |
| tree | 7851ef1c93aa1aba7ef327ca4b75fd35e6d10f29 /drivers/net/wireless/wl1251/cmd.c | |
| parent | 02f36038c568111ad4fc433f6fa760ff5e38fab4 (diff) | |
| parent | ec37a48d1d16c30b655ac5280209edf52a6775d4 (diff) | |
| download | olio-linux-3.10-5f05647dd81c11a6a165ccc8f0c1370b16f3bcb0.tar.xz olio-linux-3.10-5f05647dd81c11a6a165ccc8f0c1370b16f3bcb0.zip  | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1699 commits)
  bnx2/bnx2x: Unsupported Ethtool operations should return -EINVAL.
  vlan: Calling vlan_hwaccel_do_receive() is always valid.
  tproxy: use the interface primary IP address as a default value for --on-ip
  tproxy: added IPv6 support to the socket match
  cxgb3: function namespace cleanup
  tproxy: added IPv6 support to the TPROXY target
  tproxy: added IPv6 socket lookup function to nf_tproxy_core
  be2net: Changes to use only priority codes allowed by f/w
  tproxy: allow non-local binds of IPv6 sockets if IP_TRANSPARENT is enabled
  tproxy: added tproxy sockopt interface in the IPV6 layer
  tproxy: added udp6_lib_lookup function
  tproxy: added const specifiers to udp lookup functions
  tproxy: split off ipv6 defragmentation to a separate module
  l2tp: small cleanup
  nf_nat: restrict ICMP translation for embedded header
  can: mcp251x: fix generation of error frames
  can: mcp251x: fix endless loop in interrupt handler if CANINTF_MERRF is set
  can-raw: add msg_flags to distinguish local traffic
  9p: client code cleanup
  rds: make local functions/variables static
  ...
Fix up conflicts in net/core/dev.c, drivers/net/pcmcia/smc91c92_cs.c and
drivers/net/wireless/ath/ath9k/debug.c as per David
Diffstat (limited to 'drivers/net/wireless/wl1251/cmd.c')
| -rw-r--r-- | drivers/net/wireless/wl1251/cmd.c | 496 | 
1 files changed, 496 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl1251/cmd.c b/drivers/net/wireless/wl1251/cmd.c new file mode 100644 index 00000000000..0ade4bd617c --- /dev/null +++ b/drivers/net/wireless/wl1251/cmd.c @@ -0,0 +1,496 @@ +#include "cmd.h" + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/crc7.h> + +#include "wl1251.h" +#include "reg.h" +#include "io.h" +#include "ps.h" +#include "acx.h" + +/** + * send command to firmware + * + * @wl: wl struct + * @id: command id + * @buf: buffer containing the command, must work with dma + * @len: length of the buffer + */ +int wl1251_cmd_send(struct wl1251 *wl, u16 id, void *buf, size_t len) +{ +	struct wl1251_cmd_header *cmd; +	unsigned long timeout; +	u32 intr; +	int ret = 0; + +	cmd = buf; +	cmd->id = id; +	cmd->status = 0; + +	WARN_ON(len % 4 != 0); + +	wl1251_mem_write(wl, wl->cmd_box_addr, buf, len); + +	wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); + +	timeout = jiffies + msecs_to_jiffies(WL1251_COMMAND_TIMEOUT); + +	intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); +	while (!(intr & WL1251_ACX_INTR_CMD_COMPLETE)) { +		if (time_after(jiffies, timeout)) { +			wl1251_error("command complete timeout"); +			ret = -ETIMEDOUT; +			goto out; +		} + +		msleep(1); + +		intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); +	} + +	wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, +			   WL1251_ACX_INTR_CMD_COMPLETE); + +out: +	return ret; +} + +/** + * send test command to firmware + * + * @wl: wl struct + * @buf: buffer containing the command, with all headers, must work with dma + * @len: length of the buffer + * @answer: is answer needed + */ +int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer) +{ +	int ret; + +	wl1251_debug(DEBUG_CMD, "cmd test"); + +	ret = wl1251_cmd_send(wl, CMD_TEST, buf, buf_len); + +	if (ret < 0) { +		wl1251_warning("TEST command failed"); +		return ret; +	} + +	if (answer) { +		struct wl1251_command *cmd_answer; + +		/* +		 * The test command got in, we can read the answer. +		 * The answer would be a wl1251_command, where the +		 * parameter array contains the actual answer. +		 */ +		wl1251_mem_read(wl, wl->cmd_box_addr, buf, buf_len); + +		cmd_answer = buf; + +		if (cmd_answer->header.status != CMD_STATUS_SUCCESS) +			wl1251_error("TEST command answer error: %d", +				     cmd_answer->header.status); +	} + +	return 0; +} + +/** + * read acx from firmware + * + * @wl: wl struct + * @id: acx id + * @buf: buffer for the response, including all headers, must work with dma + * @len: lenght of buf + */ +int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len) +{ +	struct acx_header *acx = buf; +	int ret; + +	wl1251_debug(DEBUG_CMD, "cmd interrogate"); + +	acx->id = id; + +	/* payload length, does not include any headers */ +	acx->len = len - sizeof(*acx); + +	ret = wl1251_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx)); +	if (ret < 0) { +		wl1251_error("INTERROGATE command failed"); +		goto out; +	} + +	/* the interrogate command got in, we can read the answer */ +	wl1251_mem_read(wl, wl->cmd_box_addr, buf, len); + +	acx = buf; +	if (acx->cmd.status != CMD_STATUS_SUCCESS) +		wl1251_error("INTERROGATE command error: %d", +			     acx->cmd.status); + +out: +	return ret; +} + +/** + * write acx value to firmware + * + * @wl: wl struct + * @id: acx id + * @buf: buffer containing acx, including all headers, must work with dma + * @len: length of buf + */ +int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len) +{ +	struct acx_header *acx = buf; +	int ret; + +	wl1251_debug(DEBUG_CMD, "cmd configure"); + +	acx->id = id; + +	/* payload length, does not include any headers */ +	acx->len = len - sizeof(*acx); + +	ret = wl1251_cmd_send(wl, CMD_CONFIGURE, acx, len); +	if (ret < 0) { +		wl1251_warning("CONFIGURE command NOK"); +		return ret; +	} + +	return 0; +} + +int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, +		   void *bitmap, u16 bitmap_len, u8 bitmap_control) +{ +	struct wl1251_cmd_vbm_update *vbm; +	int ret; + +	wl1251_debug(DEBUG_CMD, "cmd vbm"); + +	vbm = kzalloc(sizeof(*vbm), GFP_KERNEL); +	if (!vbm) { +		ret = -ENOMEM; +		goto out; +	} + +	/* Count and period will be filled by the target */ +	vbm->tim.bitmap_ctrl = bitmap_control; +	if (bitmap_len > PARTIAL_VBM_MAX) { +		wl1251_warning("cmd vbm len is %d B, truncating to %d", +			       bitmap_len, PARTIAL_VBM_MAX); +		bitmap_len = PARTIAL_VBM_MAX; +	} +	memcpy(vbm->tim.pvb_field, bitmap, bitmap_len); +	vbm->tim.identity = identity; +	vbm->tim.length = bitmap_len + 3; + +	vbm->len = cpu_to_le16(bitmap_len + 5); + +	ret = wl1251_cmd_send(wl, CMD_VBM, vbm, sizeof(*vbm)); +	if (ret < 0) { +		wl1251_error("VBM command failed"); +		goto out; +	} + +out: +	kfree(vbm); +	return ret; +} + +int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable) +{ +	struct cmd_enabledisable_path *cmd; +	int ret; +	u16 cmd_rx, cmd_tx; + +	wl1251_debug(DEBUG_CMD, "cmd data path"); + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (!cmd) { +		ret = -ENOMEM; +		goto out; +	} + +	cmd->channel = channel; + +	if (enable) { +		cmd_rx = CMD_ENABLE_RX; +		cmd_tx = CMD_ENABLE_TX; +	} else { +		cmd_rx = CMD_DISABLE_RX; +		cmd_tx = CMD_DISABLE_TX; +	} + +	ret = wl1251_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); +	if (ret < 0) { +		wl1251_error("rx %s cmd for channel %d failed", +			     enable ? "start" : "stop", channel); +		goto out; +	} + +	wl1251_debug(DEBUG_BOOT, "rx %s cmd channel %d", +		     enable ? "start" : "stop", channel); + +	ret = wl1251_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); +	if (ret < 0) { +		wl1251_error("tx %s cmd for channel %d failed", +			     enable ? "start" : "stop", channel); +		return ret; +	} + +	wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d", +		     enable ? "start" : "stop", channel); + +out: +	kfree(cmd); +	return ret; +} + +int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, +		    u16 beacon_interval, u8 dtim_interval) +{ +	struct cmd_join *join; +	int ret, i; +	u8 *bssid; + +	join = kzalloc(sizeof(*join), GFP_KERNEL); +	if (!join) { +		ret = -ENOMEM; +		goto out; +	} + +	wl1251_debug(DEBUG_CMD, "cmd join%s ch %d %d/%d", +		     bss_type == BSS_TYPE_IBSS ? " ibss" : "", +		     channel, beacon_interval, dtim_interval); + +	/* Reverse order BSSID */ +	bssid = (u8 *) &join->bssid_lsb; +	for (i = 0; i < ETH_ALEN; i++) +		bssid[i] = wl->bssid[ETH_ALEN - i - 1]; + +	join->rx_config_options = wl->rx_config; +	join->rx_filter_options = wl->rx_filter; + +	/* +	 * FIXME: disable temporarily all filters because after commit +	 * 9cef8737 "mac80211: fix managed mode BSSID handling" broke +	 * association. The filter logic needs to be implemented properly +	 * and once that is done, this hack can be removed. +	 */ +	join->rx_config_options = 0; +	join->rx_filter_options = WL1251_DEFAULT_RX_FILTER; + +	join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | +		RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; + +	join->beacon_interval = beacon_interval; +	join->dtim_interval = dtim_interval; +	join->bss_type = bss_type; +	join->channel = channel; +	join->ctrl = JOIN_CMD_CTRL_TX_FLUSH; + +	ret = wl1251_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join)); +	if (ret < 0) { +		wl1251_error("failed to initiate cmd join"); +		goto out; +	} + +out: +	kfree(join); +	return ret; +} + +int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode) +{ +	struct wl1251_cmd_ps_params *ps_params = NULL; +	int ret = 0; + +	wl1251_debug(DEBUG_CMD, "cmd set ps mode"); + +	ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); +	if (!ps_params) { +		ret = -ENOMEM; +		goto out; +	} + +	ps_params->ps_mode = ps_mode; +	ps_params->send_null_data = 1; +	ps_params->retries = 5; +	ps_params->hang_over_period = 128; +	ps_params->null_data_rate = 1; /* 1 Mbps */ + +	ret = wl1251_cmd_send(wl, CMD_SET_PS_MODE, ps_params, +			      sizeof(*ps_params)); +	if (ret < 0) { +		wl1251_error("cmd set_ps_mode failed"); +		goto out; +	} + +out: +	kfree(ps_params); +	return ret; +} + +int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, +			   size_t len) +{ +	struct cmd_read_write_memory *cmd; +	int ret = 0; + +	wl1251_debug(DEBUG_CMD, "cmd read memory"); + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (!cmd) { +		ret = -ENOMEM; +		goto out; +	} + +	WARN_ON(len > MAX_READ_SIZE); +	len = min_t(size_t, len, MAX_READ_SIZE); + +	cmd->addr = addr; +	cmd->size = len; + +	ret = wl1251_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd)); +	if (ret < 0) { +		wl1251_error("read memory command failed: %d", ret); +		goto out; +	} + +	/* the read command got in, we can now read the answer */ +	wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); + +	if (cmd->header.status != CMD_STATUS_SUCCESS) +		wl1251_error("error in read command result: %d", +			     cmd->header.status); + +	memcpy(answer, cmd->value, len); + +out: +	kfree(cmd); +	return ret; +} + +int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, +			    void *buf, size_t buf_len) +{ +	struct wl1251_cmd_packet_template *cmd; +	size_t cmd_len; +	int ret = 0; + +	wl1251_debug(DEBUG_CMD, "cmd template %d", cmd_id); + +	WARN_ON(buf_len > WL1251_MAX_TEMPLATE_SIZE); +	buf_len = min_t(size_t, buf_len, WL1251_MAX_TEMPLATE_SIZE); +	cmd_len = ALIGN(sizeof(*cmd) + buf_len, 4); + +	cmd = kzalloc(cmd_len, GFP_KERNEL); +	if (!cmd) { +		ret = -ENOMEM; +		goto out; +	} + +	cmd->size = cpu_to_le16(buf_len); + +	if (buf) +		memcpy(cmd->data, buf, buf_len); + +	ret = wl1251_cmd_send(wl, cmd_id, cmd, cmd_len); +	if (ret < 0) { +		wl1251_warning("cmd set_template failed: %d", ret); +		goto out; +	} + +out: +	kfree(cmd); +	return ret; +} + +int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, +		    struct ieee80211_channel *channels[], +		    unsigned int n_channels, unsigned int n_probes) +{ +	struct wl1251_cmd_scan *cmd; +	int i, ret = 0; + +	wl1251_debug(DEBUG_CMD, "cmd scan"); + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (!cmd) +		return -ENOMEM; + +	cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); +	cmd->params.rx_filter_options = cpu_to_le32(CFG_RX_PRSP_EN | +						    CFG_RX_MGMT_EN | +						    CFG_RX_BCN_EN); +	cmd->params.scan_options = 0; +	cmd->params.num_channels = n_channels; +	cmd->params.num_probe_requests = n_probes; +	cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ +	cmd->params.tid_trigger = 0; + +	for (i = 0; i < n_channels; i++) { +		cmd->channels[i].min_duration = +			cpu_to_le32(WL1251_SCAN_MIN_DURATION); +		cmd->channels[i].max_duration = +			cpu_to_le32(WL1251_SCAN_MAX_DURATION); +		memset(&cmd->channels[i].bssid_lsb, 0xff, 4); +		memset(&cmd->channels[i].bssid_msb, 0xff, 2); +		cmd->channels[i].early_termination = 0; +		cmd->channels[i].tx_power_att = 0; +		cmd->channels[i].channel = channels[i]->hw_value; +	} + +	cmd->params.ssid_len = ssid_len; +	if (ssid) +		memcpy(cmd->params.ssid, ssid, ssid_len); + +	ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); +	if (ret < 0) { +		wl1251_error("cmd scan failed: %d", ret); +		goto out; +	} + +	wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); + +	if (cmd->header.status != CMD_STATUS_SUCCESS) { +		wl1251_error("cmd scan status wasn't success: %d", +			     cmd->header.status); +		ret = -EIO; +		goto out; +	} + +out: +	kfree(cmd); +	return ret; +} + +int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout) +{ +	struct wl1251_cmd_trigger_scan_to *cmd; +	int ret; + +	wl1251_debug(DEBUG_CMD, "cmd trigger scan to"); + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (!cmd) +		return -ENOMEM; + +	cmd->timeout = timeout; + +	ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, cmd, sizeof(*cmd)); +	if (ret < 0) { +		wl1251_error("cmd trigger scan to failed: %d", ret); +		goto out; +	} + +out: +	kfree(cmd); +	return ret; +}  |