cnss: Add support to program MAC address thru debugfs

MAC addresses provisioning thru CNSS is usually done by OEM drivers.
Debugfs interfaces can be used for internal testing.

Change-Id: I1a2693835ac09619baf03ee7d2e1b69dbe48559f
Signed-off-by: Sameer Thalappil <sameert@codeaurora.org>
diff --git a/drivers/net/wireless/cnss_utils/cnss_utils.c b/drivers/net/wireless/cnss_utils/cnss_utils.c
index 4955130..77a58c8 100644
--- a/drivers/net/wireless/cnss_utils/cnss_utils.c
+++ b/drivers/net/wireless/cnss_utils/cnss_utils.c
@@ -13,8 +13,10 @@
 #define pr_fmt(fmt) "cnss_utils: " fmt
 
 #include <linux/module.h>
+#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/etherdevice.h>
+#include <linux/debugfs.h>
 #include <net/cnss_utils.h>
 
 #define CNSS_MAX_CH_NUM 45
@@ -29,6 +31,7 @@
 };
 
 #define MAX_NO_OF_MAC_ADDR 4
+#define MAC_PREFIX_LEN 2
 struct cnss_wlan_mac_addr {
 	u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
 	u32 no_of_mac_addr_set;
@@ -50,6 +53,7 @@
 	struct cnss_wlan_mac_addr wlan_mac_addr;
 	struct cnss_wlan_mac_addr wlan_der_mac_addr;
 	enum cnss_utils_cc_src cc_source;
+	struct dentry *root_dentry;
 } *cnss_utils_priv;
 
 int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
@@ -317,6 +321,137 @@
 }
 EXPORT_SYMBOL(cnss_utils_get_cc_source);
 
+static ssize_t cnss_utils_mac_write(struct file *fp,
+				    const char __user *user_buf,
+				    size_t count, loff_t *off)
+{
+	struct cnss_utils_priv *priv =
+		((struct seq_file *)fp->private_data)->private;
+	char buf[128];
+	char *input, *mac_type, *mac_address;
+	u8 *dest_mac;
+	u8 val;
+	const char *delim = " \n";
+	size_t len = 0;
+	char temp[3] = "";
+
+	len = min_t(size_t, count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EINVAL;
+	buf[len] = '\0';
+
+	input = buf;
+
+	mac_type = strsep(&input, delim);
+	if (!mac_type)
+		return -EINVAL;
+	if (!input)
+		return -EINVAL;
+
+	mac_address = strsep(&input, delim);
+	if (!mac_address)
+		return -EINVAL;
+	if (strncmp("0x", mac_address, MAC_PREFIX_LEN)) {
+		pr_err("Invalid MAC prefix\n");
+		return -EINVAL;
+	}
+
+	len = strlen(mac_address);
+	mac_address += MAC_PREFIX_LEN;
+	len -= MAC_PREFIX_LEN;
+	if (len < ETH_ALEN * 2 || len > ETH_ALEN * 2 * MAX_NO_OF_MAC_ADDR ||
+	    len % (ETH_ALEN * 2) != 0) {
+		pr_err("Invalid MAC address length %zu\n", len);
+		return -EINVAL;
+	}
+
+	if (!strcmp("provisioned", mac_type)) {
+		dest_mac = &priv->wlan_mac_addr.mac_addr[0][0];
+		priv->wlan_mac_addr.no_of_mac_addr_set = len / (ETH_ALEN * 2);
+	} else if (!strcmp("derived", mac_type)) {
+		dest_mac = &priv->wlan_der_mac_addr.mac_addr[0][0];
+		priv->wlan_der_mac_addr.no_of_mac_addr_set =
+			len / (ETH_ALEN * 2);
+	} else {
+		pr_err("Invalid MAC address type %s\n", mac_type);
+		return -EINVAL;
+	}
+
+	while (len--) {
+		temp[0] = *mac_address++;
+		temp[1] = *mac_address++;
+		if (kstrtou8(temp, 16, &val))
+			return -EINVAL;
+		*dest_mac++ = val;
+	}
+	return count;
+}
+
+static int cnss_utils_mac_show(struct seq_file *s, void *data)
+{
+	u8 mac[6];
+	int i;
+	struct cnss_utils_priv *priv = s->private;
+	struct cnss_wlan_mac_addr *addr = NULL;
+
+	addr = &priv->wlan_mac_addr;
+	if (addr->no_of_mac_addr_set) {
+		seq_puts(s, "\nProvisioned MAC addresseses\n");
+		for (i = 0; i < addr->no_of_mac_addr_set; i++) {
+			ether_addr_copy(mac, addr->mac_addr[i]);
+			seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
+				   mac[0], mac[1], mac[2],
+				   mac[3], mac[4], mac[5]);
+		}
+	}
+
+	addr = &priv->wlan_der_mac_addr;
+	if (addr->no_of_mac_addr_set) {
+		seq_puts(s, "\nDerived MAC addresseses\n");
+		for (i = 0; i < addr->no_of_mac_addr_set; i++) {
+			ether_addr_copy(mac, addr->mac_addr[i]);
+			seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
+				   mac[0], mac[1], mac[2],
+				   mac[3], mac[4], mac[5]);
+		}
+	}
+
+	return 0;
+}
+
+static int cnss_utils_mac_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cnss_utils_mac_show, inode->i_private);
+}
+
+static const struct file_operations cnss_utils_mac_fops = {
+	.read		= seq_read,
+	.write		= cnss_utils_mac_write,
+	.release	= single_release,
+	.open		= cnss_utils_mac_open,
+	.owner		= THIS_MODULE,
+	.llseek		= seq_lseek,
+};
+
+static int cnss_utils_debugfs_create(struct cnss_utils_priv *priv)
+{
+	int ret = 0;
+	struct dentry *root_dentry;
+
+	root_dentry = debugfs_create_dir("cnss_utils", NULL);
+
+	if (IS_ERR(root_dentry)) {
+		ret = PTR_ERR(root_dentry);
+		pr_err("Unable to create debugfs %d\n", ret);
+		goto out;
+	}
+	priv->root_dentry = root_dentry;
+	debugfs_create_file("mac_address", 0600, root_dentry, priv,
+			    &cnss_utils_mac_fops);
+out:
+	return ret;
+}
+
 static int __init cnss_utils_init(void)
 {
 	struct cnss_utils_priv *priv = NULL;
@@ -329,7 +464,7 @@
 
 	mutex_init(&priv->unsafe_channel_list_lock);
 	spin_lock_init(&priv->dfs_nol_info_lock);
-
+	cnss_utils_debugfs_create(priv);
 	cnss_utils_priv = priv;
 
 	return 0;