xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/intersil/orinoco/hw.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /* Encapsulate basic setting changes and retrieval on Hermes hardware
2*4882a593Smuzhiyun  *
3*4882a593Smuzhiyun  * See copyright notice in main.c
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun #include <linux/kernel.h>
6*4882a593Smuzhiyun #include <linux/device.h>
7*4882a593Smuzhiyun #include <linux/if_arp.h>
8*4882a593Smuzhiyun #include <linux/ieee80211.h>
9*4882a593Smuzhiyun #include <linux/wireless.h>
10*4882a593Smuzhiyun #include <net/cfg80211.h>
11*4882a593Smuzhiyun #include "hermes.h"
12*4882a593Smuzhiyun #include "hermes_rid.h"
13*4882a593Smuzhiyun #include "orinoco.h"
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include "hw.h"
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #define SYMBOL_MAX_VER_LEN	(14)
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun /* Symbol firmware has a bug allocating buffers larger than this */
20*4882a593Smuzhiyun #define TX_NICBUF_SIZE_BUG	1585
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun /********************************************************************/
23*4882a593Smuzhiyun /* Data tables                                                      */
24*4882a593Smuzhiyun /********************************************************************/
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /* This tables gives the actual meanings of the bitrate IDs returned
27*4882a593Smuzhiyun  * by the firmware. */
28*4882a593Smuzhiyun static const struct {
29*4882a593Smuzhiyun 	int bitrate; /* in 100s of kilobits */
30*4882a593Smuzhiyun 	int automatic;
31*4882a593Smuzhiyun 	u16 agere_txratectrl;
32*4882a593Smuzhiyun 	u16 intersil_txratectrl;
33*4882a593Smuzhiyun } bitrate_table[] = {
34*4882a593Smuzhiyun 	{110, 1,  3, 15}, /* Entry 0 is the default */
35*4882a593Smuzhiyun 	{10,  0,  1,  1},
36*4882a593Smuzhiyun 	{10,  1,  1,  1},
37*4882a593Smuzhiyun 	{20,  0,  2,  2},
38*4882a593Smuzhiyun 	{20,  1,  6,  3},
39*4882a593Smuzhiyun 	{55,  0,  4,  4},
40*4882a593Smuzhiyun 	{55,  1,  7,  7},
41*4882a593Smuzhiyun 	{110, 0,  5,  8},
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun #define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table)
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun /* Firmware version encoding */
46*4882a593Smuzhiyun struct comp_id {
47*4882a593Smuzhiyun 	u16 id, variant, major, minor;
48*4882a593Smuzhiyun } __packed;
49*4882a593Smuzhiyun 
determine_firmware_type(struct comp_id * nic_id)50*4882a593Smuzhiyun static inline enum fwtype determine_firmware_type(struct comp_id *nic_id)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun 	if (nic_id->id < 0x8000)
53*4882a593Smuzhiyun 		return FIRMWARE_TYPE_AGERE;
54*4882a593Smuzhiyun 	else if (nic_id->id == 0x8000 && nic_id->major == 0)
55*4882a593Smuzhiyun 		return FIRMWARE_TYPE_SYMBOL;
56*4882a593Smuzhiyun 	else
57*4882a593Smuzhiyun 		return FIRMWARE_TYPE_INTERSIL;
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun /* Set priv->firmware type, determine firmware properties
61*4882a593Smuzhiyun  * This function can be called before we have registerred with netdev,
62*4882a593Smuzhiyun  * so all errors go out with dev_* rather than printk
63*4882a593Smuzhiyun  *
64*4882a593Smuzhiyun  * If non-NULL stores a firmware description in fw_name.
65*4882a593Smuzhiyun  * If non-NULL stores a HW version in hw_ver
66*4882a593Smuzhiyun  *
67*4882a593Smuzhiyun  * These are output via generic cfg80211 ethtool support.
68*4882a593Smuzhiyun  */
determine_fw_capabilities(struct orinoco_private * priv,char * fw_name,size_t fw_name_len,u32 * hw_ver)69*4882a593Smuzhiyun int determine_fw_capabilities(struct orinoco_private *priv,
70*4882a593Smuzhiyun 			      char *fw_name, size_t fw_name_len,
71*4882a593Smuzhiyun 			      u32 *hw_ver)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun 	struct device *dev = priv->dev;
74*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
75*4882a593Smuzhiyun 	int err;
76*4882a593Smuzhiyun 	struct comp_id nic_id, sta_id;
77*4882a593Smuzhiyun 	unsigned int firmver;
78*4882a593Smuzhiyun 	char tmp[SYMBOL_MAX_VER_LEN + 1] __attribute__((aligned(2)));
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	/* Get the hardware version */
81*4882a593Smuzhiyun 	err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
82*4882a593Smuzhiyun 	if (err) {
83*4882a593Smuzhiyun 		dev_err(dev, "Cannot read hardware identity: error %d\n",
84*4882a593Smuzhiyun 			err);
85*4882a593Smuzhiyun 		return err;
86*4882a593Smuzhiyun 	}
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	le16_to_cpus(&nic_id.id);
89*4882a593Smuzhiyun 	le16_to_cpus(&nic_id.variant);
90*4882a593Smuzhiyun 	le16_to_cpus(&nic_id.major);
91*4882a593Smuzhiyun 	le16_to_cpus(&nic_id.minor);
92*4882a593Smuzhiyun 	dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n",
93*4882a593Smuzhiyun 		 nic_id.id, nic_id.variant, nic_id.major, nic_id.minor);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	if (hw_ver)
96*4882a593Smuzhiyun 		*hw_ver = (((nic_id.id & 0xff) << 24) |
97*4882a593Smuzhiyun 			   ((nic_id.variant & 0xff) << 16) |
98*4882a593Smuzhiyun 			   ((nic_id.major & 0xff) << 8) |
99*4882a593Smuzhiyun 			   (nic_id.minor & 0xff));
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	priv->firmware_type = determine_firmware_type(&nic_id);
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	/* Get the firmware version */
104*4882a593Smuzhiyun 	err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
105*4882a593Smuzhiyun 	if (err) {
106*4882a593Smuzhiyun 		dev_err(dev, "Cannot read station identity: error %d\n",
107*4882a593Smuzhiyun 			err);
108*4882a593Smuzhiyun 		return err;
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	le16_to_cpus(&sta_id.id);
112*4882a593Smuzhiyun 	le16_to_cpus(&sta_id.variant);
113*4882a593Smuzhiyun 	le16_to_cpus(&sta_id.major);
114*4882a593Smuzhiyun 	le16_to_cpus(&sta_id.minor);
115*4882a593Smuzhiyun 	dev_info(dev, "Station identity  %04x:%04x:%04x:%04x\n",
116*4882a593Smuzhiyun 		 sta_id.id, sta_id.variant, sta_id.major, sta_id.minor);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	switch (sta_id.id) {
119*4882a593Smuzhiyun 	case 0x15:
120*4882a593Smuzhiyun 		dev_err(dev, "Primary firmware is active\n");
121*4882a593Smuzhiyun 		return -ENODEV;
122*4882a593Smuzhiyun 	case 0x14b:
123*4882a593Smuzhiyun 		dev_err(dev, "Tertiary firmware is active\n");
124*4882a593Smuzhiyun 		return -ENODEV;
125*4882a593Smuzhiyun 	case 0x1f:	/* Intersil, Agere, Symbol Spectrum24 */
126*4882a593Smuzhiyun 	case 0x21:	/* Symbol Spectrum24 Trilogy */
127*4882a593Smuzhiyun 		break;
128*4882a593Smuzhiyun 	default:
129*4882a593Smuzhiyun 		dev_notice(dev, "Unknown station ID, please report\n");
130*4882a593Smuzhiyun 		break;
131*4882a593Smuzhiyun 	}
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	/* Default capabilities */
134*4882a593Smuzhiyun 	priv->has_sensitivity = 1;
135*4882a593Smuzhiyun 	priv->has_mwo = 0;
136*4882a593Smuzhiyun 	priv->has_preamble = 0;
137*4882a593Smuzhiyun 	priv->has_port3 = 1;
138*4882a593Smuzhiyun 	priv->has_ibss = 1;
139*4882a593Smuzhiyun 	priv->has_wep = 0;
140*4882a593Smuzhiyun 	priv->has_big_wep = 0;
141*4882a593Smuzhiyun 	priv->has_alt_txcntl = 0;
142*4882a593Smuzhiyun 	priv->has_ext_scan = 0;
143*4882a593Smuzhiyun 	priv->has_wpa = 0;
144*4882a593Smuzhiyun 	priv->do_fw_download = 0;
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	/* Determine capabilities from the firmware version */
147*4882a593Smuzhiyun 	switch (priv->firmware_type) {
148*4882a593Smuzhiyun 	case FIRMWARE_TYPE_AGERE:
149*4882a593Smuzhiyun 		/* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
150*4882a593Smuzhiyun 		   ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
151*4882a593Smuzhiyun 		if (fw_name)
152*4882a593Smuzhiyun 			snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d",
153*4882a593Smuzhiyun 				 sta_id.major, sta_id.minor);
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 		firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 		priv->has_ibss = (firmver >= 0x60006);
158*4882a593Smuzhiyun 		priv->has_wep = (firmver >= 0x40020);
159*4882a593Smuzhiyun 		priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
160*4882a593Smuzhiyun 					  Gold cards from the others? */
161*4882a593Smuzhiyun 		priv->has_mwo = (firmver >= 0x60000);
162*4882a593Smuzhiyun 		priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
163*4882a593Smuzhiyun 		priv->ibss_port = 1;
164*4882a593Smuzhiyun 		priv->has_hostscan = (firmver >= 0x8000a);
165*4882a593Smuzhiyun 		priv->do_fw_download = 1;
166*4882a593Smuzhiyun 		priv->broken_monitor = (firmver >= 0x80000);
167*4882a593Smuzhiyun 		priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
168*4882a593Smuzhiyun 		priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
169*4882a593Smuzhiyun 		priv->has_wpa = (firmver >= 0x9002a);
170*4882a593Smuzhiyun 		/* Tested with Agere firmware :
171*4882a593Smuzhiyun 		 *	1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
172*4882a593Smuzhiyun 		 * Tested CableTron firmware : 4.32 => Anton */
173*4882a593Smuzhiyun 		break;
174*4882a593Smuzhiyun 	case FIRMWARE_TYPE_SYMBOL:
175*4882a593Smuzhiyun 		/* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
176*4882a593Smuzhiyun 		/* Intel MAC : 00:02:B3:* */
177*4882a593Smuzhiyun 		/* 3Com MAC : 00:50:DA:* */
178*4882a593Smuzhiyun 		memset(tmp, 0, sizeof(tmp));
179*4882a593Smuzhiyun 		/* Get the Symbol firmware version */
180*4882a593Smuzhiyun 		err = hw->ops->read_ltv(hw, USER_BAP,
181*4882a593Smuzhiyun 					HERMES_RID_SECONDARYVERSION_SYMBOL,
182*4882a593Smuzhiyun 					SYMBOL_MAX_VER_LEN, NULL, &tmp);
183*4882a593Smuzhiyun 		if (err) {
184*4882a593Smuzhiyun 			dev_warn(dev, "Error %d reading Symbol firmware info. "
185*4882a593Smuzhiyun 				 "Wildly guessing capabilities...\n", err);
186*4882a593Smuzhiyun 			firmver = 0;
187*4882a593Smuzhiyun 			tmp[0] = '\0';
188*4882a593Smuzhiyun 		} else {
189*4882a593Smuzhiyun 			/* The firmware revision is a string, the format is
190*4882a593Smuzhiyun 			 * something like : "V2.20-01".
191*4882a593Smuzhiyun 			 * Quick and dirty parsing... - Jean II
192*4882a593Smuzhiyun 			 */
193*4882a593Smuzhiyun 			firmver = ((tmp[1] - '0') << 16)
194*4882a593Smuzhiyun 				| ((tmp[3] - '0') << 12)
195*4882a593Smuzhiyun 				| ((tmp[4] - '0') << 8)
196*4882a593Smuzhiyun 				| ((tmp[6] - '0') << 4)
197*4882a593Smuzhiyun 				| (tmp[7] - '0');
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 			tmp[SYMBOL_MAX_VER_LEN] = '\0';
200*4882a593Smuzhiyun 		}
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 		if (fw_name)
203*4882a593Smuzhiyun 			snprintf(fw_name, fw_name_len, "Symbol %s", tmp);
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 		priv->has_ibss = (firmver >= 0x20000);
206*4882a593Smuzhiyun 		priv->has_wep = (firmver >= 0x15012);
207*4882a593Smuzhiyun 		priv->has_big_wep = (firmver >= 0x20000);
208*4882a593Smuzhiyun 		priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
209*4882a593Smuzhiyun 			       (firmver >= 0x29000 && firmver < 0x30000) ||
210*4882a593Smuzhiyun 			       firmver >= 0x31000;
211*4882a593Smuzhiyun 		priv->has_preamble = (firmver >= 0x20000);
212*4882a593Smuzhiyun 		priv->ibss_port = 4;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 		/* Symbol firmware is found on various cards, but
215*4882a593Smuzhiyun 		 * there has been no attempt to check firmware
216*4882a593Smuzhiyun 		 * download on non-spectrum_cs based cards.
217*4882a593Smuzhiyun 		 *
218*4882a593Smuzhiyun 		 * Given that the Agere firmware download works
219*4882a593Smuzhiyun 		 * differently, we should avoid doing a firmware
220*4882a593Smuzhiyun 		 * download with the Symbol algorithm on non-spectrum
221*4882a593Smuzhiyun 		 * cards.
222*4882a593Smuzhiyun 		 *
223*4882a593Smuzhiyun 		 * For now we can identify a spectrum_cs based card
224*4882a593Smuzhiyun 		 * because it has a firmware reset function.
225*4882a593Smuzhiyun 		 */
226*4882a593Smuzhiyun 		priv->do_fw_download = (priv->stop_fw != NULL);
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 		priv->broken_disableport = (firmver == 0x25013) ||
229*4882a593Smuzhiyun 				(firmver >= 0x30000 && firmver <= 0x31000);
230*4882a593Smuzhiyun 		priv->has_hostscan = (firmver >= 0x31001) ||
231*4882a593Smuzhiyun 				     (firmver >= 0x29057 && firmver < 0x30000);
232*4882a593Smuzhiyun 		/* Tested with Intel firmware : 0x20015 => Jean II */
233*4882a593Smuzhiyun 		/* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
234*4882a593Smuzhiyun 		break;
235*4882a593Smuzhiyun 	case FIRMWARE_TYPE_INTERSIL:
236*4882a593Smuzhiyun 		/* D-Link, Linksys, Adtron, ZoomAir, and many others...
237*4882a593Smuzhiyun 		 * Samsung, Compaq 100/200 and Proxim are slightly
238*4882a593Smuzhiyun 		 * different and less well tested */
239*4882a593Smuzhiyun 		/* D-Link MAC : 00:40:05:* */
240*4882a593Smuzhiyun 		/* Addtron MAC : 00:90:D1:* */
241*4882a593Smuzhiyun 		if (fw_name)
242*4882a593Smuzhiyun 			snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d",
243*4882a593Smuzhiyun 				 sta_id.major, sta_id.minor, sta_id.variant);
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 		firmver = ((unsigned long)sta_id.major << 16) |
246*4882a593Smuzhiyun 			((unsigned long)sta_id.minor << 8) | sta_id.variant;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 		priv->has_ibss = (firmver >= 0x000700); /* FIXME */
249*4882a593Smuzhiyun 		priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
250*4882a593Smuzhiyun 		priv->has_pm = (firmver >= 0x000700);
251*4882a593Smuzhiyun 		priv->has_hostscan = (firmver >= 0x010301);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 		if (firmver >= 0x000800)
254*4882a593Smuzhiyun 			priv->ibss_port = 0;
255*4882a593Smuzhiyun 		else {
256*4882a593Smuzhiyun 			dev_notice(dev, "Intersil firmware earlier than v0.8.x"
257*4882a593Smuzhiyun 				   " - several features not supported\n");
258*4882a593Smuzhiyun 			priv->ibss_port = 1;
259*4882a593Smuzhiyun 		}
260*4882a593Smuzhiyun 		break;
261*4882a593Smuzhiyun 	}
262*4882a593Smuzhiyun 	if (fw_name)
263*4882a593Smuzhiyun 		dev_info(dev, "Firmware determined as %s\n", fw_name);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun #ifndef CONFIG_HERMES_PRISM
266*4882a593Smuzhiyun 	if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
267*4882a593Smuzhiyun 		dev_err(dev, "Support for Prism chipset is not enabled\n");
268*4882a593Smuzhiyun 		return -ENODEV;
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun #endif
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	return 0;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun /* Read settings from EEPROM into our private structure.
276*4882a593Smuzhiyun  * MAC address gets dropped into callers buffer
277*4882a593Smuzhiyun  * Can be called before netdev registration.
278*4882a593Smuzhiyun  */
orinoco_hw_read_card_settings(struct orinoco_private * priv,u8 * dev_addr)279*4882a593Smuzhiyun int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun 	struct device *dev = priv->dev;
282*4882a593Smuzhiyun 	struct hermes_idstring nickbuf;
283*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
284*4882a593Smuzhiyun 	int len;
285*4882a593Smuzhiyun 	int err;
286*4882a593Smuzhiyun 	u16 reclen;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	/* Get the MAC address */
289*4882a593Smuzhiyun 	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
290*4882a593Smuzhiyun 				ETH_ALEN, NULL, dev_addr);
291*4882a593Smuzhiyun 	if (err) {
292*4882a593Smuzhiyun 		dev_warn(dev, "Failed to read MAC address!\n");
293*4882a593Smuzhiyun 		goto out;
294*4882a593Smuzhiyun 	}
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	dev_dbg(dev, "MAC address %pM\n", dev_addr);
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	/* Get the station name */
299*4882a593Smuzhiyun 	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
300*4882a593Smuzhiyun 				sizeof(nickbuf), &reclen, &nickbuf);
301*4882a593Smuzhiyun 	if (err) {
302*4882a593Smuzhiyun 		dev_err(dev, "failed to read station name\n");
303*4882a593Smuzhiyun 		goto out;
304*4882a593Smuzhiyun 	}
305*4882a593Smuzhiyun 	if (nickbuf.len)
306*4882a593Smuzhiyun 		len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
307*4882a593Smuzhiyun 	else
308*4882a593Smuzhiyun 		len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
309*4882a593Smuzhiyun 	memcpy(priv->nick, &nickbuf.val, len);
310*4882a593Smuzhiyun 	priv->nick[len] = '\0';
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	dev_dbg(dev, "Station name \"%s\"\n", priv->nick);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	/* Get allowed channels */
315*4882a593Smuzhiyun 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
316*4882a593Smuzhiyun 				  &priv->channel_mask);
317*4882a593Smuzhiyun 	if (err) {
318*4882a593Smuzhiyun 		dev_err(dev, "Failed to read channel list!\n");
319*4882a593Smuzhiyun 		goto out;
320*4882a593Smuzhiyun 	}
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	/* Get initial AP density */
323*4882a593Smuzhiyun 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
324*4882a593Smuzhiyun 				  &priv->ap_density);
325*4882a593Smuzhiyun 	if (err || priv->ap_density < 1 || priv->ap_density > 3)
326*4882a593Smuzhiyun 		priv->has_sensitivity = 0;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	/* Get initial RTS threshold */
329*4882a593Smuzhiyun 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
330*4882a593Smuzhiyun 				  &priv->rts_thresh);
331*4882a593Smuzhiyun 	if (err) {
332*4882a593Smuzhiyun 		dev_err(dev, "Failed to read RTS threshold!\n");
333*4882a593Smuzhiyun 		goto out;
334*4882a593Smuzhiyun 	}
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	/* Get initial fragmentation settings */
337*4882a593Smuzhiyun 	if (priv->has_mwo)
338*4882a593Smuzhiyun 		err = hermes_read_wordrec(hw, USER_BAP,
339*4882a593Smuzhiyun 					  HERMES_RID_CNFMWOROBUST_AGERE,
340*4882a593Smuzhiyun 					  &priv->mwo_robust);
341*4882a593Smuzhiyun 	else
342*4882a593Smuzhiyun 		err = hermes_read_wordrec(hw, USER_BAP,
343*4882a593Smuzhiyun 					  HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
344*4882a593Smuzhiyun 					  &priv->frag_thresh);
345*4882a593Smuzhiyun 	if (err) {
346*4882a593Smuzhiyun 		dev_err(dev, "Failed to read fragmentation settings!\n");
347*4882a593Smuzhiyun 		goto out;
348*4882a593Smuzhiyun 	}
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	/* Power management setup */
351*4882a593Smuzhiyun 	if (priv->has_pm) {
352*4882a593Smuzhiyun 		priv->pm_on = 0;
353*4882a593Smuzhiyun 		priv->pm_mcast = 1;
354*4882a593Smuzhiyun 		err = hermes_read_wordrec(hw, USER_BAP,
355*4882a593Smuzhiyun 					  HERMES_RID_CNFMAXSLEEPDURATION,
356*4882a593Smuzhiyun 					  &priv->pm_period);
357*4882a593Smuzhiyun 		if (err) {
358*4882a593Smuzhiyun 			dev_err(dev, "Failed to read power management "
359*4882a593Smuzhiyun 				"period!\n");
360*4882a593Smuzhiyun 			goto out;
361*4882a593Smuzhiyun 		}
362*4882a593Smuzhiyun 		err = hermes_read_wordrec(hw, USER_BAP,
363*4882a593Smuzhiyun 					  HERMES_RID_CNFPMHOLDOVERDURATION,
364*4882a593Smuzhiyun 					  &priv->pm_timeout);
365*4882a593Smuzhiyun 		if (err) {
366*4882a593Smuzhiyun 			dev_err(dev, "Failed to read power management "
367*4882a593Smuzhiyun 				"timeout!\n");
368*4882a593Smuzhiyun 			goto out;
369*4882a593Smuzhiyun 		}
370*4882a593Smuzhiyun 	}
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	/* Preamble setup */
373*4882a593Smuzhiyun 	if (priv->has_preamble) {
374*4882a593Smuzhiyun 		err = hermes_read_wordrec(hw, USER_BAP,
375*4882a593Smuzhiyun 					  HERMES_RID_CNFPREAMBLE_SYMBOL,
376*4882a593Smuzhiyun 					  &priv->preamble);
377*4882a593Smuzhiyun 		if (err) {
378*4882a593Smuzhiyun 			dev_err(dev, "Failed to read preamble setup\n");
379*4882a593Smuzhiyun 			goto out;
380*4882a593Smuzhiyun 		}
381*4882a593Smuzhiyun 	}
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	/* Retry settings */
384*4882a593Smuzhiyun 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
385*4882a593Smuzhiyun 				  &priv->short_retry_limit);
386*4882a593Smuzhiyun 	if (err) {
387*4882a593Smuzhiyun 		dev_err(dev, "Failed to read short retry limit\n");
388*4882a593Smuzhiyun 		goto out;
389*4882a593Smuzhiyun 	}
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
392*4882a593Smuzhiyun 				  &priv->long_retry_limit);
393*4882a593Smuzhiyun 	if (err) {
394*4882a593Smuzhiyun 		dev_err(dev, "Failed to read long retry limit\n");
395*4882a593Smuzhiyun 		goto out;
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
399*4882a593Smuzhiyun 				  &priv->retry_lifetime);
400*4882a593Smuzhiyun 	if (err) {
401*4882a593Smuzhiyun 		dev_err(dev, "Failed to read max retry lifetime\n");
402*4882a593Smuzhiyun 		goto out;
403*4882a593Smuzhiyun 	}
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun out:
406*4882a593Smuzhiyun 	return err;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun /* Can be called before netdev registration */
orinoco_hw_allocate_fid(struct orinoco_private * priv)410*4882a593Smuzhiyun int orinoco_hw_allocate_fid(struct orinoco_private *priv)
411*4882a593Smuzhiyun {
412*4882a593Smuzhiyun 	struct device *dev = priv->dev;
413*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
414*4882a593Smuzhiyun 	int err;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
417*4882a593Smuzhiyun 	if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
418*4882a593Smuzhiyun 		/* Try workaround for old Symbol firmware bug */
419*4882a593Smuzhiyun 		priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
420*4882a593Smuzhiyun 		err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 		dev_warn(dev, "Firmware ALLOC bug detected "
423*4882a593Smuzhiyun 			 "(old Symbol firmware?). Work around %s\n",
424*4882a593Smuzhiyun 			 err ? "failed!" : "ok.");
425*4882a593Smuzhiyun 	}
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	return err;
428*4882a593Smuzhiyun }
429*4882a593Smuzhiyun 
orinoco_get_bitratemode(int bitrate,int automatic)430*4882a593Smuzhiyun int orinoco_get_bitratemode(int bitrate, int automatic)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun 	int ratemode = -1;
433*4882a593Smuzhiyun 	int i;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	if ((bitrate != 10) && (bitrate != 20) &&
436*4882a593Smuzhiyun 	    (bitrate != 55) && (bitrate != 110))
437*4882a593Smuzhiyun 		return ratemode;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	for (i = 0; i < BITRATE_TABLE_SIZE; i++) {
440*4882a593Smuzhiyun 		if ((bitrate_table[i].bitrate == bitrate) &&
441*4882a593Smuzhiyun 		    (bitrate_table[i].automatic == automatic)) {
442*4882a593Smuzhiyun 			ratemode = i;
443*4882a593Smuzhiyun 			break;
444*4882a593Smuzhiyun 		}
445*4882a593Smuzhiyun 	}
446*4882a593Smuzhiyun 	return ratemode;
447*4882a593Smuzhiyun }
448*4882a593Smuzhiyun 
orinoco_get_ratemode_cfg(int ratemode,int * bitrate,int * automatic)449*4882a593Smuzhiyun void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic)
450*4882a593Smuzhiyun {
451*4882a593Smuzhiyun 	BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	*bitrate = bitrate_table[ratemode].bitrate * 100000;
454*4882a593Smuzhiyun 	*automatic = bitrate_table[ratemode].automatic;
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun 
orinoco_hw_program_rids(struct orinoco_private * priv)457*4882a593Smuzhiyun int orinoco_hw_program_rids(struct orinoco_private *priv)
458*4882a593Smuzhiyun {
459*4882a593Smuzhiyun 	struct net_device *dev = priv->ndev;
460*4882a593Smuzhiyun 	struct wireless_dev *wdev = netdev_priv(dev);
461*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
462*4882a593Smuzhiyun 	int err;
463*4882a593Smuzhiyun 	struct hermes_idstring idbuf;
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	/* Set the MAC address */
466*4882a593Smuzhiyun 	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
467*4882a593Smuzhiyun 				 HERMES_BYTES_TO_RECLEN(ETH_ALEN),
468*4882a593Smuzhiyun 				 dev->dev_addr);
469*4882a593Smuzhiyun 	if (err) {
470*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting MAC address\n",
471*4882a593Smuzhiyun 		       dev->name, err);
472*4882a593Smuzhiyun 		return err;
473*4882a593Smuzhiyun 	}
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	/* Set up the link mode */
476*4882a593Smuzhiyun 	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
477*4882a593Smuzhiyun 				   priv->port_type);
478*4882a593Smuzhiyun 	if (err) {
479*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting port type\n",
480*4882a593Smuzhiyun 		       dev->name, err);
481*4882a593Smuzhiyun 		return err;
482*4882a593Smuzhiyun 	}
483*4882a593Smuzhiyun 	/* Set the channel/frequency */
484*4882a593Smuzhiyun 	if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) {
485*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
486*4882a593Smuzhiyun 					   HERMES_RID_CNFOWNCHANNEL,
487*4882a593Smuzhiyun 					   priv->channel);
488*4882a593Smuzhiyun 		if (err) {
489*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting channel %d\n",
490*4882a593Smuzhiyun 			       dev->name, err, priv->channel);
491*4882a593Smuzhiyun 			return err;
492*4882a593Smuzhiyun 		}
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	if (priv->has_ibss) {
496*4882a593Smuzhiyun 		u16 createibss;
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 		if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
499*4882a593Smuzhiyun 			printk(KERN_WARNING "%s: This firmware requires an "
500*4882a593Smuzhiyun 			       "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
501*4882a593Smuzhiyun 			/* With wvlan_cs, in this case, we would crash.
502*4882a593Smuzhiyun 			 * hopefully, this driver will behave better...
503*4882a593Smuzhiyun 			 * Jean II */
504*4882a593Smuzhiyun 			createibss = 0;
505*4882a593Smuzhiyun 		} else {
506*4882a593Smuzhiyun 			createibss = priv->createibss;
507*4882a593Smuzhiyun 		}
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
510*4882a593Smuzhiyun 					   HERMES_RID_CNFCREATEIBSS,
511*4882a593Smuzhiyun 					   createibss);
512*4882a593Smuzhiyun 		if (err) {
513*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
514*4882a593Smuzhiyun 			       dev->name, err);
515*4882a593Smuzhiyun 			return err;
516*4882a593Smuzhiyun 		}
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	/* Set the desired BSSID */
520*4882a593Smuzhiyun 	err = __orinoco_hw_set_wap(priv);
521*4882a593Smuzhiyun 	if (err) {
522*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting AP address\n",
523*4882a593Smuzhiyun 		       dev->name, err);
524*4882a593Smuzhiyun 		return err;
525*4882a593Smuzhiyun 	}
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	/* Set the desired ESSID */
528*4882a593Smuzhiyun 	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
529*4882a593Smuzhiyun 	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
530*4882a593Smuzhiyun 	/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
531*4882a593Smuzhiyun 	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
532*4882a593Smuzhiyun 			HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
533*4882a593Smuzhiyun 			&idbuf);
534*4882a593Smuzhiyun 	if (err) {
535*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
536*4882a593Smuzhiyun 		       dev->name, err);
537*4882a593Smuzhiyun 		return err;
538*4882a593Smuzhiyun 	}
539*4882a593Smuzhiyun 	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
540*4882a593Smuzhiyun 			HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2),
541*4882a593Smuzhiyun 			&idbuf);
542*4882a593Smuzhiyun 	if (err) {
543*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
544*4882a593Smuzhiyun 		       dev->name, err);
545*4882a593Smuzhiyun 		return err;
546*4882a593Smuzhiyun 	}
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	/* Set the station name */
549*4882a593Smuzhiyun 	idbuf.len = cpu_to_le16(strlen(priv->nick));
550*4882a593Smuzhiyun 	memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
551*4882a593Smuzhiyun 	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
552*4882a593Smuzhiyun 				 HERMES_BYTES_TO_RECLEN(strlen(priv->nick) + 2),
553*4882a593Smuzhiyun 				 &idbuf);
554*4882a593Smuzhiyun 	if (err) {
555*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting nickname\n",
556*4882a593Smuzhiyun 		       dev->name, err);
557*4882a593Smuzhiyun 		return err;
558*4882a593Smuzhiyun 	}
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	/* Set AP density */
561*4882a593Smuzhiyun 	if (priv->has_sensitivity) {
562*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
563*4882a593Smuzhiyun 					   HERMES_RID_CNFSYSTEMSCALE,
564*4882a593Smuzhiyun 					   priv->ap_density);
565*4882a593Smuzhiyun 		if (err) {
566*4882a593Smuzhiyun 			printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
567*4882a593Smuzhiyun 			       "Disabling sensitivity control\n",
568*4882a593Smuzhiyun 			       dev->name, err);
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 			priv->has_sensitivity = 0;
571*4882a593Smuzhiyun 		}
572*4882a593Smuzhiyun 	}
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	/* Set RTS threshold */
575*4882a593Smuzhiyun 	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
576*4882a593Smuzhiyun 				   priv->rts_thresh);
577*4882a593Smuzhiyun 	if (err) {
578*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
579*4882a593Smuzhiyun 		       dev->name, err);
580*4882a593Smuzhiyun 		return err;
581*4882a593Smuzhiyun 	}
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	/* Set fragmentation threshold or MWO robustness */
584*4882a593Smuzhiyun 	if (priv->has_mwo)
585*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
586*4882a593Smuzhiyun 					   HERMES_RID_CNFMWOROBUST_AGERE,
587*4882a593Smuzhiyun 					   priv->mwo_robust);
588*4882a593Smuzhiyun 	else
589*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
590*4882a593Smuzhiyun 					   HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
591*4882a593Smuzhiyun 					   priv->frag_thresh);
592*4882a593Smuzhiyun 	if (err) {
593*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting fragmentation\n",
594*4882a593Smuzhiyun 		       dev->name, err);
595*4882a593Smuzhiyun 		return err;
596*4882a593Smuzhiyun 	}
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	/* Set bitrate */
599*4882a593Smuzhiyun 	err = __orinoco_hw_set_bitrate(priv);
600*4882a593Smuzhiyun 	if (err) {
601*4882a593Smuzhiyun 		printk(KERN_ERR "%s: Error %d setting bitrate\n",
602*4882a593Smuzhiyun 		       dev->name, err);
603*4882a593Smuzhiyun 		return err;
604*4882a593Smuzhiyun 	}
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	/* Set power management */
607*4882a593Smuzhiyun 	if (priv->has_pm) {
608*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
609*4882a593Smuzhiyun 					   HERMES_RID_CNFPMENABLED,
610*4882a593Smuzhiyun 					   priv->pm_on);
611*4882a593Smuzhiyun 		if (err) {
612*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting up PM\n",
613*4882a593Smuzhiyun 			       dev->name, err);
614*4882a593Smuzhiyun 			return err;
615*4882a593Smuzhiyun 		}
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
618*4882a593Smuzhiyun 					   HERMES_RID_CNFMULTICASTRECEIVE,
619*4882a593Smuzhiyun 					   priv->pm_mcast);
620*4882a593Smuzhiyun 		if (err) {
621*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting up PM\n",
622*4882a593Smuzhiyun 			       dev->name, err);
623*4882a593Smuzhiyun 			return err;
624*4882a593Smuzhiyun 		}
625*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
626*4882a593Smuzhiyun 					   HERMES_RID_CNFMAXSLEEPDURATION,
627*4882a593Smuzhiyun 					   priv->pm_period);
628*4882a593Smuzhiyun 		if (err) {
629*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting up PM\n",
630*4882a593Smuzhiyun 			       dev->name, err);
631*4882a593Smuzhiyun 			return err;
632*4882a593Smuzhiyun 		}
633*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
634*4882a593Smuzhiyun 					   HERMES_RID_CNFPMHOLDOVERDURATION,
635*4882a593Smuzhiyun 					   priv->pm_timeout);
636*4882a593Smuzhiyun 		if (err) {
637*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting up PM\n",
638*4882a593Smuzhiyun 			       dev->name, err);
639*4882a593Smuzhiyun 			return err;
640*4882a593Smuzhiyun 		}
641*4882a593Smuzhiyun 	}
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 	/* Set preamble - only for Symbol so far... */
644*4882a593Smuzhiyun 	if (priv->has_preamble) {
645*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
646*4882a593Smuzhiyun 					   HERMES_RID_CNFPREAMBLE_SYMBOL,
647*4882a593Smuzhiyun 					   priv->preamble);
648*4882a593Smuzhiyun 		if (err) {
649*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting preamble\n",
650*4882a593Smuzhiyun 			       dev->name, err);
651*4882a593Smuzhiyun 			return err;
652*4882a593Smuzhiyun 		}
653*4882a593Smuzhiyun 	}
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	/* Set up encryption */
656*4882a593Smuzhiyun 	if (priv->has_wep || priv->has_wpa) {
657*4882a593Smuzhiyun 		err = __orinoco_hw_setup_enc(priv);
658*4882a593Smuzhiyun 		if (err) {
659*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d activating encryption\n",
660*4882a593Smuzhiyun 			       dev->name, err);
661*4882a593Smuzhiyun 			return err;
662*4882a593Smuzhiyun 		}
663*4882a593Smuzhiyun 	}
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun 	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
666*4882a593Smuzhiyun 		/* Enable monitor mode */
667*4882a593Smuzhiyun 		dev->type = ARPHRD_IEEE80211;
668*4882a593Smuzhiyun 		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
669*4882a593Smuzhiyun 					    HERMES_TEST_MONITOR, 0, NULL);
670*4882a593Smuzhiyun 	} else {
671*4882a593Smuzhiyun 		/* Disable monitor mode */
672*4882a593Smuzhiyun 		dev->type = ARPHRD_ETHER;
673*4882a593Smuzhiyun 		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
674*4882a593Smuzhiyun 					    HERMES_TEST_STOP, 0, NULL);
675*4882a593Smuzhiyun 	}
676*4882a593Smuzhiyun 	if (err)
677*4882a593Smuzhiyun 		return err;
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 	/* Reset promiscuity / multicast*/
680*4882a593Smuzhiyun 	priv->promiscuous = 0;
681*4882a593Smuzhiyun 	priv->mc_count = 0;
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	/* Record mode change */
684*4882a593Smuzhiyun 	wdev->iftype = priv->iw_mode;
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	return 0;
687*4882a593Smuzhiyun }
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun /* Get tsc from the firmware */
orinoco_hw_get_tkip_iv(struct orinoco_private * priv,int key,u8 * tsc)690*4882a593Smuzhiyun int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc)
691*4882a593Smuzhiyun {
692*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
693*4882a593Smuzhiyun 	int err = 0;
694*4882a593Smuzhiyun 	u8 tsc_arr[4][ORINOCO_SEQ_LEN];
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun 	if ((key < 0) || (key >= 4))
697*4882a593Smuzhiyun 		return -EINVAL;
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
700*4882a593Smuzhiyun 				sizeof(tsc_arr), NULL, &tsc_arr);
701*4882a593Smuzhiyun 	if (!err)
702*4882a593Smuzhiyun 		memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	return err;
705*4882a593Smuzhiyun }
706*4882a593Smuzhiyun 
__orinoco_hw_set_bitrate(struct orinoco_private * priv)707*4882a593Smuzhiyun int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
708*4882a593Smuzhiyun {
709*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
710*4882a593Smuzhiyun 	int ratemode = priv->bitratemode;
711*4882a593Smuzhiyun 	int err = 0;
712*4882a593Smuzhiyun 
713*4882a593Smuzhiyun 	if (ratemode >= BITRATE_TABLE_SIZE) {
714*4882a593Smuzhiyun 		printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
715*4882a593Smuzhiyun 		       priv->ndev->name, ratemode);
716*4882a593Smuzhiyun 		return -EINVAL;
717*4882a593Smuzhiyun 	}
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 	switch (priv->firmware_type) {
720*4882a593Smuzhiyun 	case FIRMWARE_TYPE_AGERE:
721*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
722*4882a593Smuzhiyun 				HERMES_RID_CNFTXRATECONTROL,
723*4882a593Smuzhiyun 				bitrate_table[ratemode].agere_txratectrl);
724*4882a593Smuzhiyun 		break;
725*4882a593Smuzhiyun 	case FIRMWARE_TYPE_INTERSIL:
726*4882a593Smuzhiyun 	case FIRMWARE_TYPE_SYMBOL:
727*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
728*4882a593Smuzhiyun 				HERMES_RID_CNFTXRATECONTROL,
729*4882a593Smuzhiyun 				bitrate_table[ratemode].intersil_txratectrl);
730*4882a593Smuzhiyun 		break;
731*4882a593Smuzhiyun 	default:
732*4882a593Smuzhiyun 		BUG();
733*4882a593Smuzhiyun 	}
734*4882a593Smuzhiyun 
735*4882a593Smuzhiyun 	return err;
736*4882a593Smuzhiyun }
737*4882a593Smuzhiyun 
orinoco_hw_get_act_bitrate(struct orinoco_private * priv,int * bitrate)738*4882a593Smuzhiyun int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate)
739*4882a593Smuzhiyun {
740*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
741*4882a593Smuzhiyun 	int i;
742*4882a593Smuzhiyun 	int err = 0;
743*4882a593Smuzhiyun 	u16 val;
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 	err = hermes_read_wordrec(hw, USER_BAP,
746*4882a593Smuzhiyun 				  HERMES_RID_CURRENTTXRATE, &val);
747*4882a593Smuzhiyun 	if (err)
748*4882a593Smuzhiyun 		return err;
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 	switch (priv->firmware_type) {
751*4882a593Smuzhiyun 	case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
752*4882a593Smuzhiyun 		/* Note : in Lucent firmware, the return value of
753*4882a593Smuzhiyun 		 * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
754*4882a593Smuzhiyun 		 * and therefore is totally different from the
755*4882a593Smuzhiyun 		 * encoding of HERMES_RID_CNFTXRATECONTROL.
756*4882a593Smuzhiyun 		 * Don't forget that 6Mb/s is really 5.5Mb/s */
757*4882a593Smuzhiyun 		if (val == 6)
758*4882a593Smuzhiyun 			*bitrate = 5500000;
759*4882a593Smuzhiyun 		else
760*4882a593Smuzhiyun 			*bitrate = val * 1000000;
761*4882a593Smuzhiyun 		break;
762*4882a593Smuzhiyun 	case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
763*4882a593Smuzhiyun 	case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
764*4882a593Smuzhiyun 		for (i = 0; i < BITRATE_TABLE_SIZE; i++)
765*4882a593Smuzhiyun 			if (bitrate_table[i].intersil_txratectrl == val) {
766*4882a593Smuzhiyun 				*bitrate = bitrate_table[i].bitrate * 100000;
767*4882a593Smuzhiyun 				break;
768*4882a593Smuzhiyun 			}
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 		if (i >= BITRATE_TABLE_SIZE) {
771*4882a593Smuzhiyun 			printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
772*4882a593Smuzhiyun 			       priv->ndev->name, val);
773*4882a593Smuzhiyun 			err = -EIO;
774*4882a593Smuzhiyun 		}
775*4882a593Smuzhiyun 
776*4882a593Smuzhiyun 		break;
777*4882a593Smuzhiyun 	default:
778*4882a593Smuzhiyun 		BUG();
779*4882a593Smuzhiyun 	}
780*4882a593Smuzhiyun 
781*4882a593Smuzhiyun 	return err;
782*4882a593Smuzhiyun }
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun /* Set fixed AP address */
__orinoco_hw_set_wap(struct orinoco_private * priv)785*4882a593Smuzhiyun int __orinoco_hw_set_wap(struct orinoco_private *priv)
786*4882a593Smuzhiyun {
787*4882a593Smuzhiyun 	int roaming_flag;
788*4882a593Smuzhiyun 	int err = 0;
789*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun 	switch (priv->firmware_type) {
792*4882a593Smuzhiyun 	case FIRMWARE_TYPE_AGERE:
793*4882a593Smuzhiyun 		/* not supported */
794*4882a593Smuzhiyun 		break;
795*4882a593Smuzhiyun 	case FIRMWARE_TYPE_INTERSIL:
796*4882a593Smuzhiyun 		if (priv->bssid_fixed)
797*4882a593Smuzhiyun 			roaming_flag = 2;
798*4882a593Smuzhiyun 		else
799*4882a593Smuzhiyun 			roaming_flag = 1;
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
802*4882a593Smuzhiyun 					   HERMES_RID_CNFROAMINGMODE,
803*4882a593Smuzhiyun 					   roaming_flag);
804*4882a593Smuzhiyun 		break;
805*4882a593Smuzhiyun 	case FIRMWARE_TYPE_SYMBOL:
806*4882a593Smuzhiyun 		err = HERMES_WRITE_RECORD(hw, USER_BAP,
807*4882a593Smuzhiyun 					  HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
808*4882a593Smuzhiyun 					  &priv->desired_bssid);
809*4882a593Smuzhiyun 		break;
810*4882a593Smuzhiyun 	}
811*4882a593Smuzhiyun 	return err;
812*4882a593Smuzhiyun }
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun /* Change the WEP keys and/or the current keys.  Can be called
815*4882a593Smuzhiyun  * either from __orinoco_hw_setup_enc() or directly from
816*4882a593Smuzhiyun  * orinoco_ioctl_setiwencode().  In the later case the association
817*4882a593Smuzhiyun  * with the AP is not broken (if the firmware can handle it),
818*4882a593Smuzhiyun  * which is needed for 802.1x implementations. */
__orinoco_hw_setup_wepkeys(struct orinoco_private * priv)819*4882a593Smuzhiyun int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
820*4882a593Smuzhiyun {
821*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
822*4882a593Smuzhiyun 	int err = 0;
823*4882a593Smuzhiyun 	int i;
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	switch (priv->firmware_type) {
826*4882a593Smuzhiyun 	case FIRMWARE_TYPE_AGERE:
827*4882a593Smuzhiyun 	{
828*4882a593Smuzhiyun 		struct orinoco_key keys[ORINOCO_MAX_KEYS];
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 		memset(&keys, 0, sizeof(keys));
831*4882a593Smuzhiyun 		for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
832*4882a593Smuzhiyun 			int len = min(priv->keys[i].key_len,
833*4882a593Smuzhiyun 				      ORINOCO_MAX_KEY_SIZE);
834*4882a593Smuzhiyun 			memcpy(&keys[i].data, priv->keys[i].key, len);
835*4882a593Smuzhiyun 			if (len > SMALL_KEY_SIZE)
836*4882a593Smuzhiyun 				keys[i].len = cpu_to_le16(LARGE_KEY_SIZE);
837*4882a593Smuzhiyun 			else if (len > 0)
838*4882a593Smuzhiyun 				keys[i].len = cpu_to_le16(SMALL_KEY_SIZE);
839*4882a593Smuzhiyun 			else
840*4882a593Smuzhiyun 				keys[i].len = cpu_to_le16(0);
841*4882a593Smuzhiyun 		}
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 		err = HERMES_WRITE_RECORD(hw, USER_BAP,
844*4882a593Smuzhiyun 					  HERMES_RID_CNFWEPKEYS_AGERE,
845*4882a593Smuzhiyun 					  &keys);
846*4882a593Smuzhiyun 		if (err)
847*4882a593Smuzhiyun 			return err;
848*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
849*4882a593Smuzhiyun 					   HERMES_RID_CNFTXKEY_AGERE,
850*4882a593Smuzhiyun 					   priv->tx_key);
851*4882a593Smuzhiyun 		if (err)
852*4882a593Smuzhiyun 			return err;
853*4882a593Smuzhiyun 		break;
854*4882a593Smuzhiyun 	}
855*4882a593Smuzhiyun 	case FIRMWARE_TYPE_INTERSIL:
856*4882a593Smuzhiyun 	case FIRMWARE_TYPE_SYMBOL:
857*4882a593Smuzhiyun 		{
858*4882a593Smuzhiyun 			int keylen;
859*4882a593Smuzhiyun 
860*4882a593Smuzhiyun 			/* Force uniform key length to work around
861*4882a593Smuzhiyun 			 * firmware bugs */
862*4882a593Smuzhiyun 			keylen = priv->keys[priv->tx_key].key_len;
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun 			if (keylen > LARGE_KEY_SIZE) {
865*4882a593Smuzhiyun 				printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
866*4882a593Smuzhiyun 				       priv->ndev->name, priv->tx_key, keylen);
867*4882a593Smuzhiyun 				return -E2BIG;
868*4882a593Smuzhiyun 			} else if (keylen > SMALL_KEY_SIZE)
869*4882a593Smuzhiyun 				keylen = LARGE_KEY_SIZE;
870*4882a593Smuzhiyun 			else if (keylen > 0)
871*4882a593Smuzhiyun 				keylen = SMALL_KEY_SIZE;
872*4882a593Smuzhiyun 			else
873*4882a593Smuzhiyun 				keylen = 0;
874*4882a593Smuzhiyun 
875*4882a593Smuzhiyun 			/* Write all 4 keys */
876*4882a593Smuzhiyun 			for (i = 0; i < ORINOCO_MAX_KEYS; i++) {
877*4882a593Smuzhiyun 				u8 key[LARGE_KEY_SIZE] = { 0 };
878*4882a593Smuzhiyun 
879*4882a593Smuzhiyun 				memcpy(key, priv->keys[i].key,
880*4882a593Smuzhiyun 				       priv->keys[i].key_len);
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 				err = hw->ops->write_ltv(hw, USER_BAP,
883*4882a593Smuzhiyun 						HERMES_RID_CNFDEFAULTKEY0 + i,
884*4882a593Smuzhiyun 						HERMES_BYTES_TO_RECLEN(keylen),
885*4882a593Smuzhiyun 						key);
886*4882a593Smuzhiyun 				if (err)
887*4882a593Smuzhiyun 					return err;
888*4882a593Smuzhiyun 			}
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 			/* Write the index of the key used in transmission */
891*4882a593Smuzhiyun 			err = hermes_write_wordrec(hw, USER_BAP,
892*4882a593Smuzhiyun 						HERMES_RID_CNFWEPDEFAULTKEYID,
893*4882a593Smuzhiyun 						priv->tx_key);
894*4882a593Smuzhiyun 			if (err)
895*4882a593Smuzhiyun 				return err;
896*4882a593Smuzhiyun 		}
897*4882a593Smuzhiyun 		break;
898*4882a593Smuzhiyun 	}
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun 	return 0;
901*4882a593Smuzhiyun }
902*4882a593Smuzhiyun 
__orinoco_hw_setup_enc(struct orinoco_private * priv)903*4882a593Smuzhiyun int __orinoco_hw_setup_enc(struct orinoco_private *priv)
904*4882a593Smuzhiyun {
905*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
906*4882a593Smuzhiyun 	int err = 0;
907*4882a593Smuzhiyun 	int master_wep_flag;
908*4882a593Smuzhiyun 	int auth_flag;
909*4882a593Smuzhiyun 	int enc_flag;
910*4882a593Smuzhiyun 
911*4882a593Smuzhiyun 	/* Setup WEP keys */
912*4882a593Smuzhiyun 	if (priv->encode_alg == ORINOCO_ALG_WEP)
913*4882a593Smuzhiyun 		__orinoco_hw_setup_wepkeys(priv);
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun 	if (priv->wep_restrict)
916*4882a593Smuzhiyun 		auth_flag = HERMES_AUTH_SHARED_KEY;
917*4882a593Smuzhiyun 	else
918*4882a593Smuzhiyun 		auth_flag = HERMES_AUTH_OPEN;
919*4882a593Smuzhiyun 
920*4882a593Smuzhiyun 	if (priv->wpa_enabled)
921*4882a593Smuzhiyun 		enc_flag = 2;
922*4882a593Smuzhiyun 	else if (priv->encode_alg == ORINOCO_ALG_WEP)
923*4882a593Smuzhiyun 		enc_flag = 1;
924*4882a593Smuzhiyun 	else
925*4882a593Smuzhiyun 		enc_flag = 0;
926*4882a593Smuzhiyun 
927*4882a593Smuzhiyun 	switch (priv->firmware_type) {
928*4882a593Smuzhiyun 	case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
929*4882a593Smuzhiyun 		if (priv->encode_alg == ORINOCO_ALG_WEP) {
930*4882a593Smuzhiyun 			/* Enable the shared-key authentication. */
931*4882a593Smuzhiyun 			err = hermes_write_wordrec(hw, USER_BAP,
932*4882a593Smuzhiyun 					HERMES_RID_CNFAUTHENTICATION_AGERE,
933*4882a593Smuzhiyun 					auth_flag);
934*4882a593Smuzhiyun 		}
935*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
936*4882a593Smuzhiyun 					   HERMES_RID_CNFWEPENABLED_AGERE,
937*4882a593Smuzhiyun 					   enc_flag);
938*4882a593Smuzhiyun 		if (err)
939*4882a593Smuzhiyun 			return err;
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun 		if (priv->has_wpa) {
942*4882a593Smuzhiyun 			/* Set WPA key management */
943*4882a593Smuzhiyun 			err = hermes_write_wordrec(hw, USER_BAP,
944*4882a593Smuzhiyun 				  HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
945*4882a593Smuzhiyun 				  priv->key_mgmt);
946*4882a593Smuzhiyun 			if (err)
947*4882a593Smuzhiyun 				return err;
948*4882a593Smuzhiyun 		}
949*4882a593Smuzhiyun 
950*4882a593Smuzhiyun 		break;
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 	case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
953*4882a593Smuzhiyun 	case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
954*4882a593Smuzhiyun 		if (priv->encode_alg == ORINOCO_ALG_WEP) {
955*4882a593Smuzhiyun 			if (priv->wep_restrict ||
956*4882a593Smuzhiyun 			    (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
957*4882a593Smuzhiyun 				master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
958*4882a593Smuzhiyun 						  HERMES_WEP_EXCL_UNENCRYPTED;
959*4882a593Smuzhiyun 			else
960*4882a593Smuzhiyun 				master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
961*4882a593Smuzhiyun 
962*4882a593Smuzhiyun 			err = hermes_write_wordrec(hw, USER_BAP,
963*4882a593Smuzhiyun 						   HERMES_RID_CNFAUTHENTICATION,
964*4882a593Smuzhiyun 						   auth_flag);
965*4882a593Smuzhiyun 			if (err)
966*4882a593Smuzhiyun 				return err;
967*4882a593Smuzhiyun 		} else
968*4882a593Smuzhiyun 			master_wep_flag = 0;
969*4882a593Smuzhiyun 
970*4882a593Smuzhiyun 		if (priv->iw_mode == NL80211_IFTYPE_MONITOR)
971*4882a593Smuzhiyun 			master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun 		/* Master WEP setting : on/off */
974*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
975*4882a593Smuzhiyun 					   HERMES_RID_CNFWEPFLAGS_INTERSIL,
976*4882a593Smuzhiyun 					   master_wep_flag);
977*4882a593Smuzhiyun 		if (err)
978*4882a593Smuzhiyun 			return err;
979*4882a593Smuzhiyun 
980*4882a593Smuzhiyun 		break;
981*4882a593Smuzhiyun 	}
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	return 0;
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun /* key must be 32 bytes, including the tx and rx MIC keys.
987*4882a593Smuzhiyun  * rsc must be NULL or up to 8 bytes
988*4882a593Smuzhiyun  * tsc must be NULL or up to 8 bytes
989*4882a593Smuzhiyun  */
__orinoco_hw_set_tkip_key(struct orinoco_private * priv,int key_idx,int set_tx,const u8 * key,const u8 * rsc,size_t rsc_len,const u8 * tsc,size_t tsc_len)990*4882a593Smuzhiyun int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
991*4882a593Smuzhiyun 			      int set_tx, const u8 *key, const u8 *rsc,
992*4882a593Smuzhiyun 			      size_t rsc_len, const u8 *tsc, size_t tsc_len)
993*4882a593Smuzhiyun {
994*4882a593Smuzhiyun 	struct {
995*4882a593Smuzhiyun 		__le16 idx;
996*4882a593Smuzhiyun 		u8 rsc[ORINOCO_SEQ_LEN];
997*4882a593Smuzhiyun 		u8 key[TKIP_KEYLEN];
998*4882a593Smuzhiyun 		u8 tx_mic[MIC_KEYLEN];
999*4882a593Smuzhiyun 		u8 rx_mic[MIC_KEYLEN];
1000*4882a593Smuzhiyun 		u8 tsc[ORINOCO_SEQ_LEN];
1001*4882a593Smuzhiyun 	} __packed buf;
1002*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1003*4882a593Smuzhiyun 	int ret;
1004*4882a593Smuzhiyun 	int err;
1005*4882a593Smuzhiyun 	int k;
1006*4882a593Smuzhiyun 	u16 xmitting;
1007*4882a593Smuzhiyun 
1008*4882a593Smuzhiyun 	key_idx &= 0x3;
1009*4882a593Smuzhiyun 
1010*4882a593Smuzhiyun 	if (set_tx)
1011*4882a593Smuzhiyun 		key_idx |= 0x8000;
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 	buf.idx = cpu_to_le16(key_idx);
1014*4882a593Smuzhiyun 	memcpy(buf.key, key,
1015*4882a593Smuzhiyun 	       sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
1016*4882a593Smuzhiyun 
1017*4882a593Smuzhiyun 	if (rsc_len > sizeof(buf.rsc))
1018*4882a593Smuzhiyun 		rsc_len = sizeof(buf.rsc);
1019*4882a593Smuzhiyun 
1020*4882a593Smuzhiyun 	if (tsc_len > sizeof(buf.tsc))
1021*4882a593Smuzhiyun 		tsc_len = sizeof(buf.tsc);
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun 	memset(buf.rsc, 0, sizeof(buf.rsc));
1024*4882a593Smuzhiyun 	memset(buf.tsc, 0, sizeof(buf.tsc));
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 	if (rsc != NULL)
1027*4882a593Smuzhiyun 		memcpy(buf.rsc, rsc, rsc_len);
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 	if (tsc != NULL)
1030*4882a593Smuzhiyun 		memcpy(buf.tsc, tsc, tsc_len);
1031*4882a593Smuzhiyun 	else
1032*4882a593Smuzhiyun 		buf.tsc[4] = 0x10;
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 	/* Wait up to 100ms for tx queue to empty */
1035*4882a593Smuzhiyun 	for (k = 100; k > 0; k--) {
1036*4882a593Smuzhiyun 		udelay(1000);
1037*4882a593Smuzhiyun 		ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
1038*4882a593Smuzhiyun 					  &xmitting);
1039*4882a593Smuzhiyun 		if (ret || !xmitting)
1040*4882a593Smuzhiyun 			break;
1041*4882a593Smuzhiyun 	}
1042*4882a593Smuzhiyun 
1043*4882a593Smuzhiyun 	if (k == 0)
1044*4882a593Smuzhiyun 		ret = -ETIMEDOUT;
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun 	err = HERMES_WRITE_RECORD(hw, USER_BAP,
1047*4882a593Smuzhiyun 				  HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
1048*4882a593Smuzhiyun 				  &buf);
1049*4882a593Smuzhiyun 
1050*4882a593Smuzhiyun 	return ret ? ret : err;
1051*4882a593Smuzhiyun }
1052*4882a593Smuzhiyun 
orinoco_clear_tkip_key(struct orinoco_private * priv,int key_idx)1053*4882a593Smuzhiyun int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx)
1054*4882a593Smuzhiyun {
1055*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1056*4882a593Smuzhiyun 	int err;
1057*4882a593Smuzhiyun 
1058*4882a593Smuzhiyun 	err = hermes_write_wordrec(hw, USER_BAP,
1059*4882a593Smuzhiyun 				   HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
1060*4882a593Smuzhiyun 				   key_idx);
1061*4882a593Smuzhiyun 	if (err)
1062*4882a593Smuzhiyun 		printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
1063*4882a593Smuzhiyun 		       priv->ndev->name, err, key_idx);
1064*4882a593Smuzhiyun 	return err;
1065*4882a593Smuzhiyun }
1066*4882a593Smuzhiyun 
__orinoco_hw_set_multicast_list(struct orinoco_private * priv,struct net_device * dev,int mc_count,int promisc)1067*4882a593Smuzhiyun int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
1068*4882a593Smuzhiyun 				    struct net_device *dev,
1069*4882a593Smuzhiyun 				    int mc_count, int promisc)
1070*4882a593Smuzhiyun {
1071*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1072*4882a593Smuzhiyun 	int err = 0;
1073*4882a593Smuzhiyun 
1074*4882a593Smuzhiyun 	if (promisc != priv->promiscuous) {
1075*4882a593Smuzhiyun 		err = hermes_write_wordrec(hw, USER_BAP,
1076*4882a593Smuzhiyun 					   HERMES_RID_CNFPROMISCUOUSMODE,
1077*4882a593Smuzhiyun 					   promisc);
1078*4882a593Smuzhiyun 		if (err) {
1079*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
1080*4882a593Smuzhiyun 			       priv->ndev->name, err);
1081*4882a593Smuzhiyun 		} else
1082*4882a593Smuzhiyun 			priv->promiscuous = promisc;
1083*4882a593Smuzhiyun 	}
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 	/* If we're not in promiscuous mode, then we need to set the
1086*4882a593Smuzhiyun 	 * group address if either we want to multicast, or if we were
1087*4882a593Smuzhiyun 	 * multicasting and want to stop */
1088*4882a593Smuzhiyun 	if (!promisc && (mc_count || priv->mc_count)) {
1089*4882a593Smuzhiyun 		struct netdev_hw_addr *ha;
1090*4882a593Smuzhiyun 		struct hermes_multicast mclist;
1091*4882a593Smuzhiyun 		int i = 0;
1092*4882a593Smuzhiyun 
1093*4882a593Smuzhiyun 		netdev_for_each_mc_addr(ha, dev) {
1094*4882a593Smuzhiyun 			if (i == mc_count)
1095*4882a593Smuzhiyun 				break;
1096*4882a593Smuzhiyun 			memcpy(mclist.addr[i++], ha->addr, ETH_ALEN);
1097*4882a593Smuzhiyun 		}
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun 		err = hw->ops->write_ltv(hw, USER_BAP,
1100*4882a593Smuzhiyun 				   HERMES_RID_CNFGROUPADDRESSES,
1101*4882a593Smuzhiyun 				   HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
1102*4882a593Smuzhiyun 				   &mclist);
1103*4882a593Smuzhiyun 		if (err)
1104*4882a593Smuzhiyun 			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
1105*4882a593Smuzhiyun 			       priv->ndev->name, err);
1106*4882a593Smuzhiyun 		else
1107*4882a593Smuzhiyun 			priv->mc_count = mc_count;
1108*4882a593Smuzhiyun 	}
1109*4882a593Smuzhiyun 	return err;
1110*4882a593Smuzhiyun }
1111*4882a593Smuzhiyun 
1112*4882a593Smuzhiyun /* Return : < 0 -> error code ; >= 0 -> length */
orinoco_hw_get_essid(struct orinoco_private * priv,int * active,char buf[IW_ESSID_MAX_SIZE+1])1113*4882a593Smuzhiyun int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
1114*4882a593Smuzhiyun 			 char buf[IW_ESSID_MAX_SIZE + 1])
1115*4882a593Smuzhiyun {
1116*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1117*4882a593Smuzhiyun 	int err = 0;
1118*4882a593Smuzhiyun 	struct hermes_idstring essidbuf;
1119*4882a593Smuzhiyun 	char *p = (char *)(&essidbuf.val);
1120*4882a593Smuzhiyun 	int len;
1121*4882a593Smuzhiyun 	unsigned long flags;
1122*4882a593Smuzhiyun 
1123*4882a593Smuzhiyun 	if (orinoco_lock(priv, &flags) != 0)
1124*4882a593Smuzhiyun 		return -EBUSY;
1125*4882a593Smuzhiyun 
1126*4882a593Smuzhiyun 	if (strlen(priv->desired_essid) > 0) {
1127*4882a593Smuzhiyun 		/* We read the desired SSID from the hardware rather
1128*4882a593Smuzhiyun 		   than from priv->desired_essid, just in case the
1129*4882a593Smuzhiyun 		   firmware is allowed to change it on us. I'm not
1130*4882a593Smuzhiyun 		   sure about this */
1131*4882a593Smuzhiyun 		/* My guess is that the OWNSSID should always be whatever
1132*4882a593Smuzhiyun 		 * we set to the card, whereas CURRENT_SSID is the one that
1133*4882a593Smuzhiyun 		 * may change... - Jean II */
1134*4882a593Smuzhiyun 		u16 rid;
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun 		*active = 1;
1137*4882a593Smuzhiyun 
1138*4882a593Smuzhiyun 		rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
1139*4882a593Smuzhiyun 			HERMES_RID_CNFDESIREDSSID;
1140*4882a593Smuzhiyun 
1141*4882a593Smuzhiyun 		err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
1142*4882a593Smuzhiyun 					NULL, &essidbuf);
1143*4882a593Smuzhiyun 		if (err)
1144*4882a593Smuzhiyun 			goto fail_unlock;
1145*4882a593Smuzhiyun 	} else {
1146*4882a593Smuzhiyun 		*active = 0;
1147*4882a593Smuzhiyun 
1148*4882a593Smuzhiyun 		err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
1149*4882a593Smuzhiyun 					sizeof(essidbuf), NULL, &essidbuf);
1150*4882a593Smuzhiyun 		if (err)
1151*4882a593Smuzhiyun 			goto fail_unlock;
1152*4882a593Smuzhiyun 	}
1153*4882a593Smuzhiyun 
1154*4882a593Smuzhiyun 	len = le16_to_cpu(essidbuf.len);
1155*4882a593Smuzhiyun 	BUG_ON(len > IW_ESSID_MAX_SIZE);
1156*4882a593Smuzhiyun 
1157*4882a593Smuzhiyun 	memset(buf, 0, IW_ESSID_MAX_SIZE);
1158*4882a593Smuzhiyun 	memcpy(buf, p, len);
1159*4882a593Smuzhiyun 	err = len;
1160*4882a593Smuzhiyun 
1161*4882a593Smuzhiyun  fail_unlock:
1162*4882a593Smuzhiyun 	orinoco_unlock(priv, &flags);
1163*4882a593Smuzhiyun 
1164*4882a593Smuzhiyun 	return err;
1165*4882a593Smuzhiyun }
1166*4882a593Smuzhiyun 
orinoco_hw_get_freq(struct orinoco_private * priv)1167*4882a593Smuzhiyun int orinoco_hw_get_freq(struct orinoco_private *priv)
1168*4882a593Smuzhiyun {
1169*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1170*4882a593Smuzhiyun 	int err = 0;
1171*4882a593Smuzhiyun 	u16 channel;
1172*4882a593Smuzhiyun 	int freq = 0;
1173*4882a593Smuzhiyun 	unsigned long flags;
1174*4882a593Smuzhiyun 
1175*4882a593Smuzhiyun 	if (orinoco_lock(priv, &flags) != 0)
1176*4882a593Smuzhiyun 		return -EBUSY;
1177*4882a593Smuzhiyun 
1178*4882a593Smuzhiyun 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL,
1179*4882a593Smuzhiyun 				  &channel);
1180*4882a593Smuzhiyun 	if (err)
1181*4882a593Smuzhiyun 		goto out;
1182*4882a593Smuzhiyun 
1183*4882a593Smuzhiyun 	/* Intersil firmware 1.3.5 returns 0 when the interface is down */
1184*4882a593Smuzhiyun 	if (channel == 0) {
1185*4882a593Smuzhiyun 		err = -EBUSY;
1186*4882a593Smuzhiyun 		goto out;
1187*4882a593Smuzhiyun 	}
1188*4882a593Smuzhiyun 
1189*4882a593Smuzhiyun 	if ((channel < 1) || (channel > NUM_CHANNELS)) {
1190*4882a593Smuzhiyun 		printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
1191*4882a593Smuzhiyun 		       priv->ndev->name, channel);
1192*4882a593Smuzhiyun 		err = -EBUSY;
1193*4882a593Smuzhiyun 		goto out;
1194*4882a593Smuzhiyun 
1195*4882a593Smuzhiyun 	}
1196*4882a593Smuzhiyun 	freq = ieee80211_channel_to_frequency(channel, NL80211_BAND_2GHZ);
1197*4882a593Smuzhiyun 
1198*4882a593Smuzhiyun  out:
1199*4882a593Smuzhiyun 	orinoco_unlock(priv, &flags);
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun 	if (err > 0)
1202*4882a593Smuzhiyun 		err = -EBUSY;
1203*4882a593Smuzhiyun 	return err ? err : freq;
1204*4882a593Smuzhiyun }
1205*4882a593Smuzhiyun 
orinoco_hw_get_bitratelist(struct orinoco_private * priv,int * numrates,s32 * rates,int max)1206*4882a593Smuzhiyun int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
1207*4882a593Smuzhiyun 			       int *numrates, s32 *rates, int max)
1208*4882a593Smuzhiyun {
1209*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1210*4882a593Smuzhiyun 	struct hermes_idstring list;
1211*4882a593Smuzhiyun 	unsigned char *p = (unsigned char *)&list.val;
1212*4882a593Smuzhiyun 	int err = 0;
1213*4882a593Smuzhiyun 	int num;
1214*4882a593Smuzhiyun 	int i;
1215*4882a593Smuzhiyun 	unsigned long flags;
1216*4882a593Smuzhiyun 
1217*4882a593Smuzhiyun 	if (orinoco_lock(priv, &flags) != 0)
1218*4882a593Smuzhiyun 		return -EBUSY;
1219*4882a593Smuzhiyun 
1220*4882a593Smuzhiyun 	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
1221*4882a593Smuzhiyun 				sizeof(list), NULL, &list);
1222*4882a593Smuzhiyun 	orinoco_unlock(priv, &flags);
1223*4882a593Smuzhiyun 
1224*4882a593Smuzhiyun 	if (err)
1225*4882a593Smuzhiyun 		return err;
1226*4882a593Smuzhiyun 
1227*4882a593Smuzhiyun 	num = le16_to_cpu(list.len);
1228*4882a593Smuzhiyun 	*numrates = num;
1229*4882a593Smuzhiyun 	num = min(num, max);
1230*4882a593Smuzhiyun 
1231*4882a593Smuzhiyun 	for (i = 0; i < num; i++)
1232*4882a593Smuzhiyun 		rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
1233*4882a593Smuzhiyun 
1234*4882a593Smuzhiyun 	return 0;
1235*4882a593Smuzhiyun }
1236*4882a593Smuzhiyun 
orinoco_hw_trigger_scan(struct orinoco_private * priv,const struct cfg80211_ssid * ssid)1237*4882a593Smuzhiyun int orinoco_hw_trigger_scan(struct orinoco_private *priv,
1238*4882a593Smuzhiyun 			    const struct cfg80211_ssid *ssid)
1239*4882a593Smuzhiyun {
1240*4882a593Smuzhiyun 	struct net_device *dev = priv->ndev;
1241*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1242*4882a593Smuzhiyun 	unsigned long flags;
1243*4882a593Smuzhiyun 	int err = 0;
1244*4882a593Smuzhiyun 
1245*4882a593Smuzhiyun 	if (orinoco_lock(priv, &flags) != 0)
1246*4882a593Smuzhiyun 		return -EBUSY;
1247*4882a593Smuzhiyun 
1248*4882a593Smuzhiyun 	/* Scanning with port 0 disabled would fail */
1249*4882a593Smuzhiyun 	if (!netif_running(dev)) {
1250*4882a593Smuzhiyun 		err = -ENETDOWN;
1251*4882a593Smuzhiyun 		goto out;
1252*4882a593Smuzhiyun 	}
1253*4882a593Smuzhiyun 
1254*4882a593Smuzhiyun 	/* In monitor mode, the scan results are always empty.
1255*4882a593Smuzhiyun 	 * Probe responses are passed to the driver as received
1256*4882a593Smuzhiyun 	 * frames and could be processed in software. */
1257*4882a593Smuzhiyun 	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
1258*4882a593Smuzhiyun 		err = -EOPNOTSUPP;
1259*4882a593Smuzhiyun 		goto out;
1260*4882a593Smuzhiyun 	}
1261*4882a593Smuzhiyun 
1262*4882a593Smuzhiyun 	if (priv->has_hostscan) {
1263*4882a593Smuzhiyun 		switch (priv->firmware_type) {
1264*4882a593Smuzhiyun 		case FIRMWARE_TYPE_SYMBOL:
1265*4882a593Smuzhiyun 			err = hermes_write_wordrec(hw, USER_BAP,
1266*4882a593Smuzhiyun 						HERMES_RID_CNFHOSTSCAN_SYMBOL,
1267*4882a593Smuzhiyun 						HERMES_HOSTSCAN_SYMBOL_ONCE |
1268*4882a593Smuzhiyun 						HERMES_HOSTSCAN_SYMBOL_BCAST);
1269*4882a593Smuzhiyun 			break;
1270*4882a593Smuzhiyun 		case FIRMWARE_TYPE_INTERSIL: {
1271*4882a593Smuzhiyun 			__le16 req[3];
1272*4882a593Smuzhiyun 
1273*4882a593Smuzhiyun 			req[0] = cpu_to_le16(0x3fff);	/* All channels */
1274*4882a593Smuzhiyun 			req[1] = cpu_to_le16(0x0001);	/* rate 1 Mbps */
1275*4882a593Smuzhiyun 			req[2] = 0;			/* Any ESSID */
1276*4882a593Smuzhiyun 			err = HERMES_WRITE_RECORD(hw, USER_BAP,
1277*4882a593Smuzhiyun 						  HERMES_RID_CNFHOSTSCAN, &req);
1278*4882a593Smuzhiyun 			break;
1279*4882a593Smuzhiyun 		}
1280*4882a593Smuzhiyun 		case FIRMWARE_TYPE_AGERE:
1281*4882a593Smuzhiyun 			if (ssid->ssid_len > 0) {
1282*4882a593Smuzhiyun 				struct hermes_idstring idbuf;
1283*4882a593Smuzhiyun 				size_t len = ssid->ssid_len;
1284*4882a593Smuzhiyun 
1285*4882a593Smuzhiyun 				idbuf.len = cpu_to_le16(len);
1286*4882a593Smuzhiyun 				memcpy(idbuf.val, ssid->ssid, len);
1287*4882a593Smuzhiyun 
1288*4882a593Smuzhiyun 				err = hw->ops->write_ltv(hw, USER_BAP,
1289*4882a593Smuzhiyun 					       HERMES_RID_CNFSCANSSID_AGERE,
1290*4882a593Smuzhiyun 					       HERMES_BYTES_TO_RECLEN(len + 2),
1291*4882a593Smuzhiyun 					       &idbuf);
1292*4882a593Smuzhiyun 			} else
1293*4882a593Smuzhiyun 				err = hermes_write_wordrec(hw, USER_BAP,
1294*4882a593Smuzhiyun 						   HERMES_RID_CNFSCANSSID_AGERE,
1295*4882a593Smuzhiyun 						   0);	/* Any ESSID */
1296*4882a593Smuzhiyun 			if (err)
1297*4882a593Smuzhiyun 				break;
1298*4882a593Smuzhiyun 
1299*4882a593Smuzhiyun 			if (priv->has_ext_scan) {
1300*4882a593Smuzhiyun 				err = hermes_write_wordrec(hw, USER_BAP,
1301*4882a593Smuzhiyun 						HERMES_RID_CNFSCANCHANNELS2GHZ,
1302*4882a593Smuzhiyun 						0x7FFF);
1303*4882a593Smuzhiyun 				if (err)
1304*4882a593Smuzhiyun 					goto out;
1305*4882a593Smuzhiyun 
1306*4882a593Smuzhiyun 				err = hermes_inquire(hw,
1307*4882a593Smuzhiyun 						     HERMES_INQ_CHANNELINFO);
1308*4882a593Smuzhiyun 			} else
1309*4882a593Smuzhiyun 				err = hermes_inquire(hw, HERMES_INQ_SCAN);
1310*4882a593Smuzhiyun 
1311*4882a593Smuzhiyun 			break;
1312*4882a593Smuzhiyun 		}
1313*4882a593Smuzhiyun 	} else
1314*4882a593Smuzhiyun 		err = hermes_inquire(hw, HERMES_INQ_SCAN);
1315*4882a593Smuzhiyun 
1316*4882a593Smuzhiyun  out:
1317*4882a593Smuzhiyun 	orinoco_unlock(priv, &flags);
1318*4882a593Smuzhiyun 
1319*4882a593Smuzhiyun 	return err;
1320*4882a593Smuzhiyun }
1321*4882a593Smuzhiyun 
1322*4882a593Smuzhiyun /* Disassociate from node with BSSID addr */
orinoco_hw_disassociate(struct orinoco_private * priv,u8 * addr,u16 reason_code)1323*4882a593Smuzhiyun int orinoco_hw_disassociate(struct orinoco_private *priv,
1324*4882a593Smuzhiyun 			    u8 *addr, u16 reason_code)
1325*4882a593Smuzhiyun {
1326*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1327*4882a593Smuzhiyun 	int err;
1328*4882a593Smuzhiyun 
1329*4882a593Smuzhiyun 	struct {
1330*4882a593Smuzhiyun 		u8 addr[ETH_ALEN];
1331*4882a593Smuzhiyun 		__le16 reason_code;
1332*4882a593Smuzhiyun 	} __packed buf;
1333*4882a593Smuzhiyun 
1334*4882a593Smuzhiyun 	/* Currently only supported by WPA enabled Agere fw */
1335*4882a593Smuzhiyun 	if (!priv->has_wpa)
1336*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1337*4882a593Smuzhiyun 
1338*4882a593Smuzhiyun 	memcpy(buf.addr, addr, ETH_ALEN);
1339*4882a593Smuzhiyun 	buf.reason_code = cpu_to_le16(reason_code);
1340*4882a593Smuzhiyun 	err = HERMES_WRITE_RECORD(hw, USER_BAP,
1341*4882a593Smuzhiyun 				  HERMES_RID_CNFDISASSOCIATE,
1342*4882a593Smuzhiyun 				  &buf);
1343*4882a593Smuzhiyun 	return err;
1344*4882a593Smuzhiyun }
1345*4882a593Smuzhiyun 
orinoco_hw_get_current_bssid(struct orinoco_private * priv,u8 * addr)1346*4882a593Smuzhiyun int orinoco_hw_get_current_bssid(struct orinoco_private *priv,
1347*4882a593Smuzhiyun 				 u8 *addr)
1348*4882a593Smuzhiyun {
1349*4882a593Smuzhiyun 	struct hermes *hw = &priv->hw;
1350*4882a593Smuzhiyun 	int err;
1351*4882a593Smuzhiyun 
1352*4882a593Smuzhiyun 	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
1353*4882a593Smuzhiyun 				ETH_ALEN, NULL, addr);
1354*4882a593Smuzhiyun 
1355*4882a593Smuzhiyun 	return err;
1356*4882a593Smuzhiyun }
1357