diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 9f405dc2e782..a10385c604aa 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -123,5 +123,6 @@ config VIRT_WIFI are wifi connections through a special rtnetlink device. source "drivers/net/wireless/cnss2/Kconfig" +source "drivers/net/wireless/cnss_utils/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 14bbe9a2fd15..26733f656be0 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o obj-$(CONFIG_CNSS2) += cnss2/ +obj-$(CONFIG_CNSS_UTILS) += cnss_utils/ diff --git a/drivers/net/wireless/cnss_utils/Kconfig b/drivers/net/wireless/cnss_utils/Kconfig new file mode 100644 index 000000000000..ed1662cd4896 --- /dev/null +++ b/drivers/net/wireless/cnss_utils/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config CNSS_UTILS + tristate "CNSS utilities support" + help + Add CNSS utilities support for the WLAN driver module. + This feature enables wlan driver to use CNSS utilities APIs to set + and get wlan related information. \ No newline at end of file diff --git a/drivers/net/wireless/cnss_utils/Makefile b/drivers/net/wireless/cnss_utils/Makefile new file mode 100644 index 000000000000..aa007fd50508 --- /dev/null +++ b/drivers/net/wireless/cnss_utils/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_CNSS_UTILS) += cnss_utils.o diff --git a/drivers/net/wireless/cnss_utils/cnss_utils.c b/drivers/net/wireless/cnss_utils/cnss_utils.c new file mode 100644 index 000000000000..84fcfa07cdef --- /dev/null +++ b/drivers/net/wireless/cnss_utils/cnss_utils.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved. */ + +#define pr_fmt(fmt) "cnss_utils: " fmt + +#include +#include +#include +#include +#include +#include + +#define CNSS_MAX_CH_NUM 45 +struct cnss_unsafe_channel_list { + u16 unsafe_ch_count; + u16 unsafe_ch_list[CNSS_MAX_CH_NUM]; +}; + +struct cnss_dfs_nol_info { + void *dfs_nol_info; + u16 dfs_nol_info_len; +}; + +#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; +}; + +enum mac_type { + CNSS_MAC_PROVISIONED, + CNSS_MAC_DERIVED, +}; + +static struct cnss_utils_priv { + struct cnss_unsafe_channel_list unsafe_channel_list; + struct cnss_dfs_nol_info dfs_nol_info; + /* generic mutex for unsafe channel */ + struct mutex unsafe_channel_list_lock; + /* generic spin-lock for dfs_nol info */ + spinlock_t dfs_nol_info_lock; + int driver_load_cnt; + 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, + u16 *unsafe_ch_list, u16 ch_count) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + mutex_lock(&priv->unsafe_channel_list_lock); + if (!unsafe_ch_list || ch_count > CNSS_MAX_CH_NUM) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -EINVAL; + } + + priv->unsafe_channel_list.unsafe_ch_count = ch_count; + + if (ch_count == 0) + goto end; + + memcpy(priv->unsafe_channel_list.unsafe_ch_list, + unsafe_ch_list, ch_count * sizeof(u16)); + +end: + mutex_unlock(&priv->unsafe_channel_list_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel); + +int cnss_utils_get_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 *ch_count, u16 buf_len) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + mutex_lock(&priv->unsafe_channel_list_lock); + if (!unsafe_ch_list || !ch_count) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -EINVAL; + } + + if (buf_len < + (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -ENOMEM; + } + + *ch_count = priv->unsafe_channel_list.unsafe_ch_count; + memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list, + priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16)); + mutex_unlock(&priv->unsafe_channel_list_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel); + +int cnss_utils_wlan_set_dfs_nol(struct device *dev, + const void *info, u16 info_len) +{ + void *temp; + void *old_nol_info; + struct cnss_dfs_nol_info *dfs_info; + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + if (!info || !info_len) + return -EINVAL; + + temp = kmemdup(info, info_len, GFP_ATOMIC); + if (!temp) + return -ENOMEM; + + spin_lock_bh(&priv->dfs_nol_info_lock); + dfs_info = &priv->dfs_nol_info; + old_nol_info = dfs_info->dfs_nol_info; + dfs_info->dfs_nol_info = temp; + dfs_info->dfs_nol_info_len = info_len; + spin_unlock_bh(&priv->dfs_nol_info_lock); + kfree(old_nol_info); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol); + +int cnss_utils_wlan_get_dfs_nol(struct device *dev, + void *info, u16 info_len) +{ + int len; + struct cnss_dfs_nol_info *dfs_info; + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + if (!info || !info_len) + return -EINVAL; + + spin_lock_bh(&priv->dfs_nol_info_lock); + + dfs_info = &priv->dfs_nol_info; + if (!dfs_info->dfs_nol_info || + dfs_info->dfs_nol_info_len == 0) { + spin_unlock_bh(&priv->dfs_nol_info_lock); + return -ENOENT; + } + + len = min(info_len, dfs_info->dfs_nol_info_len); + memcpy(info, dfs_info->dfs_nol_info, len); + spin_unlock_bh(&priv->dfs_nol_info_lock); + + return len; +} +EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol); + +void cnss_utils_increment_driver_load_cnt(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return; + + ++(priv->driver_load_cnt); +} +EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt); + +int cnss_utils_get_driver_load_cnt(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + return priv->driver_load_cnt; +} +EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt); + +static int set_wlan_mac_address(const u8 *mac_list, const uint32_t len, + enum mac_type type) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + u32 no_of_mac_addr; + struct cnss_wlan_mac_addr *addr = NULL; + int iter; + u8 *temp = NULL; + + if (!priv) + return -EINVAL; + + if (len == 0 || (len % ETH_ALEN) != 0) { + pr_err("Invalid length %d\n", len); + return -EINVAL; + } + + no_of_mac_addr = len / ETH_ALEN; + if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) { + pr_err("Exceed maximum supported MAC address %u %u\n", + MAX_NO_OF_MAC_ADDR, no_of_mac_addr); + return -EINVAL; + } + + if (type == CNSS_MAC_PROVISIONED) + addr = &priv->wlan_mac_addr; + else + addr = &priv->wlan_der_mac_addr; + + if (addr->no_of_mac_addr_set) { + pr_err("WLAN MAC address is already set, num %d type %d\n", + addr->no_of_mac_addr_set, type); + return 0; + } + + addr->no_of_mac_addr_set = no_of_mac_addr; + temp = &addr->mac_addr[0][0]; + + for (iter = 0; iter < no_of_mac_addr; + ++iter, temp += ETH_ALEN, mac_list += ETH_ALEN) { + ether_addr_copy(temp, mac_list); + pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n", + temp[0], temp[1], temp[2], + temp[3], temp[4], temp[5]); + } + return 0; +} + +int cnss_utils_set_wlan_mac_address(const u8 *mac_list, const uint32_t len) +{ + return set_wlan_mac_address(mac_list, len, CNSS_MAC_PROVISIONED); +} +EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address); + +int cnss_utils_set_wlan_derived_mac_address(const u8 *mac_list, + const uint32_t len) +{ + return set_wlan_mac_address(mac_list, len, CNSS_MAC_DERIVED); +} +EXPORT_SYMBOL(cnss_utils_set_wlan_derived_mac_address); + +static u8 *get_wlan_mac_address(struct device *dev, + u32 *num, enum mac_type type) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + struct cnss_wlan_mac_addr *addr = NULL; + + if (!priv) + goto out; + + if (type == CNSS_MAC_PROVISIONED) + addr = &priv->wlan_mac_addr; + else + addr = &priv->wlan_der_mac_addr; + + if (!addr->no_of_mac_addr_set) { + pr_err("WLAN MAC address is not set, type %d\n", type); + goto out; + } + *num = addr->no_of_mac_addr_set; + return &addr->mac_addr[0][0]; + +out: + *num = 0; + return NULL; +} + +u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num) +{ + return get_wlan_mac_address(dev, num, CNSS_MAC_PROVISIONED); +} +EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address); + +u8 *cnss_utils_get_wlan_derived_mac_address(struct device *dev, + uint32_t *num) +{ + return get_wlan_mac_address(dev, num, CNSS_MAC_DERIVED); +} +EXPORT_SYMBOL(cnss_utils_get_wlan_derived_mac_address); + +void cnss_utils_set_cc_source(struct device *dev, + enum cnss_utils_cc_src cc_source) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return; + + priv->cc_source = cc_source; +} +EXPORT_SYMBOL(cnss_utils_set_cc_source); + +enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + return priv->cc_source; +} +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 (strcmp("0x", mac_address)) { + 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; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->cc_source = CNSS_UTILS_SOURCE_CORE; + + 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; +} + +static void __exit cnss_utils_exit(void) +{ + kfree(cnss_utils_priv); + cnss_utils_priv = NULL; +} + +module_init(cnss_utils_init); +module_exit(cnss_utils_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CNSS Utilities Driver"); diff --git a/include/net/cnss_utils.h b/include/net/cnss_utils.h new file mode 100644 index 000000000000..fdac4fada497 --- /dev/null +++ b/include/net/cnss_utils.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved. */ + +#ifndef _CNSS_UTILS_H_ +#define _CNSS_UTILS_H_ + +enum cnss_utils_cc_src { + CNSS_UTILS_SOURCE_CORE, + CNSS_UTILS_SOURCE_11D, + CNSS_UTILS_SOURCE_USER +}; + +extern int cnss_utils_set_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 ch_count); +extern int cnss_utils_get_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 *ch_count, u16 buf_len); +extern int cnss_utils_wlan_set_dfs_nol(struct device *dev, + const void *info, u16 info_len); +extern int cnss_utils_wlan_get_dfs_nol(struct device *dev, + void *info, u16 info_len); +extern int cnss_utils_get_driver_load_cnt(struct device *dev); +extern void cnss_utils_increment_driver_load_cnt(struct device *dev); +extern int cnss_utils_set_wlan_mac_address(const u8 *in, uint32_t len); +extern u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num); +extern int cnss_utils_set_wlan_derived_mac_address(const u8 *in, uint32_t len); +extern u8 *cnss_utils_get_wlan_derived_mac_address(struct device *dev, + uint32_t *num); +extern void cnss_utils_set_cc_source(struct device *dev, + enum cnss_utils_cc_src cc_source); +extern enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev); + +#endif