This adds a bwmode debugfs file which can be used to set alternate
channel operating bandwidths.  Only tested with AR5413 and only at
5 and 20 mhz channels.

Signed-off-by: Pat Erley <pat-lkml at erley.org>
---
Other devices will need to be added to the switch in  write_file_bwmode

drivers/net/wireless/ath/ath5k/debug.c |   86 ++++++++++++++++++++++++++++++++
 1 files changed, 86 insertions(+), 0 deletions(-)

--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -813,6 +813,89 @@ static const struct file_operations fops
 	.llseek = default_llseek,
 };
 
+/* debugfs: bwmode */
+
+static ssize_t read_file_bwmode(struct file *file, char __user *user_buf,
+				   size_t count, loff_t *ppos)
+{
+	struct ath5k_hw *ah = file->private_data;
+	char buf[15];
+	unsigned int len = 0;
+
+	int cur_ah_bwmode = ah->ah_bwmode;
+
+#define print_selected(MODE, LABEL) \
+	if (cur_ah_bwmode == MODE) \
+		len += snprintf(buf+len, sizeof(buf)-len, "[%s]", LABEL); \
+	else \
+		len += snprintf(buf+len, sizeof(buf)-len, "%s", LABEL); \
+	len += snprintf(buf+len, sizeof(buf)-len, " ");
+
+	print_selected(AR5K_BWMODE_5MHZ, "5");
+	print_selected(AR5K_BWMODE_10MHZ, "10");
+	print_selected(AR5K_BWMODE_DEFAULT, "20");
+	print_selected(AR5K_BWMODE_40MHZ, "40");
+#undef print_selected
+
+	len += snprintf(buf+len, sizeof(buf)-len, "\n");
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_bwmode(struct file *file,
+				 const char __user *userbuf,
+				 size_t count, loff_t *ppos)
+{
+	struct ath5k_hw *ah = file->private_data;
+	char buf[3];
+	int bw = 20;
+	int tobwmode = AR5K_BWMODE_DEFAULT;
+
+	if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+		return -EFAULT;
+
+	/* TODO: Add check for active interface */
+
+	if(strncmp(buf, "5", 1) == 0 ) {
+		tobwmode = AR5K_BWMODE_5MHZ;
+		bw = 5;
+	} else if ( strncmp(buf, "10", 2) == 0 ) {
+		tobwmode = AR5K_BWMODE_10MHZ;
+		bw = 10;
+	} else if ( strncmp(buf, "20", 2) == 0 ) {
+		tobwmode = AR5K_BWMODE_DEFAULT;
+		bw = 20;
+	} else if ( strncmp(buf, "40", 2) == 0 ) {
+		tobwmode = AR5K_BWMODE_40MHZ;
+		bw = 40;
+	} else
+		return -EINVAL;
+
+	ATH5K_INFO(ah, "Changing to %imhz channel width[%i]\n",
+		bw, tobwmode);
+
+	switch (ah->ah_radio) {
+		/* TODO: only define radios that actually support 5/10mhz channels */
+		case AR5K_RF5413: case AR5K_RF5110: case AR5K_RF5111: case AR5K_RF5112: case AR5K_RF2413: case AR5K_RF2316: case AR5K_RF2317: case AR5K_RF2425:
+			if(ah->ah_bwmode != tobwmode) {
+				mutex_lock(&ah->lock);
+				ah->ah_bwmode = tobwmode;
+				mutex_unlock(&ah->lock);
+			}
+			break;
+		default:
+			return -EOPNOTSUPP;
+	}
+	return count;
+}
+
+static const struct file_operations fops_bwmode = {
+	.read = read_file_bwmode,
+	.write = write_file_bwmode,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
 
 /* debugfs: queues etc */
 
@@ -904,6 +987,9 @@ ath5k_debug_init_device(struct ath5k_hw 
 	debugfs_create_file("beacon", S_IWUSR | S_IRUSR, phydir, ah,
 			    &fops_beacon);
 
+	debugfs_create_file("bwmode", S_IWUSR | S_IRUSR, phydir, ah,
+			    &fops_bwmode);
+
 	debugfs_create_file("reset", S_IWUSR, phydir, ah, &fops_reset);
 
 	debugfs_create_file("antenna", S_IWUSR | S_IRUSR, phydir, ah,
