162306a36Sopenharmony_ci/* Encapsulate basic setting changes and retrieval on Hermes hardware
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * See copyright notice in main.c
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/device.h>
762306a36Sopenharmony_ci#include <linux/if_arp.h>
862306a36Sopenharmony_ci#include <linux/ieee80211.h>
962306a36Sopenharmony_ci#include <linux/wireless.h>
1062306a36Sopenharmony_ci#include <net/cfg80211.h>
1162306a36Sopenharmony_ci#include "hermes.h"
1262306a36Sopenharmony_ci#include "hermes_rid.h"
1362306a36Sopenharmony_ci#include "orinoco.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "hw.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define SYMBOL_MAX_VER_LEN	(14)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* Symbol firmware has a bug allocating buffers larger than this */
2062306a36Sopenharmony_ci#define TX_NICBUF_SIZE_BUG	1585
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/********************************************************************/
2362306a36Sopenharmony_ci/* Data tables                                                      */
2462306a36Sopenharmony_ci/********************************************************************/
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* This tables gives the actual meanings of the bitrate IDs returned
2762306a36Sopenharmony_ci * by the firmware. */
2862306a36Sopenharmony_cistatic const struct {
2962306a36Sopenharmony_ci	int bitrate; /* in 100s of kilobits */
3062306a36Sopenharmony_ci	int automatic;
3162306a36Sopenharmony_ci	u16 agere_txratectrl;
3262306a36Sopenharmony_ci	u16 intersil_txratectrl;
3362306a36Sopenharmony_ci} bitrate_table[] = {
3462306a36Sopenharmony_ci	{110, 1,  3, 15}, /* Entry 0 is the default */
3562306a36Sopenharmony_ci	{10,  0,  1,  1},
3662306a36Sopenharmony_ci	{10,  1,  1,  1},
3762306a36Sopenharmony_ci	{20,  0,  2,  2},
3862306a36Sopenharmony_ci	{20,  1,  6,  3},
3962306a36Sopenharmony_ci	{55,  0,  4,  4},
4062306a36Sopenharmony_ci	{55,  1,  7,  7},
4162306a36Sopenharmony_ci	{110, 0,  5,  8},
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Firmware version encoding */
4662306a36Sopenharmony_cistruct comp_id {
4762306a36Sopenharmony_ci	u16 id, variant, major, minor;
4862306a36Sopenharmony_ci} __packed;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic inline enum fwtype determine_firmware_type(struct comp_id *nic_id)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	if (nic_id->id < 0x8000)
5362306a36Sopenharmony_ci		return FIRMWARE_TYPE_AGERE;
5462306a36Sopenharmony_ci	else if (nic_id->id == 0x8000 && nic_id->major == 0)
5562306a36Sopenharmony_ci		return FIRMWARE_TYPE_SYMBOL;
5662306a36Sopenharmony_ci	else
5762306a36Sopenharmony_ci		return FIRMWARE_TYPE_INTERSIL;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* Set priv->firmware type, determine firmware properties
6162306a36Sopenharmony_ci * This function can be called before we have registerred with netdev,
6262306a36Sopenharmony_ci * so all errors go out with dev_* rather than printk
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci * If non-NULL stores a firmware description in fw_name.
6562306a36Sopenharmony_ci * If non-NULL stores a HW version in hw_ver
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * These are output via generic cfg80211 ethtool support.
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_ciint determine_fw_capabilities(struct orinoco_private *priv,
7062306a36Sopenharmony_ci			      char *fw_name, size_t fw_name_len,
7162306a36Sopenharmony_ci			      u32 *hw_ver)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct device *dev = priv->dev;
7462306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
7562306a36Sopenharmony_ci	int err;
7662306a36Sopenharmony_ci	struct comp_id nic_id, sta_id;
7762306a36Sopenharmony_ci	unsigned int firmver;
7862306a36Sopenharmony_ci	char tmp[SYMBOL_MAX_VER_LEN + 1] __attribute__((aligned(2)));
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Get the hardware version */
8162306a36Sopenharmony_ci	err = HERMES_READ_RECORD_PR(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
8262306a36Sopenharmony_ci	if (err) {
8362306a36Sopenharmony_ci		dev_err(dev, "Cannot read hardware identity: error %d\n",
8462306a36Sopenharmony_ci			err);
8562306a36Sopenharmony_ci		return err;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	le16_to_cpus(&nic_id.id);
8962306a36Sopenharmony_ci	le16_to_cpus(&nic_id.variant);
9062306a36Sopenharmony_ci	le16_to_cpus(&nic_id.major);
9162306a36Sopenharmony_ci	le16_to_cpus(&nic_id.minor);
9262306a36Sopenharmony_ci	dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n",
9362306a36Sopenharmony_ci		 nic_id.id, nic_id.variant, nic_id.major, nic_id.minor);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (hw_ver)
9662306a36Sopenharmony_ci		*hw_ver = (((nic_id.id & 0xff) << 24) |
9762306a36Sopenharmony_ci			   ((nic_id.variant & 0xff) << 16) |
9862306a36Sopenharmony_ci			   ((nic_id.major & 0xff) << 8) |
9962306a36Sopenharmony_ci			   (nic_id.minor & 0xff));
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	priv->firmware_type = determine_firmware_type(&nic_id);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Get the firmware version */
10462306a36Sopenharmony_ci	err = HERMES_READ_RECORD_PR(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
10562306a36Sopenharmony_ci	if (err) {
10662306a36Sopenharmony_ci		dev_err(dev, "Cannot read station identity: error %d\n",
10762306a36Sopenharmony_ci			err);
10862306a36Sopenharmony_ci		return err;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	le16_to_cpus(&sta_id.id);
11262306a36Sopenharmony_ci	le16_to_cpus(&sta_id.variant);
11362306a36Sopenharmony_ci	le16_to_cpus(&sta_id.major);
11462306a36Sopenharmony_ci	le16_to_cpus(&sta_id.minor);
11562306a36Sopenharmony_ci	dev_info(dev, "Station identity  %04x:%04x:%04x:%04x\n",
11662306a36Sopenharmony_ci		 sta_id.id, sta_id.variant, sta_id.major, sta_id.minor);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	switch (sta_id.id) {
11962306a36Sopenharmony_ci	case 0x15:
12062306a36Sopenharmony_ci		dev_err(dev, "Primary firmware is active\n");
12162306a36Sopenharmony_ci		return -ENODEV;
12262306a36Sopenharmony_ci	case 0x14b:
12362306a36Sopenharmony_ci		dev_err(dev, "Tertiary firmware is active\n");
12462306a36Sopenharmony_ci		return -ENODEV;
12562306a36Sopenharmony_ci	case 0x1f:	/* Intersil, Agere, Symbol Spectrum24 */
12662306a36Sopenharmony_ci	case 0x21:	/* Symbol Spectrum24 Trilogy */
12762306a36Sopenharmony_ci		break;
12862306a36Sopenharmony_ci	default:
12962306a36Sopenharmony_ci		dev_notice(dev, "Unknown station ID, please report\n");
13062306a36Sopenharmony_ci		break;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Default capabilities */
13462306a36Sopenharmony_ci	priv->has_sensitivity = 1;
13562306a36Sopenharmony_ci	priv->has_mwo = 0;
13662306a36Sopenharmony_ci	priv->has_preamble = 0;
13762306a36Sopenharmony_ci	priv->has_port3 = 1;
13862306a36Sopenharmony_ci	priv->has_ibss = 1;
13962306a36Sopenharmony_ci	priv->has_wep = 0;
14062306a36Sopenharmony_ci	priv->has_big_wep = 0;
14162306a36Sopenharmony_ci	priv->has_alt_txcntl = 0;
14262306a36Sopenharmony_ci	priv->has_ext_scan = 0;
14362306a36Sopenharmony_ci	priv->has_wpa = 0;
14462306a36Sopenharmony_ci	priv->do_fw_download = 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* Determine capabilities from the firmware version */
14762306a36Sopenharmony_ci	switch (priv->firmware_type) {
14862306a36Sopenharmony_ci	case FIRMWARE_TYPE_AGERE:
14962306a36Sopenharmony_ci		/* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
15062306a36Sopenharmony_ci		   ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
15162306a36Sopenharmony_ci		if (fw_name)
15262306a36Sopenharmony_ci			snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d",
15362306a36Sopenharmony_ci				 sta_id.major, sta_id.minor);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		priv->has_ibss = (firmver >= 0x60006);
15862306a36Sopenharmony_ci		priv->has_wep = (firmver >= 0x40020);
15962306a36Sopenharmony_ci		priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
16062306a36Sopenharmony_ci					  Gold cards from the others? */
16162306a36Sopenharmony_ci		priv->has_mwo = (firmver >= 0x60000);
16262306a36Sopenharmony_ci		priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
16362306a36Sopenharmony_ci		priv->ibss_port = 1;
16462306a36Sopenharmony_ci		priv->has_hostscan = (firmver >= 0x8000a);
16562306a36Sopenharmony_ci		priv->do_fw_download = 1;
16662306a36Sopenharmony_ci		priv->broken_monitor = (firmver >= 0x80000);
16762306a36Sopenharmony_ci		priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
16862306a36Sopenharmony_ci		priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
16962306a36Sopenharmony_ci		priv->has_wpa = (firmver >= 0x9002a);
17062306a36Sopenharmony_ci		/* Tested with Agere firmware :
17162306a36Sopenharmony_ci		 *	1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
17262306a36Sopenharmony_ci		 * Tested CableTron firmware : 4.32 => Anton */
17362306a36Sopenharmony_ci		break;
17462306a36Sopenharmony_ci	case FIRMWARE_TYPE_SYMBOL:
17562306a36Sopenharmony_ci		/* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
17662306a36Sopenharmony_ci		/* Intel MAC : 00:02:B3:* */
17762306a36Sopenharmony_ci		/* 3Com MAC : 00:50:DA:* */
17862306a36Sopenharmony_ci		memset(tmp, 0, sizeof(tmp));
17962306a36Sopenharmony_ci		/* Get the Symbol firmware version */
18062306a36Sopenharmony_ci		err = hw->ops->read_ltv_pr(hw, USER_BAP,
18162306a36Sopenharmony_ci					HERMES_RID_SECONDARYVERSION_SYMBOL,
18262306a36Sopenharmony_ci					SYMBOL_MAX_VER_LEN, NULL, &tmp);
18362306a36Sopenharmony_ci		if (err) {
18462306a36Sopenharmony_ci			dev_warn(dev, "Error %d reading Symbol firmware info. "
18562306a36Sopenharmony_ci				 "Wildly guessing capabilities...\n", err);
18662306a36Sopenharmony_ci			firmver = 0;
18762306a36Sopenharmony_ci			tmp[0] = '\0';
18862306a36Sopenharmony_ci		} else {
18962306a36Sopenharmony_ci			/* The firmware revision is a string, the format is
19062306a36Sopenharmony_ci			 * something like : "V2.20-01".
19162306a36Sopenharmony_ci			 * Quick and dirty parsing... - Jean II
19262306a36Sopenharmony_ci			 */
19362306a36Sopenharmony_ci			firmver = ((tmp[1] - '0') << 16)
19462306a36Sopenharmony_ci				| ((tmp[3] - '0') << 12)
19562306a36Sopenharmony_ci				| ((tmp[4] - '0') << 8)
19662306a36Sopenharmony_ci				| ((tmp[6] - '0') << 4)
19762306a36Sopenharmony_ci				| (tmp[7] - '0');
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci			tmp[SYMBOL_MAX_VER_LEN] = '\0';
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		if (fw_name)
20362306a36Sopenharmony_ci			snprintf(fw_name, fw_name_len, "Symbol %s", tmp);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		priv->has_ibss = (firmver >= 0x20000);
20662306a36Sopenharmony_ci		priv->has_wep = (firmver >= 0x15012);
20762306a36Sopenharmony_ci		priv->has_big_wep = (firmver >= 0x20000);
20862306a36Sopenharmony_ci		priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
20962306a36Sopenharmony_ci			       (firmver >= 0x29000 && firmver < 0x30000) ||
21062306a36Sopenharmony_ci			       firmver >= 0x31000;
21162306a36Sopenharmony_ci		priv->has_preamble = (firmver >= 0x20000);
21262306a36Sopenharmony_ci		priv->ibss_port = 4;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		/* Symbol firmware is found on various cards, but
21562306a36Sopenharmony_ci		 * there has been no attempt to check firmware
21662306a36Sopenharmony_ci		 * download on non-spectrum_cs based cards.
21762306a36Sopenharmony_ci		 *
21862306a36Sopenharmony_ci		 * Given that the Agere firmware download works
21962306a36Sopenharmony_ci		 * differently, we should avoid doing a firmware
22062306a36Sopenharmony_ci		 * download with the Symbol algorithm on non-spectrum
22162306a36Sopenharmony_ci		 * cards.
22262306a36Sopenharmony_ci		 *
22362306a36Sopenharmony_ci		 * For now we can identify a spectrum_cs based card
22462306a36Sopenharmony_ci		 * because it has a firmware reset function.
22562306a36Sopenharmony_ci		 */
22662306a36Sopenharmony_ci		priv->do_fw_download = (priv->stop_fw != NULL);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		priv->broken_disableport = (firmver == 0x25013) ||
22962306a36Sopenharmony_ci				(firmver >= 0x30000 && firmver <= 0x31000);
23062306a36Sopenharmony_ci		priv->has_hostscan = (firmver >= 0x31001) ||
23162306a36Sopenharmony_ci				     (firmver >= 0x29057 && firmver < 0x30000);
23262306a36Sopenharmony_ci		/* Tested with Intel firmware : 0x20015 => Jean II */
23362306a36Sopenharmony_ci		/* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
23462306a36Sopenharmony_ci		break;
23562306a36Sopenharmony_ci	case FIRMWARE_TYPE_INTERSIL:
23662306a36Sopenharmony_ci		/* D-Link, Linksys, Adtron, ZoomAir, and many others...
23762306a36Sopenharmony_ci		 * Samsung, Compaq 100/200 and Proxim are slightly
23862306a36Sopenharmony_ci		 * different and less well tested */
23962306a36Sopenharmony_ci		/* D-Link MAC : 00:40:05:* */
24062306a36Sopenharmony_ci		/* Addtron MAC : 00:90:D1:* */
24162306a36Sopenharmony_ci		if (fw_name)
24262306a36Sopenharmony_ci			snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d",
24362306a36Sopenharmony_ci				 sta_id.major, sta_id.minor, sta_id.variant);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		firmver = ((unsigned long)sta_id.major << 16) |
24662306a36Sopenharmony_ci			((unsigned long)sta_id.minor << 8) | sta_id.variant;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		priv->has_ibss = (firmver >= 0x000700); /* FIXME */
24962306a36Sopenharmony_ci		priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
25062306a36Sopenharmony_ci		priv->has_pm = (firmver >= 0x000700);
25162306a36Sopenharmony_ci		priv->has_hostscan = (firmver >= 0x010301);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		if (firmver >= 0x000800)
25462306a36Sopenharmony_ci			priv->ibss_port = 0;
25562306a36Sopenharmony_ci		else {
25662306a36Sopenharmony_ci			dev_notice(dev, "Intersil firmware earlier than v0.8.x"
25762306a36Sopenharmony_ci				   " - several features not supported\n");
25862306a36Sopenharmony_ci			priv->ibss_port = 1;
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci		break;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	if (fw_name)
26362306a36Sopenharmony_ci		dev_info(dev, "Firmware determined as %s\n", fw_name);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci#ifndef CONFIG_HERMES_PRISM
26662306a36Sopenharmony_ci	if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
26762306a36Sopenharmony_ci		dev_err(dev, "Support for Prism chipset is not enabled\n");
26862306a36Sopenharmony_ci		return -ENODEV;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci#endif
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return 0;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/* Read settings from EEPROM into our private structure.
27662306a36Sopenharmony_ci * MAC address gets dropped into callers buffer
27762306a36Sopenharmony_ci * Can be called before netdev registration.
27862306a36Sopenharmony_ci */
27962306a36Sopenharmony_ciint orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct device *dev = priv->dev;
28262306a36Sopenharmony_ci	struct hermes_idstring nickbuf;
28362306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
28462306a36Sopenharmony_ci	int len;
28562306a36Sopenharmony_ci	int err;
28662306a36Sopenharmony_ci	u16 reclen;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Get the MAC address */
28962306a36Sopenharmony_ci	err = hw->ops->read_ltv_pr(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
29062306a36Sopenharmony_ci				ETH_ALEN, NULL, dev_addr);
29162306a36Sopenharmony_ci	if (err) {
29262306a36Sopenharmony_ci		dev_warn(dev, "Failed to read MAC address!\n");
29362306a36Sopenharmony_ci		goto out;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	dev_dbg(dev, "MAC address %pM\n", dev_addr);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* Get the station name */
29962306a36Sopenharmony_ci	err = hw->ops->read_ltv_pr(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
30062306a36Sopenharmony_ci				sizeof(nickbuf), &reclen, &nickbuf);
30162306a36Sopenharmony_ci	if (err) {
30262306a36Sopenharmony_ci		dev_err(dev, "failed to read station name\n");
30362306a36Sopenharmony_ci		goto out;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci	if (nickbuf.len)
30662306a36Sopenharmony_ci		len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
30762306a36Sopenharmony_ci	else
30862306a36Sopenharmony_ci		len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
30962306a36Sopenharmony_ci	memcpy(priv->nick, &nickbuf.val, len);
31062306a36Sopenharmony_ci	priv->nick[len] = '\0';
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	dev_dbg(dev, "Station name \"%s\"\n", priv->nick);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* Get allowed channels */
31562306a36Sopenharmony_ci	err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_CHANNELLIST,
31662306a36Sopenharmony_ci				  &priv->channel_mask);
31762306a36Sopenharmony_ci	if (err) {
31862306a36Sopenharmony_ci		dev_err(dev, "Failed to read channel list!\n");
31962306a36Sopenharmony_ci		goto out;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* Get initial AP density */
32362306a36Sopenharmony_ci	err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
32462306a36Sopenharmony_ci				  &priv->ap_density);
32562306a36Sopenharmony_ci	if (err || priv->ap_density < 1 || priv->ap_density > 3)
32662306a36Sopenharmony_ci		priv->has_sensitivity = 0;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* Get initial RTS threshold */
32962306a36Sopenharmony_ci	err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
33062306a36Sopenharmony_ci				  &priv->rts_thresh);
33162306a36Sopenharmony_ci	if (err) {
33262306a36Sopenharmony_ci		dev_err(dev, "Failed to read RTS threshold!\n");
33362306a36Sopenharmony_ci		goto out;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Get initial fragmentation settings */
33762306a36Sopenharmony_ci	if (priv->has_mwo)
33862306a36Sopenharmony_ci		err = hermes_read_wordrec_pr(hw, USER_BAP,
33962306a36Sopenharmony_ci					  HERMES_RID_CNFMWOROBUST_AGERE,
34062306a36Sopenharmony_ci					  &priv->mwo_robust);
34162306a36Sopenharmony_ci	else
34262306a36Sopenharmony_ci		err = hermes_read_wordrec_pr(hw, USER_BAP,
34362306a36Sopenharmony_ci					  HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
34462306a36Sopenharmony_ci					  &priv->frag_thresh);
34562306a36Sopenharmony_ci	if (err) {
34662306a36Sopenharmony_ci		dev_err(dev, "Failed to read fragmentation settings!\n");
34762306a36Sopenharmony_ci		goto out;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Power management setup */
35162306a36Sopenharmony_ci	if (priv->has_pm) {
35262306a36Sopenharmony_ci		priv->pm_on = 0;
35362306a36Sopenharmony_ci		priv->pm_mcast = 1;
35462306a36Sopenharmony_ci		err = hermes_read_wordrec_pr(hw, USER_BAP,
35562306a36Sopenharmony_ci					  HERMES_RID_CNFMAXSLEEPDURATION,
35662306a36Sopenharmony_ci					  &priv->pm_period);
35762306a36Sopenharmony_ci		if (err) {
35862306a36Sopenharmony_ci			dev_err(dev, "Failed to read power management "
35962306a36Sopenharmony_ci				"period!\n");
36062306a36Sopenharmony_ci			goto out;
36162306a36Sopenharmony_ci		}
36262306a36Sopenharmony_ci		err = hermes_read_wordrec_pr(hw, USER_BAP,
36362306a36Sopenharmony_ci					  HERMES_RID_CNFPMHOLDOVERDURATION,
36462306a36Sopenharmony_ci					  &priv->pm_timeout);
36562306a36Sopenharmony_ci		if (err) {
36662306a36Sopenharmony_ci			dev_err(dev, "Failed to read power management "
36762306a36Sopenharmony_ci				"timeout!\n");
36862306a36Sopenharmony_ci			goto out;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	/* Preamble setup */
37362306a36Sopenharmony_ci	if (priv->has_preamble) {
37462306a36Sopenharmony_ci		err = hermes_read_wordrec_pr(hw, USER_BAP,
37562306a36Sopenharmony_ci					  HERMES_RID_CNFPREAMBLE_SYMBOL,
37662306a36Sopenharmony_ci					  &priv->preamble);
37762306a36Sopenharmony_ci		if (err) {
37862306a36Sopenharmony_ci			dev_err(dev, "Failed to read preamble setup\n");
37962306a36Sopenharmony_ci			goto out;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* Retry settings */
38462306a36Sopenharmony_ci	err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
38562306a36Sopenharmony_ci				  &priv->short_retry_limit);
38662306a36Sopenharmony_ci	if (err) {
38762306a36Sopenharmony_ci		dev_err(dev, "Failed to read short retry limit\n");
38862306a36Sopenharmony_ci		goto out;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
39262306a36Sopenharmony_ci				  &priv->long_retry_limit);
39362306a36Sopenharmony_ci	if (err) {
39462306a36Sopenharmony_ci		dev_err(dev, "Failed to read long retry limit\n");
39562306a36Sopenharmony_ci		goto out;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	err = hermes_read_wordrec_pr(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
39962306a36Sopenharmony_ci				  &priv->retry_lifetime);
40062306a36Sopenharmony_ci	if (err) {
40162306a36Sopenharmony_ci		dev_err(dev, "Failed to read max retry lifetime\n");
40262306a36Sopenharmony_ci		goto out;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ciout:
40662306a36Sopenharmony_ci	return err;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/* Can be called before netdev registration */
41062306a36Sopenharmony_ciint orinoco_hw_allocate_fid(struct orinoco_private *priv)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct device *dev = priv->dev;
41362306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
41462306a36Sopenharmony_ci	int err;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
41762306a36Sopenharmony_ci	if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
41862306a36Sopenharmony_ci		/* Try workaround for old Symbol firmware bug */
41962306a36Sopenharmony_ci		priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
42062306a36Sopenharmony_ci		err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		dev_warn(dev, "Firmware ALLOC bug detected "
42362306a36Sopenharmony_ci			 "(old Symbol firmware?). Work around %s\n",
42462306a36Sopenharmony_ci			 err ? "failed!" : "ok.");
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	return err;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ciint orinoco_get_bitratemode(int bitrate, int automatic)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	int ratemode = -1;
43362306a36Sopenharmony_ci	int i;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if ((bitrate != 10) && (bitrate != 20) &&
43662306a36Sopenharmony_ci	    (bitrate != 55) && (bitrate != 110))
43762306a36Sopenharmony_ci		return ratemode;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	for (i = 0; i < BITRATE_TABLE_SIZE; i++) {
44062306a36Sopenharmony_ci		if ((bitrate_table[i].bitrate == bitrate) &&
44162306a36Sopenharmony_ci		    (bitrate_table[i].automatic == automatic)) {
44262306a36Sopenharmony_ci			ratemode = i;
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci		}
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci	return ratemode;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_civoid orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	*bitrate = bitrate_table[ratemode].bitrate * 100000;
45462306a36Sopenharmony_ci	*automatic = bitrate_table[ratemode].automatic;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ciint orinoco_hw_program_rids(struct orinoco_private *priv)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
46062306a36Sopenharmony_ci	struct wireless_dev *wdev = netdev_priv(dev);
46162306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
46262306a36Sopenharmony_ci	int err;
46362306a36Sopenharmony_ci	struct hermes_idstring idbuf;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* Set the MAC address */
46662306a36Sopenharmony_ci	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
46762306a36Sopenharmony_ci				 HERMES_BYTES_TO_RECLEN(ETH_ALEN),
46862306a36Sopenharmony_ci				 dev->dev_addr);
46962306a36Sopenharmony_ci	if (err) {
47062306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting MAC address\n",
47162306a36Sopenharmony_ci		       dev->name, err);
47262306a36Sopenharmony_ci		return err;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* Set up the link mode */
47662306a36Sopenharmony_ci	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
47762306a36Sopenharmony_ci				   priv->port_type);
47862306a36Sopenharmony_ci	if (err) {
47962306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting port type\n",
48062306a36Sopenharmony_ci		       dev->name, err);
48162306a36Sopenharmony_ci		return err;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci	/* Set the channel/frequency */
48462306a36Sopenharmony_ci	if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) {
48562306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
48662306a36Sopenharmony_ci					   HERMES_RID_CNFOWNCHANNEL,
48762306a36Sopenharmony_ci					   priv->channel);
48862306a36Sopenharmony_ci		if (err) {
48962306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting channel %d\n",
49062306a36Sopenharmony_ci			       dev->name, err, priv->channel);
49162306a36Sopenharmony_ci			return err;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (priv->has_ibss) {
49662306a36Sopenharmony_ci		u16 createibss;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
49962306a36Sopenharmony_ci			printk(KERN_WARNING "%s: This firmware requires an "
50062306a36Sopenharmony_ci			       "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
50162306a36Sopenharmony_ci			/* With wvlan_cs, in this case, we would crash.
50262306a36Sopenharmony_ci			 * hopefully, this driver will behave better...
50362306a36Sopenharmony_ci			 * Jean II */
50462306a36Sopenharmony_ci			createibss = 0;
50562306a36Sopenharmony_ci		} else {
50662306a36Sopenharmony_ci			createibss = priv->createibss;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
51062306a36Sopenharmony_ci					   HERMES_RID_CNFCREATEIBSS,
51162306a36Sopenharmony_ci					   createibss);
51262306a36Sopenharmony_ci		if (err) {
51362306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
51462306a36Sopenharmony_ci			       dev->name, err);
51562306a36Sopenharmony_ci			return err;
51662306a36Sopenharmony_ci		}
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/* Set the desired BSSID */
52062306a36Sopenharmony_ci	err = __orinoco_hw_set_wap(priv);
52162306a36Sopenharmony_ci	if (err) {
52262306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting AP address\n",
52362306a36Sopenharmony_ci		       dev->name, err);
52462306a36Sopenharmony_ci		return err;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Set the desired ESSID */
52862306a36Sopenharmony_ci	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
52962306a36Sopenharmony_ci	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
53062306a36Sopenharmony_ci	/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
53162306a36Sopenharmony_ci	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
53262306a36Sopenharmony_ci			HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
53362306a36Sopenharmony_ci			&idbuf);
53462306a36Sopenharmony_ci	if (err) {
53562306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
53662306a36Sopenharmony_ci		       dev->name, err);
53762306a36Sopenharmony_ci		return err;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
54062306a36Sopenharmony_ci			HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
54162306a36Sopenharmony_ci			&idbuf);
54262306a36Sopenharmony_ci	if (err) {
54362306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
54462306a36Sopenharmony_ci		       dev->name, err);
54562306a36Sopenharmony_ci		return err;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* Set the station name */
54962306a36Sopenharmony_ci	idbuf.len = cpu_to_le16(strlen(priv->nick));
55062306a36Sopenharmony_ci	memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
55162306a36Sopenharmony_ci	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
55262306a36Sopenharmony_ci				 HERMES_BYTES_TO_RECLEN(strlen(priv->nick) + 2),
55362306a36Sopenharmony_ci				 &idbuf);
55462306a36Sopenharmony_ci	if (err) {
55562306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting nickname\n",
55662306a36Sopenharmony_ci		       dev->name, err);
55762306a36Sopenharmony_ci		return err;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* Set AP density */
56162306a36Sopenharmony_ci	if (priv->has_sensitivity) {
56262306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
56362306a36Sopenharmony_ci					   HERMES_RID_CNFSYSTEMSCALE,
56462306a36Sopenharmony_ci					   priv->ap_density);
56562306a36Sopenharmony_ci		if (err) {
56662306a36Sopenharmony_ci			printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
56762306a36Sopenharmony_ci			       "Disabling sensitivity control\n",
56862306a36Sopenharmony_ci			       dev->name, err);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci			priv->has_sensitivity = 0;
57162306a36Sopenharmony_ci		}
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* Set RTS threshold */
57562306a36Sopenharmony_ci	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
57662306a36Sopenharmony_ci				   priv->rts_thresh);
57762306a36Sopenharmony_ci	if (err) {
57862306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
57962306a36Sopenharmony_ci		       dev->name, err);
58062306a36Sopenharmony_ci		return err;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* Set fragmentation threshold or MWO robustness */
58462306a36Sopenharmony_ci	if (priv->has_mwo)
58562306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
58662306a36Sopenharmony_ci					   HERMES_RID_CNFMWOROBUST_AGERE,
58762306a36Sopenharmony_ci					   priv->mwo_robust);
58862306a36Sopenharmony_ci	else
58962306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
59062306a36Sopenharmony_ci					   HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
59162306a36Sopenharmony_ci					   priv->frag_thresh);
59262306a36Sopenharmony_ci	if (err) {
59362306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting fragmentation\n",
59462306a36Sopenharmony_ci		       dev->name, err);
59562306a36Sopenharmony_ci		return err;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	/* Set bitrate */
59962306a36Sopenharmony_ci	err = __orinoco_hw_set_bitrate(priv);
60062306a36Sopenharmony_ci	if (err) {
60162306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d setting bitrate\n",
60262306a36Sopenharmony_ci		       dev->name, err);
60362306a36Sopenharmony_ci		return err;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* Set power management */
60762306a36Sopenharmony_ci	if (priv->has_pm) {
60862306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
60962306a36Sopenharmony_ci					   HERMES_RID_CNFPMENABLED,
61062306a36Sopenharmony_ci					   priv->pm_on);
61162306a36Sopenharmony_ci		if (err) {
61262306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting up PM\n",
61362306a36Sopenharmony_ci			       dev->name, err);
61462306a36Sopenharmony_ci			return err;
61562306a36Sopenharmony_ci		}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
61862306a36Sopenharmony_ci					   HERMES_RID_CNFMULTICASTRECEIVE,
61962306a36Sopenharmony_ci					   priv->pm_mcast);
62062306a36Sopenharmony_ci		if (err) {
62162306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting up PM\n",
62262306a36Sopenharmony_ci			       dev->name, err);
62362306a36Sopenharmony_ci			return err;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
62662306a36Sopenharmony_ci					   HERMES_RID_CNFMAXSLEEPDURATION,
62762306a36Sopenharmony_ci					   priv->pm_period);
62862306a36Sopenharmony_ci		if (err) {
62962306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting up PM\n",
63062306a36Sopenharmony_ci			       dev->name, err);
63162306a36Sopenharmony_ci			return err;
63262306a36Sopenharmony_ci		}
63362306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
63462306a36Sopenharmony_ci					   HERMES_RID_CNFPMHOLDOVERDURATION,
63562306a36Sopenharmony_ci					   priv->pm_timeout);
63662306a36Sopenharmony_ci		if (err) {
63762306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting up PM\n",
63862306a36Sopenharmony_ci			       dev->name, err);
63962306a36Sopenharmony_ci			return err;
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* Set preamble - only for Symbol so far... */
64462306a36Sopenharmony_ci	if (priv->has_preamble) {
64562306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
64662306a36Sopenharmony_ci					   HERMES_RID_CNFPREAMBLE_SYMBOL,
64762306a36Sopenharmony_ci					   priv->preamble);
64862306a36Sopenharmony_ci		if (err) {
64962306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting preamble\n",
65062306a36Sopenharmony_ci			       dev->name, err);
65162306a36Sopenharmony_ci			return err;
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* Set up encryption */
65662306a36Sopenharmony_ci	if (priv->has_wep || priv->has_wpa) {
65762306a36Sopenharmony_ci		err = __orinoco_hw_setup_enc(priv);
65862306a36Sopenharmony_ci		if (err) {
65962306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d activating encryption\n",
66062306a36Sopenharmony_ci			       dev->name, err);
66162306a36Sopenharmony_ci			return err;
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
66662306a36Sopenharmony_ci		/* Enable monitor mode */
66762306a36Sopenharmony_ci		dev->type = ARPHRD_IEEE80211;
66862306a36Sopenharmony_ci		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
66962306a36Sopenharmony_ci					    HERMES_TEST_MONITOR, 0, NULL);
67062306a36Sopenharmony_ci	} else {
67162306a36Sopenharmony_ci		/* Disable monitor mode */
67262306a36Sopenharmony_ci		dev->type = ARPHRD_ETHER;
67362306a36Sopenharmony_ci		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
67462306a36Sopenharmony_ci					    HERMES_TEST_STOP, 0, NULL);
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci	if (err)
67762306a36Sopenharmony_ci		return err;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* Reset promiscuity / multicast*/
68062306a36Sopenharmony_ci	priv->promiscuous = 0;
68162306a36Sopenharmony_ci	priv->mc_count = 0;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* Record mode change */
68462306a36Sopenharmony_ci	wdev->iftype = priv->iw_mode;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci/* Get tsc from the firmware */
69062306a36Sopenharmony_ciint orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
69362306a36Sopenharmony_ci	int err = 0;
69462306a36Sopenharmony_ci	u8 tsc_arr[4][ORINOCO_SEQ_LEN];
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if ((key < 0) || (key >= 4))
69762306a36Sopenharmony_ci		return -EINVAL;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
70062306a36Sopenharmony_ci				sizeof(tsc_arr), NULL, &tsc_arr);
70162306a36Sopenharmony_ci	if (!err)
70262306a36Sopenharmony_ci		memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return err;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ciint __orinoco_hw_set_bitrate(struct orinoco_private *priv)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
71062306a36Sopenharmony_ci	int ratemode = priv->bitratemode;
71162306a36Sopenharmony_ci	int err = 0;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	if (ratemode >= BITRATE_TABLE_SIZE) {
71462306a36Sopenharmony_ci		printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
71562306a36Sopenharmony_ci		       priv->ndev->name, ratemode);
71662306a36Sopenharmony_ci		return -EINVAL;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	switch (priv->firmware_type) {
72062306a36Sopenharmony_ci	case FIRMWARE_TYPE_AGERE:
72162306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
72262306a36Sopenharmony_ci				HERMES_RID_CNFTXRATECONTROL,
72362306a36Sopenharmony_ci				bitrate_table[ratemode].agere_txratectrl);
72462306a36Sopenharmony_ci		break;
72562306a36Sopenharmony_ci	case FIRMWARE_TYPE_INTERSIL:
72662306a36Sopenharmony_ci	case FIRMWARE_TYPE_SYMBOL:
72762306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
72862306a36Sopenharmony_ci				HERMES_RID_CNFTXRATECONTROL,
72962306a36Sopenharmony_ci				bitrate_table[ratemode].intersil_txratectrl);
73062306a36Sopenharmony_ci		break;
73162306a36Sopenharmony_ci	default:
73262306a36Sopenharmony_ci		BUG();
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	return err;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ciint orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
74162306a36Sopenharmony_ci	int i;
74262306a36Sopenharmony_ci	int err = 0;
74362306a36Sopenharmony_ci	u16 val;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	err = hermes_read_wordrec(hw, USER_BAP,
74662306a36Sopenharmony_ci				  HERMES_RID_CURRENTTXRATE, &val);
74762306a36Sopenharmony_ci	if (err)
74862306a36Sopenharmony_ci		return err;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	switch (priv->firmware_type) {
75162306a36Sopenharmony_ci	case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
75262306a36Sopenharmony_ci		/* Note : in Lucent firmware, the return value of
75362306a36Sopenharmony_ci		 * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
75462306a36Sopenharmony_ci		 * and therefore is totally different from the
75562306a36Sopenharmony_ci		 * encoding of HERMES_RID_CNFTXRATECONTROL.
75662306a36Sopenharmony_ci		 * Don't forget that 6Mb/s is really 5.5Mb/s */
75762306a36Sopenharmony_ci		if (val == 6)
75862306a36Sopenharmony_ci			*bitrate = 5500000;
75962306a36Sopenharmony_ci		else
76062306a36Sopenharmony_ci			*bitrate = val * 1000000;
76162306a36Sopenharmony_ci		break;
76262306a36Sopenharmony_ci	case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
76362306a36Sopenharmony_ci	case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
76462306a36Sopenharmony_ci		for (i = 0; i < BITRATE_TABLE_SIZE; i++)
76562306a36Sopenharmony_ci			if (bitrate_table[i].intersil_txratectrl == val) {
76662306a36Sopenharmony_ci				*bitrate = bitrate_table[i].bitrate * 100000;
76762306a36Sopenharmony_ci				break;
76862306a36Sopenharmony_ci			}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		if (i >= BITRATE_TABLE_SIZE) {
77162306a36Sopenharmony_ci			printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
77262306a36Sopenharmony_ci			       priv->ndev->name, val);
77362306a36Sopenharmony_ci			err = -EIO;
77462306a36Sopenharmony_ci		}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		break;
77762306a36Sopenharmony_ci	default:
77862306a36Sopenharmony_ci		BUG();
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return err;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci/* Set fixed AP address */
78562306a36Sopenharmony_ciint __orinoco_hw_set_wap(struct orinoco_private *priv)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	int roaming_flag;
78862306a36Sopenharmony_ci	int err = 0;
78962306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	switch (priv->firmware_type) {
79262306a36Sopenharmony_ci	case FIRMWARE_TYPE_AGERE:
79362306a36Sopenharmony_ci		/* not supported */
79462306a36Sopenharmony_ci		break;
79562306a36Sopenharmony_ci	case FIRMWARE_TYPE_INTERSIL:
79662306a36Sopenharmony_ci		if (priv->bssid_fixed)
79762306a36Sopenharmony_ci			roaming_flag = 2;
79862306a36Sopenharmony_ci		else
79962306a36Sopenharmony_ci			roaming_flag = 1;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
80262306a36Sopenharmony_ci					   HERMES_RID_CNFROAMINGMODE,
80362306a36Sopenharmony_ci					   roaming_flag);
80462306a36Sopenharmony_ci		break;
80562306a36Sopenharmony_ci	case FIRMWARE_TYPE_SYMBOL:
80662306a36Sopenharmony_ci		err = HERMES_WRITE_RECORD(hw, USER_BAP,
80762306a36Sopenharmony_ci					  HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
80862306a36Sopenharmony_ci					  &priv->desired_bssid);
80962306a36Sopenharmony_ci		break;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci	return err;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci/* Change the WEP keys and/or the current keys.  Can be called
81562306a36Sopenharmony_ci * either from __orinoco_hw_setup_enc() or directly from
81662306a36Sopenharmony_ci * orinoco_ioctl_setiwencode().  In the later case the association
81762306a36Sopenharmony_ci * with the AP is not broken (if the firmware can handle it),
81862306a36Sopenharmony_ci * which is needed for 802.1x implementations. */
81962306a36Sopenharmony_ciint __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
82262306a36Sopenharmony_ci	int err = 0;
82362306a36Sopenharmony_ci	int i;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	switch (priv->firmware_type) {
82662306a36Sopenharmony_ci	case FIRMWARE_TYPE_AGERE:
82762306a36Sopenharmony_ci	{
82862306a36Sopenharmony_ci		struct orinoco_key keys[ORINOCO_MAX_KEYS];
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		memset(&keys, 0, sizeof(keys));
83162306a36Sopenharmony_ci		for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
83262306a36Sopenharmony_ci			int len = min(priv->keys[i].key_len,
83362306a36Sopenharmony_ci				      ORINOCO_MAX_KEY_SIZE);
83462306a36Sopenharmony_ci			memcpy(&keys[i].data, priv->keys[i].key, len);
83562306a36Sopenharmony_ci			if (len > SMALL_KEY_SIZE)
83662306a36Sopenharmony_ci				keys[i].len = cpu_to_le16(LARGE_KEY_SIZE);
83762306a36Sopenharmony_ci			else if (len > 0)
83862306a36Sopenharmony_ci				keys[i].len = cpu_to_le16(SMALL_KEY_SIZE);
83962306a36Sopenharmony_ci			else
84062306a36Sopenharmony_ci				keys[i].len = cpu_to_le16(0);
84162306a36Sopenharmony_ci		}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci		err = HERMES_WRITE_RECORD(hw, USER_BAP,
84462306a36Sopenharmony_ci					  HERMES_RID_CNFWEPKEYS_AGERE,
84562306a36Sopenharmony_ci					  &keys);
84662306a36Sopenharmony_ci		if (err)
84762306a36Sopenharmony_ci			return err;
84862306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
84962306a36Sopenharmony_ci					   HERMES_RID_CNFTXKEY_AGERE,
85062306a36Sopenharmony_ci					   priv->tx_key);
85162306a36Sopenharmony_ci		if (err)
85262306a36Sopenharmony_ci			return err;
85362306a36Sopenharmony_ci		break;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci	case FIRMWARE_TYPE_INTERSIL:
85662306a36Sopenharmony_ci	case FIRMWARE_TYPE_SYMBOL:
85762306a36Sopenharmony_ci		{
85862306a36Sopenharmony_ci			int keylen;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci			/* Force uniform key length to work around
86162306a36Sopenharmony_ci			 * firmware bugs */
86262306a36Sopenharmony_ci			keylen = priv->keys[priv->tx_key].key_len;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci			if (keylen > LARGE_KEY_SIZE) {
86562306a36Sopenharmony_ci				printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
86662306a36Sopenharmony_ci				       priv->ndev->name, priv->tx_key, keylen);
86762306a36Sopenharmony_ci				return -E2BIG;
86862306a36Sopenharmony_ci			} else if (keylen > SMALL_KEY_SIZE)
86962306a36Sopenharmony_ci				keylen = LARGE_KEY_SIZE;
87062306a36Sopenharmony_ci			else if (keylen > 0)
87162306a36Sopenharmony_ci				keylen = SMALL_KEY_SIZE;
87262306a36Sopenharmony_ci			else
87362306a36Sopenharmony_ci				keylen = 0;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci			/* Write all 4 keys */
87662306a36Sopenharmony_ci			for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
87762306a36Sopenharmony_ci				u8 key[LARGE_KEY_SIZE] = { 0 };
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci				memcpy(key, priv->keys[i].key,
88062306a36Sopenharmony_ci				       priv->keys[i].key_len);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci				err = hw->ops->write_ltv(hw, USER_BAP,
88362306a36Sopenharmony_ci						HERMES_RID_CNFDEFAULTKEY0 + i,
88462306a36Sopenharmony_ci						HERMES_BYTES_TO_RECLEN(keylen),
88562306a36Sopenharmony_ci						key);
88662306a36Sopenharmony_ci				if (err)
88762306a36Sopenharmony_ci					return err;
88862306a36Sopenharmony_ci			}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci			/* Write the index of the key used in transmission */
89162306a36Sopenharmony_ci			err = hermes_write_wordrec(hw, USER_BAP,
89262306a36Sopenharmony_ci						HERMES_RID_CNFWEPDEFAULTKEYID,
89362306a36Sopenharmony_ci						priv->tx_key);
89462306a36Sopenharmony_ci			if (err)
89562306a36Sopenharmony_ci				return err;
89662306a36Sopenharmony_ci		}
89762306a36Sopenharmony_ci		break;
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	return 0;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ciint __orinoco_hw_setup_enc(struct orinoco_private *priv)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
90662306a36Sopenharmony_ci	int err = 0;
90762306a36Sopenharmony_ci	int master_wep_flag;
90862306a36Sopenharmony_ci	int auth_flag;
90962306a36Sopenharmony_ci	int enc_flag;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	/* Setup WEP keys */
91262306a36Sopenharmony_ci	if (priv->encode_alg == ORINOCO_ALG_WEP)
91362306a36Sopenharmony_ci		__orinoco_hw_setup_wepkeys(priv);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (priv->wep_restrict)
91662306a36Sopenharmony_ci		auth_flag = HERMES_AUTH_SHARED_KEY;
91762306a36Sopenharmony_ci	else
91862306a36Sopenharmony_ci		auth_flag = HERMES_AUTH_OPEN;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (priv->wpa_enabled)
92162306a36Sopenharmony_ci		enc_flag = 2;
92262306a36Sopenharmony_ci	else if (priv->encode_alg == ORINOCO_ALG_WEP)
92362306a36Sopenharmony_ci		enc_flag = 1;
92462306a36Sopenharmony_ci	else
92562306a36Sopenharmony_ci		enc_flag = 0;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	switch (priv->firmware_type) {
92862306a36Sopenharmony_ci	case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
92962306a36Sopenharmony_ci		if (priv->encode_alg == ORINOCO_ALG_WEP) {
93062306a36Sopenharmony_ci			/* Enable the shared-key authentication. */
93162306a36Sopenharmony_ci			err = hermes_write_wordrec(hw, USER_BAP,
93262306a36Sopenharmony_ci					HERMES_RID_CNFAUTHENTICATION_AGERE,
93362306a36Sopenharmony_ci					auth_flag);
93462306a36Sopenharmony_ci			if (err)
93562306a36Sopenharmony_ci				return err;
93662306a36Sopenharmony_ci		}
93762306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
93862306a36Sopenharmony_ci					   HERMES_RID_CNFWEPENABLED_AGERE,
93962306a36Sopenharmony_ci					   enc_flag);
94062306a36Sopenharmony_ci		if (err)
94162306a36Sopenharmony_ci			return err;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		if (priv->has_wpa) {
94462306a36Sopenharmony_ci			/* Set WPA key management */
94562306a36Sopenharmony_ci			err = hermes_write_wordrec(hw, USER_BAP,
94662306a36Sopenharmony_ci				  HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
94762306a36Sopenharmony_ci				  priv->key_mgmt);
94862306a36Sopenharmony_ci			if (err)
94962306a36Sopenharmony_ci				return err;
95062306a36Sopenharmony_ci		}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci		break;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
95562306a36Sopenharmony_ci	case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
95662306a36Sopenharmony_ci		if (priv->encode_alg == ORINOCO_ALG_WEP) {
95762306a36Sopenharmony_ci			if (priv->wep_restrict ||
95862306a36Sopenharmony_ci			    (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
95962306a36Sopenharmony_ci				master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
96062306a36Sopenharmony_ci						  HERMES_WEP_EXCL_UNENCRYPTED;
96162306a36Sopenharmony_ci			else
96262306a36Sopenharmony_ci				master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci			err = hermes_write_wordrec(hw, USER_BAP,
96562306a36Sopenharmony_ci						   HERMES_RID_CNFAUTHENTICATION,
96662306a36Sopenharmony_ci						   auth_flag);
96762306a36Sopenharmony_ci			if (err)
96862306a36Sopenharmony_ci				return err;
96962306a36Sopenharmony_ci		} else
97062306a36Sopenharmony_ci			master_wep_flag = 0;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		if (priv->iw_mode == NL80211_IFTYPE_MONITOR)
97362306a36Sopenharmony_ci			master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		/* Master WEP setting : on/off */
97662306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
97762306a36Sopenharmony_ci					   HERMES_RID_CNFWEPFLAGS_INTERSIL,
97862306a36Sopenharmony_ci					   master_wep_flag);
97962306a36Sopenharmony_ci		if (err)
98062306a36Sopenharmony_ci			return err;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		break;
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci/* key must be 32 bytes, including the tx and rx MIC keys.
98962306a36Sopenharmony_ci * rsc must be NULL or up to 8 bytes
99062306a36Sopenharmony_ci * tsc must be NULL or up to 8 bytes
99162306a36Sopenharmony_ci */
99262306a36Sopenharmony_ciint __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
99362306a36Sopenharmony_ci			      int set_tx, const u8 *key, size_t key_len,
99462306a36Sopenharmony_ci			      const u8 *rsc, size_t rsc_len,
99562306a36Sopenharmony_ci			      const u8 *tsc, size_t tsc_len)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	struct {
99862306a36Sopenharmony_ci		__le16 idx;
99962306a36Sopenharmony_ci		u8 rsc[ORINOCO_SEQ_LEN];
100062306a36Sopenharmony_ci		struct {
100162306a36Sopenharmony_ci			u8 key[TKIP_KEYLEN];
100262306a36Sopenharmony_ci			u8 tx_mic[MIC_KEYLEN];
100362306a36Sopenharmony_ci			u8 rx_mic[MIC_KEYLEN];
100462306a36Sopenharmony_ci		} tkip;
100562306a36Sopenharmony_ci		u8 tsc[ORINOCO_SEQ_LEN];
100662306a36Sopenharmony_ci	} __packed buf;
100762306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
100862306a36Sopenharmony_ci	int ret;
100962306a36Sopenharmony_ci	int err;
101062306a36Sopenharmony_ci	int k;
101162306a36Sopenharmony_ci	u16 xmitting;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	key_idx &= 0x3;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (set_tx)
101662306a36Sopenharmony_ci		key_idx |= 0x8000;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	buf.idx = cpu_to_le16(key_idx);
101962306a36Sopenharmony_ci	if (key_len != sizeof(buf.tkip))
102062306a36Sopenharmony_ci		return -EINVAL;
102162306a36Sopenharmony_ci	memcpy(&buf.tkip, key, sizeof(buf.tkip));
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (rsc_len > sizeof(buf.rsc))
102462306a36Sopenharmony_ci		rsc_len = sizeof(buf.rsc);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if (tsc_len > sizeof(buf.tsc))
102762306a36Sopenharmony_ci		tsc_len = sizeof(buf.tsc);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	memset(buf.rsc, 0, sizeof(buf.rsc));
103062306a36Sopenharmony_ci	memset(buf.tsc, 0, sizeof(buf.tsc));
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (rsc != NULL)
103362306a36Sopenharmony_ci		memcpy(buf.rsc, rsc, rsc_len);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (tsc != NULL)
103662306a36Sopenharmony_ci		memcpy(buf.tsc, tsc, tsc_len);
103762306a36Sopenharmony_ci	else
103862306a36Sopenharmony_ci		buf.tsc[4] = 0x10;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* Wait up to 100ms for tx queue to empty */
104162306a36Sopenharmony_ci	for (k = 100; k > 0; k--) {
104262306a36Sopenharmony_ci		udelay(1000);
104362306a36Sopenharmony_ci		ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
104462306a36Sopenharmony_ci					  &xmitting);
104562306a36Sopenharmony_ci		if (ret || !xmitting)
104662306a36Sopenharmony_ci			break;
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (k == 0)
105062306a36Sopenharmony_ci		ret = -ETIMEDOUT;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	err = HERMES_WRITE_RECORD(hw, USER_BAP,
105362306a36Sopenharmony_ci				  HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
105462306a36Sopenharmony_ci				  &buf);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	return ret ? ret : err;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ciint orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
106262306a36Sopenharmony_ci	int err;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	err = hermes_write_wordrec(hw, USER_BAP,
106562306a36Sopenharmony_ci				   HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
106662306a36Sopenharmony_ci				   key_idx);
106762306a36Sopenharmony_ci	if (err)
106862306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
106962306a36Sopenharmony_ci		       priv->ndev->name, err, key_idx);
107062306a36Sopenharmony_ci	return err;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ciint __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
107462306a36Sopenharmony_ci				    struct net_device *dev,
107562306a36Sopenharmony_ci				    int mc_count, int promisc)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
107862306a36Sopenharmony_ci	int err = 0;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	if (promisc != priv->promiscuous) {
108162306a36Sopenharmony_ci		err = hermes_write_wordrec(hw, USER_BAP,
108262306a36Sopenharmony_ci					   HERMES_RID_CNFPROMISCUOUSMODE,
108362306a36Sopenharmony_ci					   promisc);
108462306a36Sopenharmony_ci		if (err) {
108562306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
108662306a36Sopenharmony_ci			       priv->ndev->name, err);
108762306a36Sopenharmony_ci		} else
108862306a36Sopenharmony_ci			priv->promiscuous = promisc;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	/* If we're not in promiscuous mode, then we need to set the
109262306a36Sopenharmony_ci	 * group address if either we want to multicast, or if we were
109362306a36Sopenharmony_ci	 * multicasting and want to stop */
109462306a36Sopenharmony_ci	if (!promisc && (mc_count || priv->mc_count)) {
109562306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
109662306a36Sopenharmony_ci		struct hermes_multicast mclist;
109762306a36Sopenharmony_ci		int i = 0;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
110062306a36Sopenharmony_ci			if (i == mc_count)
110162306a36Sopenharmony_ci				break;
110262306a36Sopenharmony_ci			memcpy(mclist.addr[i++], ha->addr, ETH_ALEN);
110362306a36Sopenharmony_ci		}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		err = hw->ops->write_ltv(hw, USER_BAP,
110662306a36Sopenharmony_ci				   HERMES_RID_CNFGROUPADDRESSES,
110762306a36Sopenharmony_ci				   HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
110862306a36Sopenharmony_ci				   &mclist);
110962306a36Sopenharmony_ci		if (err)
111062306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
111162306a36Sopenharmony_ci			       priv->ndev->name, err);
111262306a36Sopenharmony_ci		else
111362306a36Sopenharmony_ci			priv->mc_count = mc_count;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci	return err;
111662306a36Sopenharmony_ci}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci/* Return : < 0 -> error code ; >= 0 -> length */
111962306a36Sopenharmony_ciint orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
112062306a36Sopenharmony_ci			 char buf[IW_ESSID_MAX_SIZE + 1])
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
112362306a36Sopenharmony_ci	int err = 0;
112462306a36Sopenharmony_ci	struct hermes_idstring essidbuf;
112562306a36Sopenharmony_ci	char *p = (char *)(&essidbuf.val);
112662306a36Sopenharmony_ci	int len;
112762306a36Sopenharmony_ci	unsigned long flags;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
113062306a36Sopenharmony_ci		return -EBUSY;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (strlen(priv->desired_essid) > 0) {
113362306a36Sopenharmony_ci		/* We read the desired SSID from the hardware rather
113462306a36Sopenharmony_ci		   than from priv->desired_essid, just in case the
113562306a36Sopenharmony_ci		   firmware is allowed to change it on us. I'm not
113662306a36Sopenharmony_ci		   sure about this */
113762306a36Sopenharmony_ci		/* My guess is that the OWNSSID should always be whatever
113862306a36Sopenharmony_ci		 * we set to the card, whereas CURRENT_SSID is the one that
113962306a36Sopenharmony_ci		 * may change... - Jean II */
114062306a36Sopenharmony_ci		u16 rid;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci		*active = 1;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
114562306a36Sopenharmony_ci			HERMES_RID_CNFDESIREDSSID;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci		err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
114862306a36Sopenharmony_ci					NULL, &essidbuf);
114962306a36Sopenharmony_ci		if (err)
115062306a36Sopenharmony_ci			goto fail_unlock;
115162306a36Sopenharmony_ci	} else {
115262306a36Sopenharmony_ci		*active = 0;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci		err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
115562306a36Sopenharmony_ci					sizeof(essidbuf), NULL, &essidbuf);
115662306a36Sopenharmony_ci		if (err)
115762306a36Sopenharmony_ci			goto fail_unlock;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	len = le16_to_cpu(essidbuf.len);
116162306a36Sopenharmony_ci	BUG_ON(len > IW_ESSID_MAX_SIZE);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	memset(buf, 0, IW_ESSID_MAX_SIZE);
116462306a36Sopenharmony_ci	memcpy(buf, p, len);
116562306a36Sopenharmony_ci	err = len;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci fail_unlock:
116862306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	return err;
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ciint orinoco_hw_get_freq(struct orinoco_private *priv)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
117662306a36Sopenharmony_ci	int err = 0;
117762306a36Sopenharmony_ci	u16 channel;
117862306a36Sopenharmony_ci	int freq = 0;
117962306a36Sopenharmony_ci	unsigned long flags;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
118262306a36Sopenharmony_ci		return -EBUSY;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL,
118562306a36Sopenharmony_ci				  &channel);
118662306a36Sopenharmony_ci	if (err)
118762306a36Sopenharmony_ci		goto out;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	/* Intersil firmware 1.3.5 returns 0 when the interface is down */
119062306a36Sopenharmony_ci	if (channel == 0) {
119162306a36Sopenharmony_ci		err = -EBUSY;
119262306a36Sopenharmony_ci		goto out;
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	if ((channel < 1) || (channel > NUM_CHANNELS)) {
119662306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
119762306a36Sopenharmony_ci		       priv->ndev->name, channel);
119862306a36Sopenharmony_ci		err = -EBUSY;
119962306a36Sopenharmony_ci		goto out;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci	freq = ieee80211_channel_to_frequency(channel, NL80211_BAND_2GHZ);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci out:
120562306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (err > 0)
120862306a36Sopenharmony_ci		err = -EBUSY;
120962306a36Sopenharmony_ci	return err ? err : freq;
121062306a36Sopenharmony_ci}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ciint orinoco_hw_get_bitratelist(struct orinoco_private *priv,
121362306a36Sopenharmony_ci			       int *numrates, s32 *rates, int max)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
121662306a36Sopenharmony_ci	struct hermes_idstring list;
121762306a36Sopenharmony_ci	unsigned char *p = (unsigned char *)&list.val;
121862306a36Sopenharmony_ci	int err = 0;
121962306a36Sopenharmony_ci	int num;
122062306a36Sopenharmony_ci	int i;
122162306a36Sopenharmony_ci	unsigned long flags;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
122462306a36Sopenharmony_ci		return -EBUSY;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
122762306a36Sopenharmony_ci				sizeof(list), NULL, &list);
122862306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (err)
123162306a36Sopenharmony_ci		return err;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	num = le16_to_cpu(list.len);
123462306a36Sopenharmony_ci	*numrates = num;
123562306a36Sopenharmony_ci	num = min(num, max);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	for (i = 0; i < num; i++)
123862306a36Sopenharmony_ci		rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	return 0;
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ciint orinoco_hw_trigger_scan(struct orinoco_private *priv,
124462306a36Sopenharmony_ci			    const struct cfg80211_ssid *ssid)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct net_device *dev = priv->ndev;
124762306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
124862306a36Sopenharmony_ci	unsigned long flags;
124962306a36Sopenharmony_ci	int err = 0;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	if (orinoco_lock(priv, &flags) != 0)
125262306a36Sopenharmony_ci		return -EBUSY;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* Scanning with port 0 disabled would fail */
125562306a36Sopenharmony_ci	if (!netif_running(dev)) {
125662306a36Sopenharmony_ci		err = -ENETDOWN;
125762306a36Sopenharmony_ci		goto out;
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	/* In monitor mode, the scan results are always empty.
126162306a36Sopenharmony_ci	 * Probe responses are passed to the driver as received
126262306a36Sopenharmony_ci	 * frames and could be processed in software. */
126362306a36Sopenharmony_ci	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
126462306a36Sopenharmony_ci		err = -EOPNOTSUPP;
126562306a36Sopenharmony_ci		goto out;
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (priv->has_hostscan) {
126962306a36Sopenharmony_ci		switch (priv->firmware_type) {
127062306a36Sopenharmony_ci		case FIRMWARE_TYPE_SYMBOL:
127162306a36Sopenharmony_ci			err = hermes_write_wordrec(hw, USER_BAP,
127262306a36Sopenharmony_ci						HERMES_RID_CNFHOSTSCAN_SYMBOL,
127362306a36Sopenharmony_ci						HERMES_HOSTSCAN_SYMBOL_ONCE |
127462306a36Sopenharmony_ci						HERMES_HOSTSCAN_SYMBOL_BCAST);
127562306a36Sopenharmony_ci			break;
127662306a36Sopenharmony_ci		case FIRMWARE_TYPE_INTERSIL: {
127762306a36Sopenharmony_ci			__le16 req[3];
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci			req[0] = cpu_to_le16(0x3fff);	/* All channels */
128062306a36Sopenharmony_ci			req[1] = cpu_to_le16(0x0001);	/* rate 1 Mbps */
128162306a36Sopenharmony_ci			req[2] = 0;			/* Any ESSID */
128262306a36Sopenharmony_ci			err = HERMES_WRITE_RECORD(hw, USER_BAP,
128362306a36Sopenharmony_ci						  HERMES_RID_CNFHOSTSCAN, &req);
128462306a36Sopenharmony_ci			break;
128562306a36Sopenharmony_ci		}
128662306a36Sopenharmony_ci		case FIRMWARE_TYPE_AGERE:
128762306a36Sopenharmony_ci			if (ssid->ssid_len > 0) {
128862306a36Sopenharmony_ci				struct hermes_idstring idbuf;
128962306a36Sopenharmony_ci				size_t len = ssid->ssid_len;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci				idbuf.len = cpu_to_le16(len);
129262306a36Sopenharmony_ci				memcpy(idbuf.val, ssid->ssid, len);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci				err = hw->ops->write_ltv(hw, USER_BAP,
129562306a36Sopenharmony_ci					       HERMES_RID_CNFSCANSSID_AGERE,
129662306a36Sopenharmony_ci					       HERMES_BYTES_TO_RECLEN(len + 2),
129762306a36Sopenharmony_ci					       &idbuf);
129862306a36Sopenharmony_ci			} else
129962306a36Sopenharmony_ci				err = hermes_write_wordrec(hw, USER_BAP,
130062306a36Sopenharmony_ci						   HERMES_RID_CNFSCANSSID_AGERE,
130162306a36Sopenharmony_ci						   0);	/* Any ESSID */
130262306a36Sopenharmony_ci			if (err)
130362306a36Sopenharmony_ci				break;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci			if (priv->has_ext_scan) {
130662306a36Sopenharmony_ci				err = hermes_write_wordrec(hw, USER_BAP,
130762306a36Sopenharmony_ci						HERMES_RID_CNFSCANCHANNELS2GHZ,
130862306a36Sopenharmony_ci						0x7FFF);
130962306a36Sopenharmony_ci				if (err)
131062306a36Sopenharmony_ci					goto out;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci				err = hermes_inquire(hw,
131362306a36Sopenharmony_ci						     HERMES_INQ_CHANNELINFO);
131462306a36Sopenharmony_ci			} else
131562306a36Sopenharmony_ci				err = hermes_inquire(hw, HERMES_INQ_SCAN);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci			break;
131862306a36Sopenharmony_ci		}
131962306a36Sopenharmony_ci	} else
132062306a36Sopenharmony_ci		err = hermes_inquire(hw, HERMES_INQ_SCAN);
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci out:
132362306a36Sopenharmony_ci	orinoco_unlock(priv, &flags);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	return err;
132662306a36Sopenharmony_ci}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci/* Disassociate from node with BSSID addr */
132962306a36Sopenharmony_ciint orinoco_hw_disassociate(struct orinoco_private *priv,
133062306a36Sopenharmony_ci			    u8 *addr, u16 reason_code)
133162306a36Sopenharmony_ci{
133262306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
133362306a36Sopenharmony_ci	int err;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	struct {
133662306a36Sopenharmony_ci		u8 addr[ETH_ALEN];
133762306a36Sopenharmony_ci		__le16 reason_code;
133862306a36Sopenharmony_ci	} __packed buf;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	/* Currently only supported by WPA enabled Agere fw */
134162306a36Sopenharmony_ci	if (!priv->has_wpa)
134262306a36Sopenharmony_ci		return -EOPNOTSUPP;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	memcpy(buf.addr, addr, ETH_ALEN);
134562306a36Sopenharmony_ci	buf.reason_code = cpu_to_le16(reason_code);
134662306a36Sopenharmony_ci	err = HERMES_WRITE_RECORD(hw, USER_BAP,
134762306a36Sopenharmony_ci				  HERMES_RID_CNFDISASSOCIATE,
134862306a36Sopenharmony_ci				  &buf);
134962306a36Sopenharmony_ci	return err;
135062306a36Sopenharmony_ci}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ciint orinoco_hw_get_current_bssid(struct orinoco_private *priv,
135362306a36Sopenharmony_ci				 u8 *addr)
135462306a36Sopenharmony_ci{
135562306a36Sopenharmony_ci	struct hermes *hw = &priv->hw;
135662306a36Sopenharmony_ci	int err;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
135962306a36Sopenharmony_ci				ETH_ALEN, NULL, addr);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	return err;
136262306a36Sopenharmony_ci}
1363