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;