From 794a098c18f4f6907bc58199901aef412ab4a002 Mon Sep 17 00:00:00 2001 From: "Chandrasekaran, Manishekar" Date: Tue, 12 Jan 2016 19:42:20 +0530 Subject: [PATCH] qcacld-3.0: Add new IOCTL to change SAP/P2P-GO's operating channel Add new IOCTL 'CHANNEL_SWITCH' which is used to move the operating channel of SAP/P2P-GO. This IOCTL is supported only for SAP/P2P-GO and not for STA/P2P-GC. E.g., format to issue command through wpa_supplicant: DRIVER CHANNEL_SWITCH - is channel number to move (1 for ch1, 149 for ch149 etc.) - is bandwidth to move (20 for BW 20, 40 for BW 40 etc.) Change-Id: Ie65f2ceb9ece04053ab32ee60f83fd09cd232f77 CRs-Fixed: 955368 --- core/hdd/inc/wlan_hdd_main.h | 3 + core/hdd/src/wlan_hdd_hostapd.c | 7 ++ core/hdd/src/wlan_hdd_ioctl.c | 119 ++++++++++++++++++++++++++++++++ core/hdd/src/wlan_hdd_main.c | 71 +++++++++++++++++++ 4 files changed, 200 insertions(+) diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 3845c5ef12c7..4cc4eeb424db 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -1341,6 +1341,9 @@ struct hdd_context_s { /*--------------------------------------------------------------------------- Function declarations and documentation -------------------------------------------------------------------------*/ +int hdd_validate_channel_and_bandwidth(hdd_adapter_t *adapter, + uint32_t chan_number, + phy_ch_width chan_bw); #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH void wlan_hdd_check_sta_ap_concurrent_ch_intf(void *sta_pAdapter); #endif diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c index 6da5ebba25a9..e7a70a974b4e 100644 --- a/core/hdd/src/wlan_hdd_hostapd.c +++ b/core/hdd/src/wlan_hdd_hostapd.c @@ -1921,6 +1921,13 @@ int hdd_softap_set_channel_change(struct net_device *dev, int target_channel, return ret; } + ret = hdd_validate_channel_and_bandwidth(pHostapdAdapter, + target_channel, target_bw); + if (ret) { + hdd_err("Invalid CH and BW combo"); + return ret; + } + sta_adapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION); /* * conc_custom_rule1: diff --git a/core/hdd/src/wlan_hdd_ioctl.c b/core/hdd/src/wlan_hdd_ioctl.c index 68005f54ec51..be6ea8f17071 100644 --- a/core/hdd/src/wlan_hdd_ioctl.c +++ b/core/hdd/src/wlan_hdd_ioctl.c @@ -34,6 +34,7 @@ #include "wlan_hdd_power.h" #include "wlan_hdd_driver_ops.h" #include "cds_concurrency.h" +#include "wlan_hdd_hostapd.h" #include "wlan_hdd_p2p.h" #include @@ -5954,6 +5955,123 @@ static int drv_cmd_set_fcc_channel(hdd_adapter_t *adapter, return ret; } +/** + * hdd_parse_set_channel_switch_command() - Parse and validate CHANNEL_SWITCH + * command + * @value: Pointer to the command + * @chan_number: Pointer to the channel number + * @chan_bw: Pointer to the channel bandwidth + * + * Parses and provides the channel number and channel width from the input + * command which is expected to be of the format: CHANNEL_SWITCH + * is channel number to move (where 1 = channel 1, 149 = channel 149, ...) + * is bandwidth to move (where 20 = BW 20, 40 = BW 40, 80 = BW 80) + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_parse_set_channel_switch_command(uint8_t *value, + uint32_t *chan_number, + uint32_t *chan_bw) +{ + const uint8_t *in_ptr = value; + int ret; + + in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE); + + /* no argument after the command */ + if (NULL == in_ptr) { + hdd_err("No argument after the command"); + return -EINVAL; + } + + /* no space after the command */ + if (SPACE_ASCII_VALUE != *in_ptr) { + hdd_err("No space after the command "); + return -EINVAL; + } + + /* remove empty spaces and move the next argument */ + while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) + in_ptr++; + + /* no argument followed by spaces */ + if ('\0' == *in_ptr) { + hdd_err("No argument followed by spaces"); + return -EINVAL; + } + + /* get the two arguments: channel number and bandwidth */ + ret = sscanf(in_ptr, "%u %u", chan_number, chan_bw); + if (ret != 2) { + hdd_err("Arguments retrieval from cmd string failed"); + return -EINVAL; + } + + return 0; +} + +/** + * drv_cmd_set_channel_switch() - Switch SAP/P2P-GO operating channel + * @adapter: HDD adapter + * @hdd_ctx: HDD context + * @command: Pointer to the input command CHANNEL_SWITCH + * @command_len: Command len + * @priv_data: Private data + * + * Handles private IOCTL CHANNEL_SWITCH command to switch the operating channel + * of SAP/P2P-GO + * + * Return: 0 for success, non-zero for failure + */ +static int drv_cmd_set_channel_switch(hdd_adapter_t *adapter, + hdd_context_t *hdd_ctx, + uint8_t *command, + uint8_t command_len, + hdd_priv_data_t *priv_data) +{ + struct net_device *dev = adapter->dev; + int status; + uint32_t chan_number = 0, chan_bw = 0; + uint8_t *value = command; + phy_ch_width width; + + if ((adapter->device_mode != WLAN_HDD_P2P_GO) && + (adapter->device_mode != WLAN_HDD_SOFTAP)) { + hdd_err("IOCTL CHANNEL_SWITCH not supported for mode %d", + adapter->device_mode); + return -EINVAL; + } + + status = hdd_parse_set_channel_switch_command(value, + &chan_number, &chan_bw); + if (status) { + hdd_err("Invalid CHANNEL_SWITCH command"); + return status; + } + + if ((chan_bw != 20) && (chan_bw != 40) && (chan_bw != 80)) { + hdd_err("BW %d is not allowed for CHANNEL_SWITCH", chan_bw); + return -EINVAL; + } + + if (chan_bw == 80) + width = CH_WIDTH_80MHZ; + else if (chan_bw == 40) + width = CH_WIDTH_40MHZ; + else + width = CH_WIDTH_20MHZ; + + hdd_info("CH:%d BW:%d", chan_number, chan_bw); + + status = hdd_softap_set_channel_change(dev, chan_number, width); + if (status) { + hdd_err("Set channel change fail"); + return status; + } + + return 0; +} + /* * The following table contains all supported WLAN HDD * IOCTL driver commands and the handler for each of them. @@ -6066,6 +6184,7 @@ static const hdd_drv_cmd_t hdd_drv_cmds[] = { {"RXFILTER-REMOVE", drv_cmd_rx_filter_remove}, {"RXFILTER-ADD", drv_cmd_rx_filter_add}, {"SET_FCC_CHANNEL", drv_cmd_set_fcc_channel}, + {"CHANNEL_SWITCH", drv_cmd_set_channel_switch}, }; /** diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index fd30caf42c52..7b7f78ce82fc 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -232,6 +232,77 @@ const char *hdd_device_mode_to_string(uint8_t device_mode) } } +/** + * hdd_validate_channel_and_bandwidth() - Validate the channel-bandwidth combo + * @adapter: HDD adapter + * @chan_number: Channel number + * @chan_bw: Bandwidth + * + * Checks if the given bandwidth is valid for the given channel number. + * + * Return: 0 for success, non-zero for failure + */ +int hdd_validate_channel_and_bandwidth(hdd_adapter_t *adapter, + uint32_t chan_number, + phy_ch_width chan_bw) +{ + uint8_t chan[WNI_CFG_VALID_CHANNEL_LIST_LEN]; + uint32_t len = WNI_CFG_VALID_CHANNEL_LIST_LEN, i; + bool found = false; + tHalHandle hal; + + hal = WLAN_HDD_GET_HAL_CTX(adapter); + if (!hal) { + hdd_err("Invalid HAL context"); + return -EINVAL; + } + + if (0 != sme_cfg_get_str(hal, WNI_CFG_VALID_CHANNEL_LIST, chan, &len)) { + hdd_err("No valid channel list"); + return -EOPNOTSUPP; + } + + for (i = 0; i < len; i++) { + if (chan[i] == chan_number) { + found = true; + break; + } + } + + if (found == false) { + hdd_err("Channel not in driver's valid channel list"); + return -EOPNOTSUPP; + } + + if ((!CDS_IS_CHANNEL_24GHZ(chan_number)) && + (!CDS_IS_CHANNEL_5GHZ(chan_number))) { + hdd_err("CH %d is not in 2.4GHz or 5GHz", chan_number); + return -EINVAL; + } + + if (CDS_IS_CHANNEL_24GHZ(chan_number)) { + if (chan_bw == CH_WIDTH_80MHZ) { + hdd_err("BW80 not possible in 2.4GHz band"); + return -EINVAL; + } + if ((chan_bw != CH_WIDTH_20MHZ) && (chan_number == 14) && + (chan_bw != CH_WIDTH_MAX)) { + hdd_err("Only BW20 possible on channel 14"); + return -EINVAL; + } + } + + if (CDS_IS_CHANNEL_5GHZ(chan_number)) { + if ((chan_bw != CH_WIDTH_20MHZ) && (chan_number == 165) && + (chan_bw != CH_WIDTH_MAX)) { + hdd_err("Only BW20 possible on channel 165"); + return -EINVAL; + } + } + + return 0; +} + static int __hdd_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *data) {