--- prism2_wlan.h-2001-12-18	Tue Jan  1 08:54:52 2002
+++ prism2_wlan.h	Tue Jan  1 10:39:08 2002
@@ -220,6 +220,8 @@
 /* Test mode operations */
 #define HFA384X_TEST_MONITOR 0x0B
 #define HFA384X_TEST_STOP 0x0F
+#define HFA384X_TEST_CFG_BITS 0x15
+#define HFA384X_TEST_CFG_BIT_ALC BIT(3)
 
 #define HFA384X_CMD_BUSY BIT(15)
 
@@ -409,6 +411,9 @@
 #define HFA384X_TX_STATUS_DISCON BIT(2)
 #define HFA384X_TX_STATUS_FORMERR BIT(3)
 
+/* HFA3861/3863 (BBP) Control Registers */
+#define HFA386X_CR_MANUAL_TX_POWER 0x3E
+
 /* IEEE 802.11 defines */
 
 #define WLAN_FC_PVER (BIT(1) | BIT(0))
@@ -566,6 +571,12 @@
 	int hw_resetting;
 	int hw_ready;
 	int hw_reset_tries; /* how many times reset has been tried */
+
+	enum {
+		PRISM2_TXPOWER_AUTO = 0, PRISM2_TXPOWER_OFF,
+		PRISM2_TXPOWER_FIXED
+	} txpower_type;
+	int txpower; /* if txpower_type == PRISM2_TXPOWER_FIXED */
 
 	/* skb queue for packets to be send using dev_queue_xmit() after
 	 * exiting hard IRQ handler (prism2_rx) */
--- prism2.c-2001-12-18	Tue Jan  1 08:54:52 2002
+++ prism2.c	Tue Jan  1 11:02:42 2002
@@ -2129,6 +2129,42 @@
 
 #endif /* WIRELESS_EXT */
 
+/*
+ * FIX: check power mapping.. this may be completely wrong
+ *
+ * This version assumes following mapping:
+ * 0x00: 0mW, 0x80: 100mW, 0xFF: 0mW (linear)
+ * HFA3861 data sheet: CR31 bit7:1 (7 bits; bit0 ignored) to DAC input;
+ * -64 to 63 in range
+ */
+
+static int prism2_txpower_hfa386x_to_mW(u16 val)
+{
+	u16 tmp;
+	int ret;
+	tmp = val > 0xff ? 0xff : val;
+	if (tmp > 0x80)
+		tmp = 0xff - tmp; /* 0x00 .. 0x80 */
+	ret = (100 * tmp) / 0x80;
+	printk(KERN_DEBUG "prism2_txpower: %d => %d mW\n",
+	       val, ret);
+	return ret;
+}
+
+static u16 prism2_txpower_mW_to_hfa386x(int val)
+{
+	int tmp;
+
+	if (val >= 100)
+		return 0x80;
+	if (val < 0)
+		return 0;
+
+	tmp = (0x80 * val) / 100;
+	tmp = 0xff - tmp; /* 0x80 .. 0xff */
+	printk(KERN_DEBUG "prism2_txpower: %d mW => %d\n", val, tmp);
+	return tmp;
+}
 
 static int prism2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
@@ -2165,6 +2201,10 @@
 		    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset" }
 		, { SIOCDEVPRIVATE + 8,
 		    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump" }
+		, { SIOCDEVPRIVATE + 9,
+		    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "alc" }
+		, { SIOCDEVPRIVATE + 10,
+		    IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "txpower" }
 	};
 
 	switch (cmd) {
@@ -2395,6 +2435,17 @@
 		}
 		wrq->u.data.length = sizeof(range);
 		memset(&range, 0, sizeof(range));
+
+#if WIRELESS_EXT > 9
+		/* FIX: actually there are more available TX powers, but let
+		 * just list something for now.. */
+		range.num_txpower = 3;
+		range.txpower[0] = 10;
+		range.txpower[1] = 30;
+		range.txpower[2] = 100;
+		range.txpower_capa = IW_TXPOW_MWATT;
+#endif /* WIRELESS_EXT > 9 */
+
 #if WIRELESS_EXT > 10
 		range.we_version_compiled = WIRELESS_EXT;
 		range.we_version_source = 11;
@@ -2477,6 +2528,97 @@
 		}
 		break;
 
+#if WIRELESS_EXT > 9
+	case SIOCSIWTXPOW:
+		printk(KERN_DEBUG "SIOCSIWTXPOW\n");
+		if (wrq->u.txpower.disabled) {
+			if (local->txpower_type != PRISM2_TXPOWER_OFF) {
+				printk(KERN_DEBUG "Turning radio off "
+				       "(not yet implemented)\n");
+				/* TODO: turn the radio power off */
+				local->txpower_type = PRISM2_TXPOWER_OFF;
+			}
+			break;
+		}
+
+		if (local->txpower_type == PRISM2_TXPOWER_OFF) {
+			printk(KERN_DEBUG "Turning radio on "
+			       "(not yet implemented)\n");
+			/* TODO: turn the radio power on */
+		}
+
+		if (!wrq->u.txpower.fixed &&
+		    local->txpower_type != PRISM2_TXPOWER_AUTO) {
+			printk(KERN_DEBUG "Setting ALC on\n");
+			val = HFA384X_TEST_CFG_BIT_ALC;
+			hfa384x_cmd(dev, HFA384X_CMDCODE_TEST |
+				    (HFA384X_TEST_CFG_BITS << 8),
+				    1, &val, NULL);
+			local->txpower_type = PRISM2_TXPOWER_AUTO;
+			break;
+		}
+
+		if (local->txpower_type != PRISM2_TXPOWER_FIXED) {
+			printk(KERN_DEBUG "Setting ALC off\n");
+			val = HFA384X_TEST_CFG_BIT_ALC;
+			hfa384x_cmd(dev, HFA384X_CMDCODE_TEST |
+				    (HFA384X_TEST_CFG_BITS << 8),
+				    0, &val, NULL);
+			local->txpower_type = PRISM2_TXPOWER_FIXED;
+		}
+
+		{
+			char *tmp;
+			if (wrq->u.txpower.flags == IW_TXPOW_DBM)
+				tmp = "dBm";
+			else if (wrq->u.txpower.flags == IW_TXPOW_MWATT)
+				tmp = "mW";
+			else
+				tmp = "UNKNOWN";
+			printk(KERN_DEBUG "Setting TX power to %d %s\n",
+			       wrq->u.txpower.value, tmp);
+		}
+		if (wrq->u.txpower.flags != IW_TXPOW_MWATT) {
+			printk("SIOCSIWTXPOW with dBm is not supported; "
+			       "use mW\n");
+			ret = -EOPNOTSUPP;
+			break;
+		}
+		local->txpower = wrq->u.txpower.value;
+		val = prism2_txpower_mW_to_hfa386x(local->txpower);
+                if (hfa384x_cmd(dev, HFA384X_CMDCODE_WRITEMIF,
+                                 HFA386X_CR_MANUAL_TX_POWER, &val, NULL))
+                        ret = -EOPNOTSUPP;
+		break;
+
+	case SIOCGIWTXPOW:
+		printk(KERN_DEBUG "SIOCGIWTXPOW\n");
+		wrq->u.txpower.flags = IW_TXPOW_MWATT;
+		wrq->u.txpower.disabled = 0;
+		wrq->u.txpower.fixed = 0;
+		if (local->txpower_type == PRISM2_TXPOWER_AUTO) {
+			if (hfa384x_cmd(dev, HFA384X_CMDCODE_READMIF,
+					HFA386X_CR_MANUAL_TX_POWER,
+					NULL, &resp0) == 0) {
+				wrq->u.txpower.value =
+					prism2_txpower_hfa386x_to_mW(resp0);
+			} else {
+				/* Could not get real txpower; guess 30 mW */
+				wrq->u.txpower.value = 30;
+			}
+		} else if (local->txpower_type == PRISM2_TXPOWER_OFF) {
+			wrq->u.txpower.value = 0;
+			wrq->u.txpower.disabled = 1;
+		} else if (local->txpower_type == PRISM2_TXPOWER_FIXED) {
+			wrq->u.txpower.value = local->txpower;
+			wrq->u.txpower.fixed = 1;
+		} else {
+			printk("SIOCGIWTXPOW - unknown txpower_type=%d\n",
+			       local->txpower_type);
+		}
+		break;
+#endif
+
 	case SIOCGIWPRIV:
 		if (!wrq->u.data.pointer ||
 		    verify_area(VERIFY_WRITE, wrq->u.data.pointer,
@@ -2706,6 +2848,28 @@
 		local->frame_dump = i;
 		break;
 
+	case SIOCDEVPRIVATE + 9:
+		if (!capable(CAP_NET_ADMIN)) {
+			ret = -EPERM;
+			break;
+		}
+
+		i = *((int *) wrq->u.name);
+		printk(KERN_DEBUG "%s: %s ALC\n", dev->name,
+		       i == 0 ? "Disabling" : "Enabling");
+		val = HFA384X_TEST_CFG_BIT_ALC;
+		hfa384x_cmd(dev, HFA384X_CMDCODE_TEST |
+			    (HFA384X_TEST_CFG_BITS << 8),
+			    i == 0 ? 0 : 1, &val, NULL);
+		break;
+
+        case SIOCDEVPRIVATE + 10:
+                val = __cpu_to_le16(*((int *) wrq->u.name));
+                if (hfa384x_cmd(dev, HFA384X_CMDCODE_WRITEMIF,
+                                 HFA386X_CR_MANUAL_TX_POWER, &val, NULL))
+                        ret = -EOPNOTSUPP;
+                break;
+
 	/* not supported wireless extensions */
 	case SIOCSIWNAME:
 	case SIOCSIWNWID:
@@ -2717,10 +2881,6 @@
 #if WIRELESS_EXT > 8
 	case SIOCSIWPOWER:
 	case SIOCGIWPOWER:
-#endif
-#if WIRELESS_EXT > 9
-	case SIOCSIWTXPOW:
-	case SIOCGIWTXPOW:
 #endif
 #if WIRELESS_EXT > 10
 	case SIOCSIWRETRY:

