--- /sys/src/9/pc/etherrt2860.c
+++ /sys/src/9/pc/etherrt2860.c
@@ -1,9 +1,9 @@
 /* 
  * Ralink RT2860 driver
  *
- * Written without any documentation but Damien Bergaminis
- * OpenBSD ral(4) driver sources. Requires ralink firmware
- * to be present in /lib/firmware/ral-rt2860 on attach.
+ * Written without any documentation but Damien Bergamini's
+ * OpenBSD ral(4) driver sources. Requires Ralink firmware
+ * to be present in /lib/firmware/ral-rt* on attach.
  */
 
 #include "u.h"
@@ -235,6 +235,24 @@
 	Rt3090OscCtrl = 0x05a4,
 	Rt3070LdoCfg0 = 0x05d4,
 	Rt3070GpioSwitch = 0x05dc,
+
+	/* RT3290 registers */
+	Rt3290CmbCtrl = 0x0020,
+		Rt3290XtalRdy = (1 << 22),
+		Rt3290PllLd = (1 << 23),
+	Rt3290EfuseCtrl = 0x0024,
+	Rt3290EfuseData0 = 0x0034,
+	Rt3290EfuseData1 = 0x0030,
+	Rt3290EfuseData2 = 0x002c,
+	Rt3290EfuseData3 = 0x0028,
+	Rt3290WlanCtrl = 0x0080,
+		Rt3290WlanEn = (1 << 0),
+		Rt3290WlanClkEn = (1 << 1),
+		Rt3290WlanReset = (1 << 3),
+		Rt3290PcieApp0ClkReq = (1 << 4),
+		Rt3290FrcWlAntSet = (1 << 5),
+		Rt3290RadioEn = (1 << 8),
+		Rt3290GpioOutOeAll = (0xff << 24),
 };
 
 enum {
@@ -767,21 +785,23 @@
 };
 
 enum {
-	Rf2820 = 1 /* 2T3R */,
-	Rf2850 = 2 /* dual-band 2T3R */,
-	Rf2720 = 3 /* 1T2R */,
-	Rf2750 = 4 /* dual-band 1T2R */,
-	Rf3020 = 5 /* 1T1R */,
-	Rf2020 = 6 /* b/g */,
-	Rf3021 = 7 /* 1T2R */,
-	Rf3022 = 8 /* 2T2R */,
- 	Rf3052 = 9 /* dual-band 2T2R */,
-	Rf3320 = 11 /* 1T1R */,
-	Rf3053 = 13 /* dual-band 3T3R */,
+	Rf2820 = 0x0001, /* 2T3R */
+	Rf2850 = 0x0002, /* dual-band 2T3R */
+	Rf2720 = 0x0003, /* 1T2R */
+	Rf2750 = 0x0004, /* dual-band 1T2R */
+	Rf3020 = 0x0005, /* 1T1R */
+	Rf2020 = 0x0006, /* b/g */
+	Rf3021 = 0x0007, /* 1T2R */
+	Rf3022 = 0x0008, /* 2T2R */
+ 	Rf3052 = 0x0009, /* dual-band 2T2R */
+	Rf3320 = 0x000b, /* 1T1R */
+	Rf3053 = 0x000d, /* dual-band 3T3R */
+	Rf3290 = 0x3290, /* 1T1R */
 };
 
 enum {	
 	Rt3070RfBlock = (1 << 0),
+	Rt3070PllPd = (1 << 1),
 	Rt3070Rx0Pd = (1 << 2),
 	Rt3070Tx0Pd = (1 << 3),
 	Rt3070Rx1Pd = (1 << 4),
@@ -804,10 +824,16 @@
 	Rt3593LdoRfVcMask = 0xe0,
 	Rt3593CpIcMask = 0xe0,
 	Rt3593CpIcShift = 5,
-	Rt3593RxCtb = (1 << 5)
+	Rt3593RxCtb = (1 << 5),
+
+	Rt5390RxLo1 = (1 << 5),
+	Rt5390RxLo2 = (1 << 7),
+	Rt5390RxCtb = (1 << 6),
+
+	Rt3290RssiGain = 0xc0,
 };
 
-static const char* rfnames[] = {
+static char* rfnames[] = {
 	[Rf2820] "RT2820",
 	[Rf2850] "RT2850",
 	[Rf2720] "RT2720",
@@ -819,9 +845,27 @@
 	[Rf3052] "RT3052",
 	[Rf3320] "RT3320",
 	[Rf3053] "RT3053",
+	[Rf3290] "RT3290",
 };
 
 enum {
+	/* BBP register values */
+
+	/* BBP3 - RX Antenna */
+	Rt3290AdcModeSwitch = (1 << 6),
+	Rt3290AdcInitMode = (1 << 7),
+
+	/* BBP4 - Bandwidth */
+	Rt5390MacIfCtrl = (1 << 6),
+
+	/* BBP47 - Bandwith */
+	Rt3290TssiAdc6 = (1 << 7),
+	
+	/* BBP152 - RX Antenna */
+	Rt5390DefRxAnt = (1 << 7),
+};
+
+enum {
 	/* USB commands, RT2870 only */
 	Rt2870Reset = 1,
 	Rt2870Write2 = 2,
@@ -833,6 +877,7 @@
 enum {
 	EepromDelay = 1 /* minimum hold time (microsecond) */,
 
+	EepromChipId = 0x00,
 	EepromVersion = 0x01,
 	EepromMac01 = 0x02,
 	EepromMac23 = 0x03,
@@ -932,7 +977,7 @@
 
 	u16int mac_ver;
 	u16int mac_rev;
-	u8int rf_rev;
+	u16int rf_rev;
 	u8int freq;
 	u8int ntxchains;
 	u8int nrxchains;
@@ -999,32 +1044,50 @@
 	ConnPciE   = 1 << 1,
 };
 
-static const struct rt2860_rate {
+static struct rt2860_rate {
 	u8int		rate;
 	u8int		mcs;
-	/*enum		ieee80211_phytype phy;*/
 	u8int		ctl_ridx;
 	u16int		sp_ack_dur;
 	u16int		lp_ack_dur;
+	int		ofdm;
 } rt2860_rates[] = {
-	{   2, 0,/* IEEE80211_T_DS,*/   0, 314, 314 },
-	{   4, 1,/* IEEE80211_T_DS,*/   1, 258, 162 },
-	{  11, 2,/* IEEE80211_T_DS,*/   2, 223, 127 },
-	{  22, 3,/* IEEE80211_T_DS,*/   3, 213, 117 },
-	{  12, 0,/* IEEE80211_T_OFDM,*/ 4,  60,  60 },
-	{  18, 1,/* IEEE80211_T_OFDM,*/ 4,  52,  52 },
-	{  24, 2,/* IEEE80211_T_OFDM,*/ 6,  48,  48 },
-	{  36, 3,/* IEEE80211_T_OFDM,*/ 6,  44,  44 },
-	{  48, 4,/* IEEE80211_T_OFDM,*/ 8,  44,  44 },
-	{  72, 5,/* IEEE80211_T_OFDM,*/ 8,  40,  40 },
-	{  96, 6,/* IEEE80211_T_OFDM,*/ 8,  40,  40 },
-	{ 108, 7,/* IEEE80211_T_OFDM,*/ 8,  40,  40 }
+	{   2, 0, 0, 314, 314, 0 },
+	{   4, 1, 1, 258, 162, 0 },
+	{  11, 2, 2, 223, 127, 0 },
+	{  22, 3, 3, 213, 117, 0 },
+	{  12, 0, 4,  60,  60, 1 },
+	{  18, 1, 4,  52,  52, 1 },
+	{  24, 2, 6,  48,  48, 1 },
+	{  36, 3, 6,  44,  44, 1 },
+	{  48, 4, 8,  44,  44, 1 },
+	{  72, 5, 8,  40,  40, 1 },
+	{  96, 6, 8,  40,  40, 1 },
+	{ 108, 7, 8,  40,  40, 1 }
 };
 
+/* for use with the wifi stack, *must* be in order with rt2860_rates */
+static u8int rates[] = {
+	0x80 | 2,
+	0x80 | 4,
+	0x80 | 11,
+	0x80 | 22,
+
+	0x80 | 12,
+	0x80 | 18,
+	0x80 | 24,
+	0x80 | 36,
+	0x80 | 48,
+	0x80 | 72,
+	0x80 | 96,
+	0x80 | 108,
+	0
+};
+
 /*
  * Default values for MAC registers; values taken from the reference driver.
  */
-static const struct {
+static struct {
 	u32int	reg;
 	u32int	val;
 } rt2860_def_mac[] = {
@@ -1061,7 +1124,7 @@
 /*
  * Default values for BBP registers; values taken from the reference driver.
  */
-static const struct {
+static struct {
 	u8int	reg;
 	u8int	val;
 } rt2860_def_bbp[] = {
@@ -1080,12 +1143,48 @@
 	{ 103, 0x00 },	
 	{ 105, 0x05 },	
 	{ 106, 0x35 },
+}, rt3290_def_bbp[] = {
+	{  31, 0x08 },
+	{  65, 0x2c },
+	{  66, 0x38 },
+	{  68, 0x0b },
+	{  69, 0x12 },
+	{  73, 0x13 },
+	{  75, 0x46 },
+	{  76, 0x28 },
+	{  77, 0x58 },
+	{  70, 0x0a },
+	{  74, 0x0b },
+	{  79, 0x18 },
+	{  80, 0x09 },
+	{  81, 0x33 },
+	{  82, 0x62 },
+	{  83, 0x7a },
+	{  84, 0x9a },
+	{  86, 0x38 },
+	{  91, 0x04 },
+	{  92, 0x02 },
+	{ 104, 0x92 },
+	{ 105, 0x1c },
+	{ 106, 0x03 },
+	{ 128, 0x12 },
+
+	{  67, 0x24 },
+	{ 143, 0x04 },
+	{ 142, 0x99 },
+	{ 150, 0x30 },
+	{ 151, 0x2e },
+	{ 152, 0x20 },
+	{ 153, 0x34 },
+	{ 154, 0x40 },
+	{ 155, 0x3b },
+	{ 253, 0x04 }
 };
 
 /*
  * Default settings for RF registers; values derived from the reference driver.
  */
-static const struct rfprog {
+static struct rfprog {
 	u8int		chan;
 	u32int		r1, r2, r3, r4;
 } rt2860_rf2850[] = {
@@ -1204,7 +1303,7 @@
 	{ 0x61, 0,  9 }
 };
 
-static const struct {
+static struct {
 	u8int reg;
 	u8int val;
 } rt3090_def_rf[] = {
@@ -1227,6 +1326,53 @@
 	{ 24, 0x16 },
 	{ 25, 0x01 },
 	{ 29, 0x1f }
+}, rt3290_def_rf[] = {
+	{  1, 0x0f },
+	{  2, 0x80 },
+	{  3, 0x08 },
+	{  4, 0x00 },
+	{  6, 0xa0 },
+	{  8, 0xf3 },
+	{  9, 0x02 },
+	{ 10, 0x53 },
+	{ 11, 0x4a },
+	{ 12, 0x46 },
+	{ 13, 0x9f },
+	{ 18, 0x02 },
+	{ 22, 0x20 },
+	{ 25, 0x83 },
+	{ 26, 0x82 },
+	{ 27, 0x09 },
+	{ 29, 0x10 },
+	{ 30, 0x10 },
+	{ 31, 0x80 },
+	{ 32, 0x80 },
+	{ 33, 0x00 },
+	{ 34, 0x05 },
+	{ 35, 0x12 },
+	{ 36, 0x00 },
+	{ 38, 0x85 },
+	{ 39, 0x1b },
+	{ 40, 0x0b },
+	{ 41, 0xbb },
+	{ 42, 0xd5 },
+	{ 43, 0x7b },
+	{ 44, 0x0e },
+	{ 45, 0xa2 },
+	{ 46, 0x73 },
+	{ 47, 0x00 },
+	{ 48, 0x10 },
+	{ 49, 0x98 },
+	{ 52, 0x38 },
+	{ 53, 0x00 },
+	{ 54, 0x78 },
+	{ 55, 0x43 },
+	{ 56, 0x02 },
+	{ 57, 0x80 },
+	{ 58, 0x7f },
+	{ 59, 0x09 },
+	{ 60, 0x45 },
+	{ 61, 0xc1 }
 };
 
 /* vendors */
@@ -1239,6 +1385,7 @@
 	RalinkRT2890 = 0x0681,
 	RalinkRT2790 = 0x0781,
 	RalinkRT3090 = 0x3090,
+	RalinkRT3290 = 0x3290,
 	AwtRT2890 = 0x1059,
 };
 
@@ -1248,6 +1395,7 @@
 static int rbplant(Ctlr*, int);
 static void setchan(Ctlr*, uint);
 static void rt3090setchan(Ctlr*, uint);
+static void rt3290setchan(Ctlr*, uint);
 static void selchangroup(Ctlr*, int);
 static void setleds(Ctlr*, u16int);
 
@@ -1346,7 +1494,9 @@
 			edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, ctlr->wcid);
 
 	/* Set channel */
-	if(ctlr->mac_ver >= 0x3071)
+	if(ctlr->mac_ver == 0x3290)
+		rt3290setchan(ctlr, ctlr->channel);
+	else if(ctlr->mac_ver >= 0x3071)
 		rt3090setchan(ctlr, ctlr->channel);
 	else
 		setchan(ctlr, ctlr->channel);
@@ -1353,6 +1503,15 @@
 	selchangroup(ctlr, 0);
 	microdelay(1000);
 
+	if(ctlr->prom)
+		return;
+
+	/* update slot */
+	tmp = csr32r(ctlr, BkoffSlotCfg);
+	tmp &= ~0xff;
+	tmp |= (cap & (1<<10)) ? 9 : 20;
+	csr32w(ctlr, BkoffSlotCfg, tmp);
+
 	/* enable mrr(?) */
 #define CCK(mcs)	(mcs)
 #define OFDM(mcs)	(1 << 3 | (mcs))
@@ -1373,20 +1532,16 @@
 	    CCK(0));		/*   1-> 1 */
 #undef OFDM
 #undef CCK
-	/* update slot */
-	tmp = csr32r(ctlr, BkoffSlotCfg);
-	tmp &= ~0xff;
-	tmp |= (cap & (1<<10)) ? 9 : 20;
-	csr32w(ctlr, BkoffSlotCfg, tmp);
 	
 	/* set TX preamble */
 	tmp = csr32r(ctlr, AutoRspCfg);
 	tmp &= ~CckShortEn;
-	if(cap & (1<<5)) tmp |= CckShortEn;	
+	if(cap & (1<<5))
+		tmp |= CckShortEn;	
 	csr32w(ctlr, AutoRspCfg, tmp);
 
 	/* set basic rates */
-	csr32w(ctlr, LegacyBasicRate, 0x003); /* 11B */
+	csr32w(ctlr, LegacyBasicRate, 0x15f); /* 11G */
 
 	/* Set BSSID */
 	csr32w(ctlr, MacBssidDw0,
@@ -1425,9 +1580,10 @@
 {
 }
 
-static FWImage*
-readfirmware(void){
-	static char name[] = "ral-rt2860";
+static void
+readfirmware(Ctlr *ctlr)
+{
+	char *name;
 	uchar dirbuf[sizeof(Dir)+100], *data;
 	char buf[128];
 	FWImage *fw;
@@ -1437,6 +1593,10 @@
 
 	if(!iseve())
 		error(Eperm);
+	if(ctlr->mac_ver == 0x3290)
+		name = "ral-rt3290";
+	else
+		name = "ral-rt2860";
 	if(!waserror()){
 		snprint(buf, sizeof buf, "/boot/%s", name);
 		c = namec(buf, Aopen, OREAD, 0);
@@ -1470,7 +1630,7 @@
 	poperror();
 	poperror();
 	cclose(c);
-	return fw;
+	ctlr->fw = fw;
 }
 
 static char*
@@ -1635,11 +1795,26 @@
 	}
 
 	/* initialize BBP registers to default values */
-	for(i = 0; i < nelem(rt2860_def_bbp); i++){
-		bbpwrite(ctlr, rt2860_def_bbp[i].reg,
-		    rt2860_def_bbp[i].val);
-	}
+	if(ctlr->mac_ver == 0x3290){
+		/* Avoid data lost and CRC error. */
+		bbpwrite(ctlr, 4, bbpread(ctlr, 4) | Rt5390MacIfCtrl);
+		
+		for(i = 0; i < nelem(rt3290_def_bbp); i++)
+			bbpwrite(ctlr, rt3290_def_bbp[i].reg,
+			    rt3290_def_bbp[i].val);
 
+		bbpwrite(ctlr, 47, bbpread(ctlr, 47) | Rt3290TssiAdc6);
+		/* use 5-bit ADC for acquisition and 8-bit ADC for data */
+		bbpwrite(ctlr, 3, bbpread(ctlr, 3) | Rt3290AdcModeSwitch | Rt3290AdcInitMode);
+		/* switch to default rx antenna */
+		bbpwrite(ctlr, 152, bbpread(ctlr, 152) | Rt5390DefRxAnt);
+
+		return nil;
+	}else
+		for(i = 0; i < nelem(rt2860_def_bbp); i++)
+			bbpwrite(ctlr, rt2860_def_bbp[i].reg,
+				rt2860_def_bbp[i].val);
+
 	/* fix BBP84 for RT2860E */
 	if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev != 0x0101)
 		bbpwrite(ctlr, 84, 0x19);
@@ -1872,7 +2047,7 @@
 static void
 setchan(Ctlr *ctlr, uint chan)
 {
-	const struct rfprog *rfprog = rt2860_rf2850;
+	struct rfprog *rfprog = rt2860_rf2850;
 	u32int r2, r3, r4;
 	s8int txpow1, txpow2;
 	uint i;
@@ -1988,6 +2163,82 @@
 	rt3090rfwrite(ctlr, 7, rf | Rt3070Tune);
 }
 
+/* Altered version of rt5390_set_chan in OpenBSD. */
+static void
+rt3290setchan(Ctlr *ctlr, uint chan)
+{
+	u8int h20mhz, rf, tmp;
+	s8int txpow1;
+	int i;
+
+	/* RT3290 is 2GHz only */
+	assert(chan >= 1 && chan <= 14);
+
+	/* find the settings for this channel (we know it exists) */
+	for(i = 0; rt2860_rf2850[i].chan != chan; i++);
+
+	/* use Tx power values from EEPROM */
+	txpow1 = ctlr->txpow1[i];
+
+	rt3090rfwrite(ctlr, 8, rt3090_freqs[i].n);
+	rt3090rfwrite(ctlr, 9, rt3090_freqs[i].k & 0x0f);
+	rf = rt3090rfread(ctlr, 11);
+	rf = (rf & ~0x03) | (rt3090_freqs[i].r & 0x03);
+	rt3090rfwrite(ctlr, 11, rf);
+
+	rf = rt3090rfread(ctlr, 49);
+	rf = (rf & ~0x3f) | (txpow1 & 0x3f);
+	/* the valid range of the RF R49 is 0x00~0x27 */
+	if ((rf & 0x3f) > 0x27)
+		rf = (rf & ~0x3f) | 0x27;
+	rt3090rfwrite(ctlr, 49, rf);
+
+	rf = rt3090rfread(ctlr, 1);
+	rf |= Rt3070RfBlock | Rt3070PllPd | Rt3070Rx0Pd | Rt3070Tx0Pd;
+	rt3090rfwrite(ctlr, 1, rf);
+
+	rf = rt3090rfread(ctlr, 2);
+	rt3090rfwrite(ctlr, 2, rf | Rt3593Rescal);
+	microdelay(1000);
+	rt3090rfwrite(ctlr, 2, rf & ~Rt3593Rescal);
+
+	rf = rt3090rfread(ctlr, 17);
+	tmp = rf;
+	rf = (rf & ~0x7f) | (ctlr->freq & 0x7f);
+	rf = MIN(rf, 0x5f);
+	if (tmp != rf)
+		mcucmd(ctlr, 0x74, (tmp << 8 ) | rf, 0);
+
+	if (chan == 6)
+		bbpwrite(ctlr, 68, 0x0c);
+	else
+		bbpwrite(ctlr, 68, 0x0b);
+
+	if (chan >= 1 && chan < 6)
+		rf = 0x0f;
+	else if (chan >= 7 && chan <= 11)
+			rf = 0x0e;
+	else if (chan >= 12 && chan <= 14)
+			rf = 0x0d;
+	rt3090rfwrite(ctlr, 59, rf);
+
+	/* Tx/Rx h20M */
+	h20mhz = (ctlr->rf24_20mhz & 0x20) >> 5;
+	rf = rt3090rfread(ctlr, 30);
+	rf = (rf & ~0x06) | (h20mhz << 1) | (h20mhz << 2);
+	rt3090rfwrite(ctlr, 30, rf);
+
+	/* Rx BB filter VCM */
+	rf = rt3090rfread(ctlr, 30);
+	rf = (rf & ~0x18) | 0x10;
+	rt3090rfwrite(ctlr, 30, rf);
+
+	/* Initiate VCO calibration. */
+	rf = rt3090rfread(ctlr, 3);
+	rf |= Rt3593Vcocal;
+	rt3090rfwrite(ctlr, 3, rf);
+}
+
 static int
 rt3090filtercalib(Ctlr *ctlr, u8int init, u8int target, u8int *val)
 {
@@ -2070,7 +2321,7 @@
 	}
 }
 
-static int
+static void
 rt3090rfinit(Ctlr *ctlr)
 {
 	u32int tmp;
@@ -2174,8 +2425,41 @@
 
 	rf = rt3090rfread(ctlr, 21);
 	rt3090rfwrite(ctlr, 21, rf & ~Rt3070RxLo2);
+}
 
-	return 0;
+/*
+ * rt5390_rf_init was full of stuff that wasn't for rt3290,
+ * this is a function from scratch.
+ */
+static void
+rt3290rfinit(Ctlr *ctlr)
+{
+	u8int rf;
+	int i;
+
+	rf = rt3090rfread(ctlr, 2);
+	/* Toggle RF R2 bit 7. */
+	rt3090rfwrite(ctlr, 2, rf | Rt3593Rescal);
+	microdelay(1000);
+	rt3090rfwrite(ctlr, 2, rf & ~Rt3593Rescal);
+
+	/* Initialize RF registers to default value. */
+	for(i = 0; i < nelem(rt3290_def_rf); i++)
+		rt3090rfwrite(ctlr, rt3290_def_rf[i].reg,
+			rt3290_def_rf[i].val);
+
+	rt3090rfwrite(ctlr, 29, rt3090rfread(ctlr, 29) | Rt3290RssiGain);
+	
+	ctlr->rf24_20mhz = 0x1f;
+	ctlr->rf24_40mhz = 0x2f;
+
+	csr32w(ctlr, Rt3070Opt14, csr32r(ctlr, Rt3070Opt14) | 1);
+
+	rf = rt3090rfread(ctlr, 17);
+	rf &= ~Rt3070TxLo1;
+	if(ctlr->txmixgain_2ghz >= 2)
+		rf = (rf & ~0x7) | ctlr->txmixgain_2ghz;
+	rt3090rfwrite(ctlr, 17, rf);
 }
 
 static void
@@ -2244,7 +2528,32 @@
 	}
 }
 
+/* Altered version of rt5390_rf_wakeup in OpenBSD. */
 static void
+rt3290rfwakeup(Ctlr *ctlr)
+{
+	u8int rf;
+	
+	rf = rt3090rfread(ctlr, 1);
+	rf |= Rt3070RfBlock | Rt3070PllPd | Rt3070Rx0Pd | Rt3070Tx0Pd;
+	rt3090rfwrite(ctlr, 1, rf);
+
+	rf = rt3090rfread(ctlr, 6);
+	rf |= Rt3593VcoIc | Rt3593Vcocal;
+	rt3090rfwrite(ctlr, 6, rf);
+
+	rt3090rfwrite(ctlr, 2, rt3090rfread(ctlr, 2) | Rt3593Rescal);
+
+	rf = rt3090rfread(ctlr, 22);
+	rf = (rf & ~0xe0) | 0x20;
+	rt3090rfwrite(ctlr, 22, rf);
+
+	rt3090rfwrite(ctlr, 42, rt3090rfread(ctlr, 42) | Rt5390RxCtb);
+	rt3090rfwrite(ctlr, 20, rt3090rfread(ctlr, 20) & ~0x77);
+	rt3090rfwrite(ctlr, 3, rt3090rfread(ctlr, 3) | Rt3593Vcocal);
+}
+
+static void
 rt3090rfsetup(Ctlr *ctlr)
 {
 	u8int bbp;
@@ -2294,9 +2603,48 @@
 		else if(ic->ic_protmode == IEEE80211_PROT_CTSONLY)
 			tmp |= ProtCtrlCts;
 	}
-	csr32w(ctlr, OfdmProtCfg, tmp); */
+*/
+	csr32w(ctlr, OfdmProtCfg, tmp);
 }
 
+static char *
+rt3290wlanenable(Ctlr *ctlr)
+{
+	u32int tmp;
+	int ntries;
+
+	/* enable chip and check readiness */
+	tmp = csr32r(ctlr, Rt3290WlanCtrl);
+	tmp |= Rt3290WlanEn | Rt3290FrcWlAntSet |
+	    Rt3290GpioOutOeAll;
+	csr32w(ctlr, Rt3290WlanCtrl, tmp);
+
+	for (ntries = 0; ntries < 200; ntries++) {
+		tmp = csr32r(ctlr, Rt3290CmbCtrl);
+		if ((tmp & Rt3290PllLd) &&
+		    (tmp & Rt3290XtalRdy))
+			break;
+		microdelay(20);
+	}
+	if (ntries == 200)
+		return "timeout while enabling wlan";
+
+	/* toggle reset */
+	tmp = csr32r(ctlr, Rt3290WlanCtrl);
+	tmp |= Rt3290WlanReset | Rt3290WlanClkEn;
+	tmp &= ~Rt3290PcieApp0ClkReq;
+	csr32w(ctlr, Rt3290WlanCtrl, tmp);
+	microdelay(20);
+	tmp &= ~Rt3290WlanReset;
+	csr32w(ctlr, Rt3290WlanCtrl, tmp);
+	microdelay(1000);
+
+	/* clear garbage interrupts */
+	csr32w(ctlr, IntStatus, 0x7fffffff);
+
+	return nil;
+}
+
 static char*
 rt2860start(Ether *edev)
 {
@@ -2307,6 +2655,26 @@
 	Ctlr *ctlr;
 
 	ctlr = edev->ctlr;
+
+	if(ctlr->rfswitch)
+		if(ctlr->mac_ver == 0x3290){
+			/* hardware has a radio switch on GPIO pin 0 */
+			if(!(csr32r(ctlr, Rt3290WlanCtrl) & Rt3290RadioEn))
+				/*XXX: rt2860stop(ifp, 1);*/
+				return "radio disabled";
+		}else
+			/* hardware has a radio switch on GPIO pin 2 */
+			if(!(csr32r(ctlr, GpioCtrl) & 1 << 2))
+				/*XXX: rt2860stop(ifp, 1);*/
+				return "radio disabled";
+
+	if(ctlr->mac_ver == 0x3290){
+		err = rt3290wlanenable(ctlr);
+		if(err != nil)
+			/*XXX: rt2860stop(ifp, 1);*/
+			return err;
+	}
+
 	csr32w(ctlr, PwrPinCfg, IoRaPe);
 
 	/* disable DMA */
@@ -2366,7 +2734,9 @@
 
 	for(i = 0; i < nelem(rt2860_def_mac); i++)
 		csr32w(ctlr, rt2860_def_mac[i].reg, rt2860_def_mac[i].val);
-	if(ctlr->mac_ver >= 0x3071){
+	if(ctlr->mac_ver == 0x3290)
+		csr32w(ctlr, TxSwCfg0, 0x000000404);
+	else if(ctlr->mac_ver >= 0x3071){
 		/* set delay of PA_PE assertion to 1us (unit of 0.25us) */
 		csr32w(ctlr, TxSwCfg0,
 		    4 << DlyPapeEnShift);
@@ -2451,7 +2821,7 @@
 	csr32w(ctlr, DelayIntCfg, 0);
 
 	/* write vendor-specific BBP values (from EEPROM) */
-	for(i = 0; i < 8; i++){
+	for(i = 0; i < nelem(ctlr->bbp); i++){
 		if(ctlr->bbp[i].reg == 0 || ctlr->bbp[i].reg == 0xff)
 			continue;
 		bbpwrite(ctlr, ctlr->bbp[i].reg, ctlr->bbp[i].val);
@@ -2460,6 +2830,7 @@
 	/* select Main antenna for 1T1R devices */
 	if(ctlr->rf_rev == Rf2020 ||
 	    ctlr->rf_rev == Rf3020 ||
+	    ctlr->rf_rev == Rf3290 ||
 	    ctlr->rf_rev == Rf3320)
 		rt3090setrxantenna(ctlr, 0);
 
@@ -2468,13 +2839,17 @@
 	mcucmd(ctlr, McuCmdLed2, ctlr->led[1], 0);
 	mcucmd(ctlr, McuCmdLed3, ctlr->led[2], 0);
 
-	if(ctlr->mac_ver >= 0x3071)
+	if(ctlr->mac_ver == 0x3290)
+		rt3290rfinit(ctlr);
+	else if(ctlr->mac_ver >= 0x3071)
 		rt3090rfinit(ctlr);
 
 	mcucmd(ctlr, McuCmdSleep, 0x02ff, 1);
 	mcucmd(ctlr, McuCmdWakeup, 0, 1);
 
-	if(ctlr->mac_ver >= 0x3071)
+	if(ctlr->mac_ver == 0x3290)
+		rt3290rfwakeup(ctlr);
+	else if(ctlr->mac_ver >= 0x3071)
 		rt3090rfwakeup(ctlr);
 
 	/* disable non-existing Rx chains */
@@ -2500,7 +2875,9 @@
 		rt3090rfsetup(ctlr);
 
 	/* select default channel */
-	if(ctlr->mac_ver >= 0x3071)
+	if(ctlr->mac_ver == 0x3290)
+		rt3290setchan(ctlr, 3);
+	else if(ctlr->mac_ver >= 0x3071)
 		rt3090setchan(ctlr, 3);
 	else
 		setchan(ctlr, 3);
@@ -2564,7 +2941,7 @@
 	Ctlr *ctlr;
 	Wifipkt *w;
 	u8int mcs, qid;
-	int ridx, /*ctl_ridx,*/ hdrlen;
+	int ridx, ctl_ridx, hdrlen;
 	uchar *p;
 	int nodeid;
 	Block *outb;
@@ -2592,7 +2969,10 @@
 
 	pool = &ctlr->pool;
 	qid = 0; /* for now */
-	ridx = 0; 
+	if(wn->minrate >= wifi->rates)
+		ridx = wn->minrate - wifi->rates;
+	else
+		ridx = 0;
 	tx = &ctlr->tx[qid];
 
 	nodeid = ctlr->bcastnodeid;
@@ -2603,26 +2983,43 @@
 	if((w->a1[0] & 1) == 0){
 		*(p+4) = TxAck; /* xflags */
 
+		/* txop, decide if we need protection */
 		if(BLEN(b) > 512-4)
-			*(p+1) = TxTxopBackoff; /* txop */ 
+			*(p+1) = TxTxopHt;
+		else
+			*(p+1) = TxTxopBackoff;
 
 		if((w->fc[0] & 0x0c) == 0x08 &&	ctlr->bssnodeid != -1){
 			nodeid = ctlr->bssnodeid;
-			ridx = 2; /* BUG: hardcode 11Mbit */
+			if(wn->actrate >= wifi->rates)
+				ridx = wn->actrate - wifi->rates;
 		}
 	}
 
-	/*ctl_ridx = rt2860_rates[ridx].ctl_ridx;*/
+	ctl_ridx = rt2860_rates[ridx].ctl_ridx;
 	mcs = rt2860_rates[ridx].mcs;
 
 	/* setup TX Wireless Information */
 	*p = 0; /* flags */
-	*(p+2) = PhyCck | mcs; /* phy */
+	/* phy */
+	if(rt2860_rates[ridx].ofdm)
+		*(p+2) = PhyOfdm | mcs;
+	else{
+		*(p+2) = PhyCck | mcs;
+		if(ridx != 0 && wn->cap & (1<<5))
+			*(p+2) |= PhyShpre;
+	}
 	/* let HW generate seq numbers */
 	*(p+4) |= TxNseq; /* xflags */
 	put16(p + 6, BLEN(b) | (((mcs+1) & 0xf) << TxPidShift) ); /* length */
 
-/*	put16((uchar*)&w->dur[0], rt2860_rates[ctl_ridx].lp_ack_dur); */
+	/* ack duration */
+	if(*(p+4) & TxAck){
+		if(wn->cap & (1<<5))
+			put16(w->dur, rt2860_rates[ctl_ridx].sp_ack_dur);
+		else
+			put16(w->dur, rt2860_rates[ctl_ridx].lp_ack_dur);
+	}
 
 	*(p+5) = nodeid; /* wcid */
 
@@ -2655,9 +3052,9 @@
 
 	p = pool->p + pool->i * TxwiDmaSz;
 	w = (Wifipkt*)(p + Txwisize);
-	if(ctlr->wifi->debug){
-		print("transmit: %E->%E,%E nodeid=%x txq[%d]=%d size=%zd\n", w->a2, w->a1, w->a3, nodeid, qid, ctlr->tx[qid].i, BLEN(outb));
-	}
+	if(ctlr->wifi->debug)
+		print("transmit: %E->%E,%E nodeid=%x txq[%d]=%d size=%zd\n",
+			w->a2, w->a1, w->a3, nodeid, qid, ctlr->tx[qid].i, BLEN(outb));
 
 	tx->i = (tx->i + 1) % Ntx;
 	pool->i = (pool->i + 1) % Ntxpool;
@@ -2674,7 +3071,6 @@
 static void
 rt2860attach(Ether *edev)
 {
-	FWImage *fw;
 	Ctlr *ctlr;
 	char *err;
 
@@ -2691,13 +3087,11 @@
 		if(ctlr->wifi == nil)
 			ctlr->wifi = wifiattach(edev, transmit);
 
-		if(ctlr->fw == nil){
-			fw = readfirmware();
-			ctlr->fw = fw;
-		}
-		if((err = rt2860start(edev)) != nil){
+		ctlr->wifi->rates = rates;
+		if(ctlr->fw == nil)
+			readfirmware(ctlr);
+		if((err = rt2860start(edev)) != nil)
 			error(err);
-		} 
 
 		ctlr->bcastnodeid = -1;
 		ctlr->bssnodeid = -1;
@@ -2841,14 +3235,10 @@
 	debug = ctlr->wifi->debug;
 
 	r = csr32r(ctlr, IntStatus);
-	if(r == 0xffffffff){
-		iunlock(ctlr);
-		return;
-	}
-	if(r == 0){
-		iunlock(ctlr);
-		return;
-	}
+	if(r == 0xffffffff)
+		goto Exit;
+	if(r == 0)
+		goto Exit;
 
 	/* acknowledge interrupts */
 	csr32w(ctlr, IntStatus, r);
@@ -2866,7 +3256,7 @@
 
 		txrxon(ctlr);
 	}
-	if(r & MacInt2)
+	if(r & MacInt2) /* TX status */
 		stats(ctlr);
 	
 	if(r & TxDoneInt5)
@@ -2889,7 +3279,7 @@
 
 	if(r & TxDoneInt0)
 		rt2860tx(ctlr, 0);
-
+Exit:
 	iunlock(ctlr);
 }
 
@@ -2959,7 +3349,7 @@
 	return val;
 }
 
-/* Read 16-bit from eFUSE ROM (>=RT3071 only.) */
+/* Read 16 bits from eFUSE ROM */
 static u16int
 efuseread2(Ctlr *ctlr, u16int addr)
 {
@@ -2968,19 +3358,17 @@
 	int ntries;
 
 	addr *= 2;
-	/*-
-	 * Read one 16-byte block into registers EFUSE_DATA[0-3]:
-	 * DATA0: F E D C
-	 * DATA1: B A 9 8
-	 * DATA2: 7 6 5 4
-	 * DATA3: 3 2 1 0
-	 */
-	tmp = csr32r(ctlr, Rt3070EfuseCtrl);
+
+	if(ctlr->mac_ver == 0x3290)
+		reg = Rt3290EfuseCtrl;
+	else
+		reg = Rt3070EfuseCtrl;
+	tmp = csr32r(ctlr, reg);
 	tmp &= ~(Rt3070EfsromModeMask | Rt3070EfsromAinMask);
 	tmp |= (addr & ~0xf) << Rt3070EfsromAinShift | Rt3070EfsromKick;
-	csr32w(ctlr, Rt3070EfuseCtrl, tmp);
+	csr32w(ctlr, reg, tmp);
 	for(ntries = 0; ntries < 500; ntries++){
-		tmp = csr32r(ctlr, Rt3070EfuseCtrl);
+		tmp = csr32r(ctlr, reg);
 		if(!(tmp & Rt3070EfsromKick))
 			break;
 		microdelay(2);
@@ -2992,7 +3380,10 @@
 		return 0xffff;	/* address not found */
 
 	/* determine to which 32-bit register our 16-bit word belongs */
-	reg = Rt3070EfuseData3 - (addr & 0xc);
+	if(ctlr->mac_ver == 0x3290)
+		reg = Rt3290EfuseData3 + (addr & 0xc);
+	else
+		reg = Rt3070EfuseData3 - (addr & 0xc);
 	tmp = csr32r(ctlr, reg);
 
 	return (addr & 2) ? tmp >> 16 : tmp & 0xffff;
@@ -3014,7 +3405,10 @@
 	/* check whether the ROM is eFUSE ROM or EEPROM */
 	rom_read = eeread2;
 	if(ctlr->mac_ver >= 0x3071){
-		tmp = csr32r(ctlr, Rt3070EfuseCtrl);
+		if(ctlr->mac_ver == 0x3290)
+			tmp = csr32r(ctlr, Rt3290EfuseCtrl);
+		else
+			tmp = csr32r(ctlr, Rt3070EfuseCtrl);
 		if(tmp & Rt3070SelEfuse)
 			rom_read = efuseread2;
 	}
@@ -3031,7 +3425,7 @@
 	edev->ea[5] = val >> 8;
 
 	/* read vendor BBP settings */
-	for(i = 0; i < 8; i++){
+	for(i = 0; i < nelem(ctlr->bbp); i++){
 		val = rom_read(ctlr, EepromBbpBase + i);
 		ctlr->bbp[i].val = val & 0xff;
 		ctlr->bbp[i].reg = val >> 8;
@@ -3070,6 +3464,11 @@
 			ctlr->rf_rev = Rf3053;
 			ctlr->ntxchains = 3;
 			ctlr->nrxchains = 3;
+		}else if(ctlr->mac_ver == 0x3290){
+			/* default to RF3290 1T1R */
+			ctlr->rf_rev = Rf3290;
+			ctlr->ntxchains = 1;
+			ctlr->nrxchains = 1;
 		}else if(ctlr->mac_ver >= 0x3071){
 			/* default to RF3020 1T1R */
 			ctlr->rf_rev = Rf3020;
@@ -3082,7 +3481,10 @@
 			ctlr->nrxchains = 2;
 		}
 	}else{
-		ctlr->rf_rev = (val >> 8) & 0xf;
+		if(ctlr->mac_ver == 0x3290)
+			ctlr->rf_rev = rom_read(ctlr, EepromChipId);
+		else
+			ctlr->rf_rev = (val >> 8) & 0xf;
 		ctlr->ntxchains = (val >> 4) & 0xf;
 		ctlr->nrxchains = val & 0xf;
 	}
@@ -3270,8 +3672,8 @@
 	return 0;
 }
 
-static const char *
-getrfname(u8int rev)
+static char *
+getrfname(u16int rev)
 {
 	if((rev == 0) || (rev >= nelem(rfnames)))
 		return "unknown";
@@ -3420,13 +3822,17 @@
 	Ctlr *ctlr;
 	int ntries;
 	char *err;
-	u32int tmp;
+	u32int tmp, reg;
 
 	SET(tmp);
 	ctlr = edev->ctlr;
+	if(ctlr->pdev->did == RalinkRT3290)
+		reg = PciCfg;
+	else
+		reg = AsicVerId;
 	/* wait for NIC to initialize */
 	for(ntries = 0; ntries < 100; ntries++){
-		tmp = csr32r(ctlr, AsicVerId);
+		tmp = csr32r(ctlr, reg);
 		if(tmp != 0 && tmp != 0xffffffff)
 			break;
 		microdelay(10);
@@ -3438,18 +3844,15 @@
 	ctlr->mac_ver = tmp >> 16;
 	ctlr->mac_rev = tmp & 0xffff;
 
-	if(ctlr->mac_ver != 0x2860){
+	if(ctlr->mac_ver != 0x2860)
 		switch(ctlr->pdev->did){
-			default:
-					break;
-			case RalinkRT2890:
-			case RalinkRT2790:
-			case RalinkRT3090:
-			case AwtRT2890:
-					ctlr->flags = AdvancedPs;
-					break;
+		case RalinkRT2890:
+		case RalinkRT2790:
+		case AwtRT2890:
+			ctlr->flags |= AdvancedPs;
+		default:
+			break;
 		}
-	}
 	/* retrieve RF rev. no and various other things from EEPROM */
 	eepromread(edev);
 
@@ -3478,17 +3881,18 @@
 		
 		if(pdev->ccrb != 2 || pdev->ccru != 0x80)
 			continue;
-		if(pdev->vid != 0x1814) /* Ralink */
+		if(pdev->vid != Ralink)
 			continue;
 		if(pdev->mem[0].bar & 1)
 			continue;
 
 		switch(pdev->did){
-		default:
-			continue;
 		case RalinkRT2790:
 		case RalinkRT3090:
+		case RalinkRT3290:
 			break;
+		default:
+			continue;
 		}
 
 		ctlr = malloc(sizeof(Ctlr));
@@ -3547,7 +3951,7 @@
 	edev->ctl = rt2860ctl;
 	edev->promiscuous = rt2860promiscuous;
 	edev->multicast = rt2860multicast;
-	edev->mbps = 10;
+	edev->mbps = 54;
 
 	if(rt2860init(edev) < 0){
 		edev->ctlr = nil;
