diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 486af5dac5df..2217e0cae4da 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -333,5 +333,6 @@ source "drivers/net/wireless/cw1200/Kconfig" source "drivers/net/wireless/rsi/Kconfig" source "drivers/net/wireless/cnss/Kconfig" source "drivers/net/wireless/cnss_genl/Kconfig" +source "drivers/net/wireless/cnss_utils/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 0204fc00f0c5..704a976bdfb5 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -67,3 +67,4 @@ obj-$(CONFIG_CNSS) += cnss/ obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/ obj-$(CONFIG_CNSS_CRYPTO) += cnss_crypto/ obj-$(CONFIG_CNSS_GENL) += cnss_genl/ +obj-$(CONFIG_CNSS_UTILS) += cnss_utils/ diff --git a/drivers/net/wireless/cnss/Kconfig b/drivers/net/wireless/cnss/Kconfig index 8e69a2b469b9..8946f65df716 100644 --- a/drivers/net/wireless/cnss/Kconfig +++ b/drivers/net/wireless/cnss/Kconfig @@ -1,5 +1,6 @@ config CNSS tristate "CNSS driver for wifi module" + select CNSS_UTILS select CRYPTO select CRYPTO_HASH select CRYPTO_BLKCIPHER diff --git a/drivers/net/wireless/cnss_utils/Kconfig b/drivers/net/wireless/cnss_utils/Kconfig new file mode 100644 index 000000000000..5f43e4872d65 --- /dev/null +++ b/drivers/net/wireless/cnss_utils/Kconfig @@ -0,0 +1,6 @@ +config CNSS_UTILS + bool "CNSS utilities support" + ---help--- + Add CNSS utilities support for the WLAN driver module. + This feature enable 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..0d1ed7ae939e --- /dev/null +++ b/drivers/net/wireless/cnss_utils/Makefile @@ -0,0 +1 @@ +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..a452900868c4 --- /dev/null +++ b/drivers/net/wireless/cnss_utils/cnss_utils.c @@ -0,0 +1,310 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "cnss_utils: " fmt + +#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 +struct cnss_wlan_mac_addr { + u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN]; + u32 no_of_mac_addr_set; +}; + +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; + bool is_wlan_mac_set; + struct cnss_wlan_mac_addr wlan_mac_addr; + enum cnss_utils_cc_src cc_source; +} *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 = kmalloc(info_len, GFP_ATOMIC); + if (!temp) + return -ENOMEM; + + memcpy(temp, info, info_len); + 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); + +int cnss_utils_set_wlan_mac_address(const u8 *in, const uint32_t len) +{ + 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 (priv->is_wlan_mac_set) { + pr_debug("WLAN MAC address is already set\n"); + return 0; + } + + 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; + } + + priv->is_wlan_mac_set = true; + addr = &priv->wlan_mac_addr; + 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, in += ETH_ALEN) { + ether_addr_copy(temp, in); + 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; +} +EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address); + +u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + struct cnss_wlan_mac_addr *addr = NULL; + + if (!priv) + goto out; + + if (!priv->is_wlan_mac_set) { + pr_debug("WLAN MAC address is not set\n"); + goto out; + } + + addr = &priv->wlan_mac_addr; + *num = addr->no_of_mac_addr_set; + return &addr->mac_addr[0][0]; +out: + *num = 0; + return NULL; +} +EXPORT_SYMBOL(cnss_utils_get_wlan_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 int __init cnss_utils_init(void) +{ + struct cnss_utils_priv *priv = NULL; + + priv = kmalloc(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_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(DEVICE "CNSS Utilities Driver"); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index ea008ffbc856..0c4414d27eeb 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -468,6 +468,7 @@ config MINIDUMP_MAX_ENTRIES config ICNSS tristate "Platform driver for Q6 integrated connectivity" + select CNSS_UTILS ---help--- This module adds support for Q6 integrated WLAN connectivity subsystem. This module is responsible for communicating WLAN on/off diff --git a/include/net/cnss_utils.h b/include/net/cnss_utils.h new file mode 100644 index 000000000000..6ff0fd0907f8 --- /dev/null +++ b/include/net/cnss_utils.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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 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