platform_system_qcom/softap/sdk/qsap_api.c
Ajit Vaishya 5d72c3af41 qsap: Add APIs to control and manage bridge interface.
This adds APIs to trigger create, delete, interface up and interface
down commands on bridge interface.
It uses following IOCTLs for sending command to linux kernel:
Add bridge iface: SIOCBRADDBR
Remove bridge iface: SIOCBRDELBR
Mark iface up/down: SIOCGIFFLAGS

CRs-Fixed: 2080924
Change-Id: I3d34dc16e9ca82dc4b550cbc83fa4120c80ad54c
2017-07-27 14:08:21 -07:00

3496 lines
102 KiB
C
Executable file

/*
* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/wireless.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <private/android_filesystem_config.h>
#include <net/if.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include "nl80211_copy.h"
#include "qsap_api.h"
#include "qsap.h"
#define QCSAP_IOCTL_GETPARAM (SIOCIWFIRSTPRIV+1)
#define WLAN_PRIV_SET_THREE_INT_GET_NONE (SIOCIWFIRSTPRIV + 4)
#define QCSAP_IOCTL_GET_CHANNEL (SIOCIWFIRSTPRIV+9)
#define QCSAP_IOCTL_ASSOC_STA_MACADDR (SIOCIWFIRSTPRIV+10)
#define QCSAP_IOCTL_DISASSOC_STA (SIOCIWFIRSTPRIV+11)
#define QCSAP_IOCTL_AP_STATS (SIOCIWFIRSTPRIV+12)
#define QCSAP_IOCTL_SET_CHANNEL_RANGE (SIOCIWFIRSTPRIV+17)
#define QCSAP_PARAM_GET_AUTO_CHANNEL 9
#define WE_SET_SAP_CHANNELS 3
//#define LOG_TAG "QCSDK-"
#include "cutils/properties.h"
#include "cutils/log.h"
#define SKIP_BLANK_SPACE(x) {while(*x != '\0') { if((*x == ' ') || (*x == '\t')) x++; else break; }}
/** If this variable is enabled, the soft AP is reloaded, after the commit
* command is received */
static volatile int gIniUpdated = 0;
/** Supported command requests.
* WANRING: The enum eCMD_REQ in the file qsap_api.h should be
* updated if Cmd_req[], us updated
*/
s8 *Cmd_req[eCMD_REQ_LAST] = {
"get",
"set"
};
/*
* WARNING: On updating the cmd_list, the enum esap_cmd in file
* qsap_api.h must be updates to reflect the changes
*/
static struct Command cmd_list[eCMD_LAST] = {
{ "ssid", "QualcommSoftAP" },
{ "ignore_broadcast_ssid", "0" },
{ "channel", "1" },
{ "beacon_int", "100" },
{ "dtim_period", "2" },
{ "hw_mode", "n" },
{ "auth_algs", "3" },
{ "security_mode", "0" },
{ "wep_key0", NULL },
{ "wep_key1", NULL },
{ "wep_key2", NULL },
{ "wep_key3", NULL },
{ "wep_default_key", NULL },
{ "wpa_passphrase", NULL },
{ "wpa_pairwise", NULL },
{ "rsn_pairwise", NULL },
{ "mac_address", "00deadbeef04" },
{ "reset_ap", NULL },
{ "macaddr_acl", "0" },
{ "add_to_allow_list", NULL },
{ "add_to_deny_list", NULL },
{ "remove_from_allow_list", NULL },
{ "remove_from_deny_list", NULL },
{ "allow_list", "" },
{ "deny_list", "" },
{ "commit", NULL },
{ "enable_softap", NULL },
{ "disassoc_sta", NULL },
{ "reset_to_default", NULL },
{ "protection_flag", "1" },
{ "data_rate", "0" },
{ "sta_mac_list", NULL },
{ "tx_power", "27" },
{ "sdk_version", SDK_VERSION },
{ "wmm_enabled", "0" },
/** Warning: Do not change the order of the WPS commands */
{ "wps_state", "0" },
{ "config_methods", NULL },
{ "uuid", NULL },
{ "device_name", NULL },
{ "manufacturer", NULL },
{ "model_name", NULL },
{ "model_number", NULL },
{ "serial_number", NULL },
{ "device_type", NULL },
{ "os_version", NULL },
{ "friendly_name", NULL },
{ "manufacturer_url", NULL },
{ "model_description", NULL },
{ "model_url", NULL },
{ "upc", NULL },
/************ WPS commands end *********/
{ "fragm_threshold", NULL },
{ "rts_threshold", NULL },
{ "wpa_group_rekey", NULL },
{ "country_code", NULL },
{ "ap_isolate", NULL },
{ "ieee80211d", NULL },
{ "apstat", NULL },
{ "auto_shut_off_time", NULL },
{ "energy_detect_threshold", "128" },
{ "basic_rates", NULL },
{ "require_ht", NULL },
{ "ieee80211n", "1" },
{ "setchannelrange", NULL },
{ "autochannel", NULL },
{ "ieee80211w", NULL },
{ "wpa_key_mgmt", NULL },
{ "max_num_sta", "8" },
{ "ieee80211ac", NULL },
{ "vht_oper_chwidth", NULL },
{ "chanlist", NULL },
{ "ht_capab", NULL },
{ "ieee80211h", NULL },
{ "enable_wigig_softap", NULL },
{ "interface", NULL },
};
struct Command qsap_str[eSTR_LAST] = {
{ "wpa", NULL },
{ "accept_mac_file", NULL },
{ "deny_mac_file", NULL },
{ "gAPMacAddr", "00deadbeef04" },/** AP MAC address */
{ "gEnableApProt", "1" },/** protection flag in ini file */
{ "gFixedRate", "0" },/** Fixed rate in ini */
{ "gTxPowerCap", "27" },/** Tx power in ini */
{ "gFragmentationThreshold", "2346" },/** Fragmentation threshold in ini */
{ "RTSThreshold", "2347" },/** RTS threshold in ini */
{ "gAPCntryCode", "USI" },/** Country code in ini */
{ "gDisableIntraBssFwd", "0" },/** Intra-bss forward in ini */
{ "WmmIsEnabled", "0" },/** WMM */
{ "g11dSupportEnabled", "1" },/** 802.11d support */
{ "ieee80211n", NULL },
{ "ctrl_interface", NULL },
{ "interface", NULL },
{ "eap_server", NULL },
{ "gAPAutoShutOff", "0" },
{ "gEnablePhyAgcListenMode", "128" },
};
/** Supported operating mode */
char *hw_mode[HW_MODE_UNKNOWN] = {
"b", "g", "n", "g-only", "n-only", "a"
};
/** configuration file path */
char *pconffile = CONFIG_FILE;
char *fIni = WIFI_DRIVER_CONF_FILE;
s8 ini_file[PROPERTY_VALUE_MAX] = {0};
static int qsap_scnprintf(char *str, size_t size, const char *format, ...)
{
va_list arg_ptr;
int ret = 0;
if (size < 1)
return 0;
va_start(arg_ptr, format);
ret = vsnprintf(str, size, format, arg_ptr);
va_end(arg_ptr);
return (ret < (int)size) ? ret : (int)(size - 1);
}
/**
* @brief
* For a give configuration parameter, read the configuration value from the file.
* @param pfile [IN] configuration file path
* @param pcmd [IN] pointer to the comand structure
* @param presp [OUT] buffer to store the configuration value
* @param plen [IN-OUT] The length of the buffer is provided as input.
* The length of the configuration parameter value, stored
* in the 'presp', is provided as the output
* @param ignore_comment [IN] if set, read the commented value also
* @return void
*/
static s32 qsap_read_cfg(s8 *pfile, struct Command * pcmd, s8 *presp, u32 *plen, s8 *var, s32 ignore_comment)
{
FILE *fcfg;
s8 buf[MAX_CONF_LINE_LEN];
u16 len;
s8 *val;
/** Open the configuration file */
fcfg = fopen(pfile, "r");
if(NULL == fcfg) {
ALOGE("%s : unable to open file \n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
return eERR_FILE_OPEN;
}
/** Read the line from the configuration file */
len = strlen(pcmd->name);
while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fcfg)) {
s8 *pline = buf;
/** Skip the commented lines */
if(buf[0] == '#') {
if (ignore_comment) {
pline++;
}
else continue;
}
/** Identify the configuration parameter in the configuration file */
if(!strncmp(pline, pcmd->name, len) && (pline[len] == '=')) {
int tmp_indx;
/* Delate all \r \n combinations infront of the config string */
tmp_indx = strlen(buf)-1;
while( (buf[tmp_indx] == '\r') || (buf[tmp_indx] == '\n') ) tmp_indx--;
buf[tmp_indx+1] = '\0';
if ( NULL != var ) {
val = strchr(pline, '=');
if(NULL == val)
break;
*plen = qsap_scnprintf(presp, *plen, "%s %s%s", SUCCESS, var, val);
}
else {
*plen = qsap_scnprintf(presp, *plen, "%s %s", SUCCESS, pline);
}
fclose(fcfg);
return eSUCCESS;
}
}
#if 0
/** Configuration parameter is absent in the file */
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_FEATURE_NOT_ENABLED);
#else
/** Value not found in the configuration file */
/** Send the default value, if we are reading from ini file */
if ( pcmd->default_value ) {
*plen = qsap_scnprintf(presp, *plen, "%s %s=%s", SUCCESS, var?var:pcmd->name, pcmd->default_value);
fclose(fcfg);
return eSUCCESS;
}
else {
/** Configuration parameter is absent in the file */
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_FEATURE_NOT_ENABLED);
}
#endif
fclose(fcfg);
return eERR_CONFIG_PARAM_MISSING;
}
/**
* @brief
* Write the configuration parameter value into the configuration file.
* @param pfile [IN] configuration file path.
* @param pcmd [IN] command name
* @param pVal [IN] configuration parameter to be written to the file.
* @param presp [OUT] buffer to store the configuration value.
* @param plen [IN-OUT] The length of the buffer is provided as input.
* The length of the configuration parameter value, stored
* in the 'presp', is provided as the output
* @return void
*/
static s32 qsap_write_cfg(s8 *pfile, struct Command * pcmd, s8 *pVal, s8 *presp, u32 *plen, s32 inifile)
{
FILE *fcfg, *ftmp;
s8 buf[MAX_CONF_LINE_LEN+1];
s16 len, result = FALSE;
ALOGD("cmd=%s, Val:%s, INI:%ld \n", pcmd->name, pVal, inifile);
/** Open the configuration file */
fcfg = fopen(pfile, "r");
if(NULL == fcfg) {
ALOGE("%s : unable to open file \n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
return eERR_FILE_OPEN;
}
qsap_scnprintf(buf, sizeof(buf), "%s~", pfile);
/** Open a temporary file */
ftmp = fopen(buf, "w+");
if(NULL == ftmp) {
ALOGE("%s : unable to open tmp file \n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
fclose(fcfg);
return eERR_FILE_OPEN;
}
/** Read the values from the configuration file */
len = strlen(pcmd->name);
while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fcfg)) {
s8 *pline = buf;
/** commented line */
if(buf[0] == '#')
pline++;
/** Identify the configuration parameter to be updated */
if((!strncmp(pline, pcmd->name, len)) && (result == FALSE)) {
if(pline[len] == '=') {
qsap_scnprintf(buf, sizeof(buf), "%s=%s\n", pcmd->name, pVal);
result = TRUE;
ALOGD("Updated:%s\n", buf);
}
}
if(inifile && (!strncmp(pline, "END", 3)))
break;
fprintf(ftmp, "%s", buf);
}
if (result == FALSE) {
/* Configuration line not found */
/* Add the new line at the end of file */
qsap_scnprintf(buf, sizeof(buf), "%s=%s\n", pcmd->name, pVal);
fprintf(ftmp, "%s", buf);
ALOGD("Adding a new line in %s file: [%s] \n", inifile ? "inifile" : "hostapd.conf", buf);
}
if(inifile) {
gIniUpdated = 1;
fprintf(ftmp, "END\n");
while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fcfg))
fprintf(ftmp, "%s", buf);
}
fclose(fcfg);
fclose(ftmp);
qsap_scnprintf(buf, sizeof(buf), "%s~", pfile);
/** Restore the updated configuration file */
result = rename(buf, pfile);
*plen = qsap_scnprintf(presp, *plen, "%s", (result == eERR_UNKNOWN) ? ERR_FEATURE_NOT_ENABLED : SUCCESS);
/** Remove the temporary file. Dont care the return value */
unlink(buf);
/* chmod is needed because open() didn't set permisions properly */
if (chmod(pfile, 0660) < 0) {
ALOGE("Error changing permissions of %s to 0660: %s",
pfile, strerror(errno));
unlink(pfile);
return -1;
}
if(result == eERR_UNKNOWN)
return eERR_FEATURE_NOT_ENABLED;
return eSUCCESS;
}
/**
* @brief Read the security mode set in the configuration
* @param pfile [IN] configuration file path.
* @param presp [OUT] buffer to store the security mode.
* @param plen [IN-OUT] The length of the buffer is provided as input.
* The length of the security mode value, stored
* in the 'presp', is provided as the output
* @return void
*/
static sec_mode_t qsap_read_security_mode(s8 *pfile, s8 *presp, u32 *plen)
{
sec_mode_t mode;
u32 temp = *plen;
/** Read the WEP default key */
qsap_read_cfg(pfile, &cmd_list[eCMD_DEFAULT_KEY], presp, plen, NULL, GET_ENABLED_ONLY);
if ( !strcmp(presp, ERR_FEATURE_NOT_ENABLED) ) {
*plen = temp;
/* WEP, is not enabled */
/** Read WPA security status */
qsap_read_cfg(pfile, &qsap_str[STR_WPA], presp, plen, NULL, GET_ENABLED_ONLY);
if ( !strcmp(presp, ERR_FEATURE_NOT_ENABLED) ) {
/** WPA is disabled, No security */
mode = SEC_MODE_NONE;
}
else {
/** WPA, WPA2 or WPA-WPA2 mixed security */
s8 * ptmp = presp;
while((*plen)-- && (*ptmp++ != '=') );
mode = *plen ? (
*ptmp == '1' ? SEC_MODE_WPA_PSK :
*ptmp == '2' ? SEC_MODE_WPA2_PSK :
*ptmp == '3' ? SEC_MODE_WPA_WPA2_PSK : SEC_MODE_INVALID ): SEC_MODE_INVALID;
}
}
else {
/** Verify if, WPA is disabled */
*plen = temp;
qsap_read_cfg(pfile, &qsap_str[STR_WPA], presp, plen, NULL, GET_ENABLED_ONLY);
if ( !strcmp(presp, ERR_FEATURE_NOT_ENABLED) ) {
/** WPA is disabled, hence WEP is enabled */
mode = SEC_MODE_WEP;
}
else {
*plen = qsap_scnprintf(presp, temp, "%s", ERR_UNKNOWN);
return SEC_MODE_INVALID;
}
}
if(mode != SEC_MODE_INVALID) {
*plen = qsap_scnprintf(presp, temp,"%s %s=%d", SUCCESS, cmd_list[eCMD_SEC_MODE].name, mode);
}
else {
*plen = qsap_scnprintf(presp, temp,"%s", ERR_NOT_SUPPORTED);
}
return mode;
}
/**
* @brief
* Enable or disable a configuration parameter in the configuration file.
* @param pfile [IN] configuration file name
* @param pcmd [IN] configuration command structure
* @param status [IN] status to be set. The valid values are 'ENABLE' or 'DISABLE'
* @return On success, return 0
* On failure, return -1
*/
static s32 qsap_change_cfg(s8 *pfile, struct Command * pcmd, u32 status)
{
FILE *fcfg, *ftmp;
s8 buf[MAX_CONF_LINE_LEN+1];
u16 len;
/** Open the configuartion file */
fcfg = fopen(pfile, "r");
if(NULL == fcfg) {
ALOGE("%s : unable to open file \n", __func__);
return eERR_UNKNOWN;
}
qsap_scnprintf(buf, sizeof(buf), "%s~", pfile);
/** Open a temporary file */
ftmp = fopen(buf, "w");
if(NULL == ftmp) {
ALOGE("%s : unable to open tmp file \n", __func__);
fclose(fcfg);
return eERR_UNKNOWN;
}
/** Read the configuration parameters from the configuration file */
len = strlen(pcmd->name);
while(NULL != fgets(buf+1, MAX_CONF_LINE_LEN, fcfg)) {
s8 *p = buf+1;
/** Commented line */
if(p[0] == '#')
p++;
/** Identify the configuration parameter */
if(!strncmp(p, pcmd->name, len)) {
if(p[len] == '=') {
if(status == DISABLE) {
fprintf(ftmp, "#%s", p);
}
else {
fprintf(ftmp, "%s", p);
}
continue;
}
}
fprintf(ftmp, "%s", buf+1);
}
fclose(fcfg);
fclose(ftmp);
qsap_scnprintf(buf, sizeof(buf), "%s~", pfile);
/** Restore the new configuration file */
if(eERR_UNKNOWN == rename(buf, pfile)) {
ALOGE("unable to rename the file \n");
return eERR_UNKNOWN;
}
/** Delete the temporary file */
unlink(buf);
/* chmod is needed because open() didn't set permisions properly */
if (chmod(pfile, 0660) < 0) {
ALOGE("Error changing permissions of %s to 0660: %s",
pfile, strerror(errno));
unlink(pfile);
return -1;
}
return 0;
}
/**
* @brief
* Set the security mode in the configuration. The security mode
* can be :
* 1. No security
* 2. WEP
* 3. WPA
* 4. WPA2
* 5. WPA and WPA2 mixed mode
* @param pfile [IN] configuration file name
* @param sec_mode [IN] security mode to be set
* @param presp [OUTPUT] presp The command output format :
* On success,
* success <cmd>=<value>
* On failure,
* failure <error message>
* @param plen [IN-OUT] plen
* [IN] The length of the buffer, presp
* [OUT] The length of the response in the buffer, presp
* @return void
*/
static void qsap_set_security_mode(s8 *pfile, u32 sec_mode, s8 *presp, u32 *plen)
{
s16 wep, wpa;
s8 sec[MAX_INT_STR];
s32 rsn_status = DISABLE;
s32 ret = eERR_UNKNOWN;
/** Is valid security mode ? */
if(sec_mode >= SEC_MODE_INVALID) {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN);
return;
}
/** No security */
if(SEC_MODE_NONE == sec_mode) {
wep = DISABLE;
wpa = DISABLE;
}
/** WEP security */
else if(SEC_MODE_WEP == sec_mode) {
wep = ENABLE;
wpa = DISABLE;
}
else {
/** WPA, WPA2 and mixed-mode security */
u16 wpa_val;
u32 tmp = *plen;
wep = DISABLE;
wpa = ENABLE;
if(sec_mode == SEC_MODE_WPA_PSK)
wpa_val = WPA_IN_CONF_FILE;
else if(sec_mode == SEC_MODE_WPA2_PSK) {
wpa_val = WPA2_IN_CONF_FILE;
rsn_status = ENABLE;
}
else if(sec_mode == SEC_MODE_WPA_WPA2_PSK) {
wpa_val = WPA_WPA2_IN_CONF_FILE;
rsn_status = ENABLE;
}
qsap_scnprintf(sec, sizeof(sec), "%u", wpa_val);
qsap_write_cfg(pfile, &qsap_str[STR_WPA], sec, presp, plen, HOSTAPD_CONF_QCOM_FILE);
*plen = tmp;
}
/** The configuration parameters for the security to be set are enabled
* and the configuration parameters for the other security types are
* disabled in the configuration file
*/
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_DEFAULT_KEY], wep)) {
ALOGE("%s: wep_default_key error\n", __func__);
goto end;
}
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WEP_KEY0], wep)) {
ALOGE("%s: CMD_WEP_KEY0 \n", __func__);
goto end;
}
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WEP_KEY1], wep)) {
ALOGE("%s: CMD_WEP_KEY1 \n", __func__);
goto end;
}
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WEP_KEY2], wep)) {
ALOGE("%s: CMD_WEP_KEY2 \n", __func__);
goto end;
}
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WEP_KEY3], wep)) {
ALOGE("%s: CMD_WEP_KEY3 \n", __func__);
goto end;
}
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_PASSPHRASE], wpa)) {
ALOGE("%s: Passphrase error\n", __func__);
goto end;
}
if((sec_mode != SEC_MODE_NONE) && (sec_mode != SEC_MODE_WEP)) {
u32 state = !rsn_status;
if(sec_mode == SEC_MODE_WPA_WPA2_PSK) state = ENABLE;
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_WPA_PAIRWISE], state)) {
ALOGE("%s: WPA Pairwise\n", __func__);
goto end;
}
}
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &cmd_list[eCMD_RSN_PAIRWISE], rsn_status)) {
ALOGE("%s: WPA2 Pairwise\n", __func__);
goto end;
}
if(eERR_UNKNOWN == qsap_change_cfg(pfile, &qsap_str[STR_WPA], wpa)) {
ALOGE("%s: WPA\n", __func__);
goto end;
}
ret = eSUCCESS;
end:
*plen = qsap_scnprintf(presp, *plen, "%s", (ret == eSUCCESS) ? SUCCESS : ERR_UNKNOWN);
return;
}
/**
* @brief
* Get the file path having the allow or deny MAC address list
* @param pcfgfile [IN] configuration file name
* @param pcmd [IN] pcmd pointer to the command string
* @param pfile [OUT] buffer to store the return value, containing the file name
* or the error message.
* @param plen [IN-OUT] size of the buffer 'pfile', is provided as input and
* the length of the file name is returned as output
* @return
* On success, a pointer to the file name in the buffer 'pfile'.
* On failure, NULL is returned
*/
static s8 *qsap_get_allow_deny_file_name(s8 *pcfgfile, struct Command * pcmd, s8 *pfile, u32 *plen)
{
if(eSUCCESS == qsap_read_cfg(pcfgfile, pcmd, pfile, plen, NULL, GET_ENABLED_ONLY)) {
return strchr(pfile, '=') + 1;
}
return NULL;
}
int qsap_hostd_exec(int argc, char ** argv) {
#define MAX_CMD_SIZE 256
char qcCmdBuf[MAX_CMD_SIZE], *pCmdBuf;
u32 len = MAX_CMD_SIZE;
int i = 2, ret;
if ( argc < 4 ) {
ALOGD("failure: invalid arguments");
return -1;
}
argc -= 2;
pCmdBuf = qcCmdBuf;
while (argc--) {
ret = snprintf(pCmdBuf, len, " %s", argv[i]);
if ((ret < 0) || (ret >= (int)len)) {
/* Error case */
/* TODO: Command too long send the error message */
*pCmdBuf = '\0';
break;
}
ALOGD("argv[%d] (%s)",i, argv[i]);
pCmdBuf += ret;
len -= ret;
i++;
}
ALOGD("QCCMD data (%s)", pCmdBuf);
len = MAX_CMD_SIZE;
qsap_hostd_exec_cmd(qcCmdBuf, qcCmdBuf, (u32*)&len);
return 0;
}
/** Function to identify a valid MAC address */
static int isValid_MAC_address(char *pMac)
{
int i, len;
len = strlen(pMac);
if(len < MAC_ADDR_LEN)
return FALSE;
for(i=0; i<MAC_ADDR_LEN; i++) {
switch(i) {
case 2: case 5: case 8: case 11: case 14:
if(pMac[i] != ':')
return FALSE;
break;
default:
if(isxdigit(pMac[i]) == 0)
return FALSE;
}
}
return TRUE;
}
/**
* @brief
* Add a given MAC address to the allow or deny MAC list file.
* A maximum of 15 MAC addresses are allowed in the list. If the input
* MAC addresses are more than the allowed number, then the allowed number
* of MAC addresses are updated to the MAC list file and the remaining
* MAC addresses are discarded
*
* @param pfile [IN] Path of the allow or deny MAC list file
* @param pVal [IN] A string containing one or more MAC addresses. Multiple
* MAC addresses are separated by a SPACE separator
* Ex. "11:22:33:44:55:66 77:88:99:00:88:00"
* @param presp [OUT] buffer to store the response
* @param plen [IN-OUT] The length of the buffer 'presp' is provided as input.
* The length of the response, stored in buffer 'presp' is
* provided as output.
* @return void
*/
static void qsap_add_mac_to_file(s8 *pfile, s8 *pVal, s8 *presp, u32 *plen)
{
s32 len;
s16 num_macs = 0;
s8 buf[32];
s8 macbuf[32];
FILE *fp;
/** Create the file if it does not exists and open it for reading */
fp = fopen(pfile, "a");
if(NULL != fp) {
fclose(fp);
fp = fopen(pfile, "r+");
}
if(NULL == fp) {
ALOGE("%s : unable to open the file \n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
return;
}
/** count the MAC address in the MAC list file */
while(NULL != (fgets(buf, 32, fp))) {
num_macs++;
}
/** Evaluate the allowed limit */
if(num_macs >= MAX_ALLOWED_MAC) {
ALOGE("%s : File is full\n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN);
fclose(fp);
return;
}
/** Update all the input MAC addresses into the MAC list file */
len = strlen(pVal);
while(len > 0) {
int i = 0;
/** Get a MAC address from the input string */
while((*pVal != ' ' ) && (*pVal != '\0')) {
macbuf[i] = *pVal;
i++;
pVal++;
if(i == MAC_ADDR_LEN)
break;
}
macbuf[i] = '\0';
pVal++;
/** Is valid MAC address input ? */
if(TRUE == isValid_MAC_address(macbuf)) {
/** Append the MAC address to the file */
fprintf(fp, "%s\n", macbuf);
num_macs++;
/** Evaluate with the allowed limit */
if(num_macs == MAX_ALLOWED_MAC) {
ALOGE("MAC file is full now.... \n");
break;
}
}
len -= strlen(macbuf);
if(*pVal != '\0')
len--;
}
fclose(fp);
*plen = qsap_scnprintf(presp, *plen, "%s", SUCCESS);
return;
}
/**
* @brief
* Remove one or more MAC addresses from the allow or deny MAC list file.
* @param pfile [IN] path of the allow or deny list file.
* @param pVal [IN] a list of MAC addresses to be removed from the MAC list file.
* @param presp [OUT] the buffer to store the response
* @param plen [IN-OUT] The length of the 'presp' buffer is provided as input.
* The lenght of the response, stored in 'presp', is
* provided as output
* @return void
*/
static void qsap_remove_from_file(s8 *pfile, s8 *pVal, s8 *presp, u32 *plen)
{
FILE *fp;
FILE *ftmp;
s8 buf[MAX_CONF_LINE_LEN];
int status;
/** Open the allow or deny MAC list file */
fp = fopen(pfile, "r+");
if(NULL == fp) {
ALOGE("%s : unable to open the file \n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
return;
}
qsap_scnprintf(buf, sizeof(buf), "%s~", pfile);
/** Open a temporary file */
ftmp = fopen(buf, "w");
if(ftmp == NULL) {
ALOGE("%s : unable to open the file \n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
fclose(fp);
return;
}
/** Read all the MAC addresses from the file */
while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fp)) {
s8 *plist;
s32 slen;
int write_back = 1;
plist = pVal;
slen = strlen(pVal);
/** Compare each MAC address in the file with all the
* input MAC addresses */
write_back = 1;
while(slen > 0) {
if(0 == strncmp(buf, plist, MAC_ADDR_LEN)) {
write_back = 0;
break;
}
while((*plist != ' ') && (*plist != '\0')) {
plist++;
slen--;
}
while(((*plist == ' ') || (*plist == '\t')) && (*plist != '\0')) {
plist++; slen--;
}
}
/** Update the file */
if(write_back) {
fprintf(ftmp, "%s", buf);
}
}
fclose(fp);
fclose(ftmp);
qsap_scnprintf(buf, sizeof(buf), "%s~", pfile);
/** Restore the configuration file */
status = rename(buf, pfile);
qsap_scnprintf(presp, *plen, "%s", (status == eERR_UNKNOWN) ? ERR_FEATURE_NOT_ENABLED : SUCCESS);
unlink(buf);
return;
}
/**
* @brief
* Identify the MAC list file and the type of updation on the file.
* The MAC list file can be : Allow file or Deny file.
* The type of operation is : Add to file or Delete from file
*
* @param file [IN] path of the allow or deny MAC list file.
* @param cNum [IN] command number to 'type of file' and the 'type of updation'
* to be done.
* @param pVal [IN] A list of one or more MAC addresses. Multiple MAC addresses
* are separated by a SPACE character
* @param presp [OUT] Buffer to store the command response
* @param plen [IN-OUT] The length of the 'presp' buffer is provided as input
* The length of the response, stored in the 'presp' is provided
* as the output
* @return void
*/
static void qsap_update_mac_list(s8 *pfile, esap_cmd_t cNum, s8 *pVal, s8 *presp, u32 *plen)
{
ALOGD("%s : Updating file %s \n", __func__, pfile);
switch(cNum) {
case eCMD_ADD_TO_ALLOW:
case eCMD_ADD_TO_DENY:
qsap_add_mac_to_file(pfile, pVal, presp, plen);
break;
case eCMD_REMOVE_FROM_ALLOW:
case eCMD_REMOVE_FROM_DENY:
qsap_remove_from_file(pfile, pVal, presp, plen);
break;
default:
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN);
return;
}
return;
}
/**
* @brief
* @param fconfig [INPUT] configuration file name
* @param cNum [INPUT] command number. The valid command numbers supported by
* this function are :
* eCMD_ALLOW_LIST - Get the MAC address list from the allow list
* eCMD_DENY_LIST - Get the MAC address list from the deny list
* @param presp [OUTPUT] presp The command output format :
* On success,
* success <cmd>=<value>
* On failure,
* failure <error message>
* @param plen [IN-OUT] plen
* [IN] The length of the buffer, presp
* [OUT] The length of the response in the buffer, presp
* @return void
**/
static void qsap_get_mac_list(s8 *fconfile, esap_cmd_t cNum, s8 *presp, u32 *plen)
{
s8 buf[MAX_CONF_LINE_LEN];
FILE *fp;
u32 len_remain;
s8 *pfile, *pOut;
esap_cmd_t sNum;
int cnt = 0;
/** Identify the allow or deny file */
if(eCMD_ALLOW_LIST == cNum) {
sNum = STR_ACCEPT_MAC_FILE;
}
else if(eCMD_DENY_LIST == cNum) {
sNum = STR_DENY_MAC_FILE;
}
else {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN);
return;
}
/** Get the MAC allow or MAC deny file path */
len_remain = MAX_CONF_LINE_LEN;
if(NULL == (pfile = qsap_get_allow_deny_file_name(fconfile, &qsap_str[sNum], buf, &len_remain))) {
ALOGE("%s:Unknown error\n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
return;
}
/** Open allow / deny file, and read the MAC addresses */
fp = fopen(pfile, "r");
if(NULL == fp) {
ALOGE("%s: file open error\n",__func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
return;
}
*plen -= qsap_scnprintf(presp, *plen, "%s %s=", SUCCESS, cmd_list[cNum].name);
pOut = presp + strlen(presp);
/** Read the MAC address from the MAC allow or deny file */
while(NULL != (fgets(buf, MAX_CONF_LINE_LEN, fp))) {
u32 len;
/** Avoid the commented lines */
if(buf[0] == '#')
continue;
if(FALSE == isValid_MAC_address(buf))
continue;
buf[strlen(buf)-1] = '\0';
if(*plen < strlen(buf)) {
*pOut = '\0';
break;
}
len = qsap_scnprintf(pOut, *plen, "%s ", buf);
cnt++;
if (cnt >= MAX_ALLOWED_MAC) {
break;
}
pOut += len;
*plen -= len;
}
*plen = strlen(presp);
fclose(fp);
return;
}
static int qsap_read_mac_address(s8 *presp, u32 *plen)
{
char *ptr;
char mac[MAC_ADDR_LEN];
u32 len, i;
int nRet = eERR_INVALID_MAC_ADDR;
len = *plen;
if(eSUCCESS != qsap_read_cfg(fIni, &qsap_str[STR_MAC_IN_INI], presp, plen, cmd_list[eCMD_MAC_ADDR].name, GET_ENABLED_ONLY)) {
ALOGE("%s :MAC addr read failure \n",__func__);
goto end;
}
ptr = strchr(presp, '=');
if(NULL == ptr)
goto end;
strlcpy(mac, ptr+1, MAC_ADDR_LEN);
*plen = qsap_scnprintf(presp, len, "%s %s=", SUCCESS, cmd_list[eCMD_MAC_ADDR].name);
ptr = presp + strlen(presp);
len -= strlen(presp); /* decrease the total available buf length */
for(i=0; i<MAC_ADDR_LEN-5; i+=2) {
u32 tlen;
tlen = qsap_scnprintf(ptr, len, "%c%c:", mac[i], mac[i+1]);
*plen += tlen;
ptr += tlen;
len -= tlen; /* decrease the available len of ptr */
}
presp[*plen-1] = '\0';
(*plen)--;
ptr = strchr(presp, '=');
if(NULL == ptr)
goto end;
ptr++;
ALOGD("MAC :%s \n", ptr);
if(TRUE == isValid_MAC_address(ptr)) {
nRet = eSUCCESS;
}
else {
ALOGE("Invalid MAC in conf file \n");
}
end:
return nRet;
}
s8 *qsap_get_config_value(s8 *pfile, struct Command *pcmd, s8 *pbuf, u32 *plen)
{
s8 *ptr = NULL;
if(eSUCCESS == qsap_read_cfg(pfile, pcmd, pbuf, (u32 *)plen, NULL, GET_ENABLED_ONLY)) {
ptr = strchr(pbuf, '=');
if(NULL != ptr){
ptr++;
}
else {
ALOGE("Invalid entry, %s\n", pcmd->name);
}
}
return ptr;
}
static void qsap_read_wps_state(s8 *presp, u32 *plen)
{
u32 tlen = *plen;
s32 status;
s8 *pstate;
if(NULL == (pstate = qsap_get_config_value(pconffile, &cmd_list[eCMD_WPS_STATE], presp, &tlen))) {
/** unable to read the wps configuration, WPS is disabled !*/
ALOGD("%s :wps_state not in cfg file \n", __func__);
status = DISABLE;
}
else {
status = (atoi(pstate) == WPS_STATE_ENABLE) ? ENABLE : DISABLE;
}
*plen = qsap_scnprintf(presp, *plen, "success %s=%ld", cmd_list[eCMD_WPS_STATE].name, status);
return;
}
/**
* Get the channel being used in the soft AP.
*/
int qsap_get_operating_channel(s32 *pchan)
{
int sock;
struct iwreq wrq;
s8 interface[MAX_CONF_LINE_LEN];
u32 len = MAX_CONF_LINE_LEN;
s8 *pif;
int ret;
if(ENABLE != is_softap_enabled()) {
goto error;
}
if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) {
ALOGE("%s :interface error \n", __func__);
goto error;
}
interface[len] = '\0';
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0) {
ALOGE("%s :socket error \n", __func__);
goto error;
}
*pchan = 0;
strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name));
wrq.u.data.length = sizeof(s32);
wrq.u.data.pointer = pchan;
wrq.u.data.flags = 0;
ret = ioctl(sock, QCSAP_IOCTL_GET_CHANNEL, &wrq);
if(ret < 0) {
ALOGE("%s: ioctl failure \n",__func__);
close(sock);
goto error;
}
ALOGE("Recv len :%d \n", wrq.u.data.length);
*pchan = *(int *)(&wrq.u.name[0]);
ALOGE("Operating channel :%ld \n", *pchan);
close(sock);
return eSUCCESS;
error:
*pchan = 0;
ALOGE("%s: Failed to read channel \n", __func__);
return eERR_CHAN_READ;
}
/**
* Get the sap auto channel selection for soft AP.
*/
int qsap_get_sap_auto_channel_selection(s32 *pautochan)
{
int sock;
struct iwreq wrq;
s8 interface[MAX_CONF_LINE_LEN];
u32 len = MAX_CONF_LINE_LEN;
s8 *pif;
int ret;
sap_auto_channel_info sap_autochan_info;
s32 *pchan;
if(ENABLE != is_softap_enabled()) {
ALOGE("%s :is_softap_enabled() goto error \n", __func__);
goto error;
}
if(NULL == (pif = qsap_get_config_value(pconffile,
&qsap_str[STR_INTERFACE], interface, &len))) {
ALOGD("%s :interface error \n", __func__);
goto error;
}
interface[len] = '\0';
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0) {
ALOGD("%s :socket error \n", __func__);
goto error;
}
*pautochan = 0;
memset(&sap_autochan_info, 0, sizeof(sap_autochan_info));
memset(&wrq, 0, sizeof(wrq));
strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name));
wrq.u.data.length = sizeof(s32);
sap_autochan_info.subioctl = QCSAP_PARAM_GET_AUTO_CHANNEL;
wrq.u.data.pointer = pautochan;
memcpy(wrq.u.name, (char *)(&sap_autochan_info), sizeof(sap_autochan_info));
wrq.u.data.flags = 0;
ret = ioctl(sock, QCSAP_IOCTL_GETPARAM, &wrq);
if(ret < 0) {
ALOGE("%s: ioctl failure \n",__func__);
close(sock);
goto error;
}
ALOGD("Recv len :%d \n", wrq.u.data.length);
*pautochan = *(int *)(&wrq.u.name[0]);
ALOGD("Sap auto channel selection pautochan=%ld \n", *pautochan);
close(sock);
return eSUCCESS;
error:
*pautochan = 0;
ALOGE("%s: Failed to read sap auto channel selection\n", __func__);
return eERR_GET_AUTO_CHAN;
}
/**
* Get the mode of operation.
*/
int qsap_get_mode(s32 *pmode)
{
int sock;
struct iwreq wrq;
s8 interface[MAX_CONF_LINE_LEN];
u32 len = MAX_CONF_LINE_LEN;
s8 *pif;
int ret;
sap_auto_channel_info sap_autochan_info;
s32 *pchan;
*pmode = -1;
if(NULL == (pif = qsap_get_config_value(pconffile,
&qsap_str[STR_INTERFACE], interface, &len))) {
ALOGD("%s :interface error \n", __func__);
goto error;
}
interface[len] = '\0';
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0) {
ALOGD("%s :socket error \n", __func__);
goto error;
}
memset(&wrq, 0, sizeof(wrq));
strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name));
ret = ioctl(sock, SIOCGIWMODE, &wrq);
if(ret < 0) {
ALOGE("%s: ioctl failure \n",__func__);
close(sock);
goto error;
}
*pmode = *(s32 *)(&wrq.u.mode);
ALOGE("%s: ioctl Get Mode = %d \n",__func__, (int)*pmode);
close(sock);
return eSUCCESS;
error:
*pmode = -1;
ALOGE("%s: (Failure) ioctl Get Mode = %d \n",__func__, (int)*pmode);
return eERR_UNKNOWN;
}
/**
* Set the channel Range for soft AP.
*/
int qsap_set_channel_range(s8 *buf)
{
int sock;
struct iwreq wrq;
s8 interface[MAX_CONF_LINE_LEN];
u32 len = MAX_CONF_LINE_LEN;
s8 *pif;
s8 *temp;
int ret, i;
sap_channel_info sap_chan_range;
sta_channel_info sta_chan_range;
ALOGE("buf :%s\n", buf);
temp = buf;
temp = strchr(buf, '=');
if (NULL == temp) {
goto error;
}
temp++;
if (NULL == (pif = qsap_get_config_value(pconffile,
&qsap_str[STR_INTERFACE], interface, &len))) {
ALOGE("%s :interface error\n", __func__);
goto error;
}
interface[len] = '\0';
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
ALOGE("%s :socket error\n", __func__);
goto error;
}
memset(&sap_chan_range, 0, sizeof(sap_chan_range));
memset(&sta_chan_range, 0, sizeof(sta_chan_range));
memset(&wrq, 0, sizeof(wrq));
if (ENABLE != is_softap_enabled()) {
strlcpy(wrq.ifr_name, "wlan0", sizeof(wrq.ifr_name));
sta_chan_range.subioctl = WE_SET_SAP_CHANNELS;
ret = sscanf(temp, "%d %d %d", &(sta_chan_range.stastartchan),
&(sta_chan_range.staendchan), &(sta_chan_range.staband));
if (3 != ret) {
ALOGE("%s : sscanf is not successful\n", __func__);
close(sock);
goto error;
}
memcpy(wrq.u.name, (char *)(&sta_chan_range), sizeof(sta_chan_range));
ALOGE("%s :Softap is off,Send SET_CHANNEL_RANGE over sta interface\n",
__func__);
ret = ioctl(sock, WLAN_PRIV_SET_THREE_INT_GET_NONE, &wrq);
} else {
strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name));
ret = sscanf(temp, "%d %d %d", &(sap_chan_range.startchan),
&(sap_chan_range.endchan), &(sap_chan_range.band));
if (3 != ret) {
ALOGE("%s : sscanf is not successful\n", __func__);
close(sock);
goto error;
}
memcpy(wrq.u.name, (char *)(&sap_chan_range), sizeof(sap_chan_range));
ALOGE("%s :SAP is on,Send SET_CHANNEL_RANGE over softap interface\n",
__func__);
ret = ioctl(sock, QCSAP_IOCTL_SET_CHANNEL_RANGE, &wrq);
}
if (ret < 0) {
ALOGE("%s: ioctl failure\n", __func__);
close(sock);
goto error;
}
ALOGE("Recv len :%d\n", wrq.u.data.length);
close(sock);
return eSUCCESS;
error:
ALOGE("%s: Failed to set channel range\n", __func__);
return eERR_SET_CHAN_RANGE;
}
int qsap_read_channel(s8 *pfile, struct Command *pcmd, s8 *presp, u32 *plen, s8 *pvar)
{
s8 *pval;
s32 chan;
u32 len = *plen;
if(eSUCCESS == qsap_get_operating_channel(&chan)) {
*plen = qsap_scnprintf(presp, len, "%s %s=%lu", SUCCESS, pcmd->name, chan);
ALOGD("presp :%s\n", presp);
} else {
*plen = qsap_scnprintf(presp, len, "%s", ERR_UNKNOWN);
}
return eSUCCESS;
}
int qsap_read_auto_channel(struct Command *pcmd, s8 *presp, u32 *plen)
{
s8 *pval, *pautoval;
s32 pautochan;
u32 len = *plen;
int autochan;
ALOGE("%s :\n", __func__);
if (eSUCCESS == qsap_get_sap_auto_channel_selection(&pautochan)) {
*plen = qsap_scnprintf(presp, len, "%s autochannel=%lu", SUCCESS, pautochan);
ALOGE("presp :%s\n", presp);
} else {
*plen = qsap_scnprintf(presp, len, "%s", ERR_UNKNOWN);
}
return eSUCCESS;
}
static int qsap_mac_to_macstr(s8 *pmac, u32 slen, s8 *pmstr, u32 *plen)
{
int len;
int totlen = 0;
while((slen > 0) && (*plen > 0)) {
len = qsap_scnprintf(pmstr, *plen, "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X ", (int)pmac[0], (int)pmac[1], (int)pmac[2],
(int)pmac[3], (int)pmac[4], (int)pmac[5]);
pmac += 6;
slen -= 6;
*plen -= len;
pmstr += len;
totlen += len;
}
if(totlen > 0) {
*pmstr--;
totlen--;
}
*pmstr = '\0';
*plen = totlen;
return 0;
}
#define MAX_STA_ALLOWED 8
void qsap_get_associated_sta_mac(s8 *presp, u32 *plen)
{
int sock, ret;
struct iwreq wrq;
s8 interface[MAX_CONF_LINE_LEN];
u32 len = MAX_CONF_LINE_LEN;
s8 *pif;
s8 *pbuf, *pout;
u32 buflen;
u32 recvLen;
u32 tlen;
if(ENABLE != is_softap_enabled()) {
goto error;
}
if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) {
ALOGE("%s :interface error \n", __func__);
goto error;
}
interface[len] = '\0';
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0) {
ALOGE("%s :socket failure \n", __func__);
goto error;
}
/* response has length field + 6 bytes per STA */
buflen = sizeof(u32) + (MAX_STA_ALLOWED * 6);
pbuf = (s8 *)malloc(buflen);
if(NULL == pbuf) {
ALOGE("%s :No memory \n", __func__);
close(sock);
goto error;
}
#define SIZE_OF_MAC_INT (6)
strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name));
wrq.u.data.length = buflen;
wrq.u.data.pointer = (void *)pbuf;
wrq.u.data.flags = 0;
ret = ioctl(sock, QCSAP_IOCTL_ASSOC_STA_MACADDR, &wrq);
if(ret < 0) {
ALOGE("%s :ioctl failure \n", __func__);
free(pbuf);
close(sock);
goto error;
}
recvLen = *(u32 *)(wrq.u.data.pointer);
recvLen -= sizeof(u32);
len = qsap_scnprintf(presp, *plen, "%s %s=", SUCCESS, cmd_list[eCMD_ASSOC_STA_MACS].name);
pout = presp + len;
tlen = *plen - len;
qsap_mac_to_macstr(pbuf+sizeof(u32), recvLen, pout, &tlen);
*plen = len + tlen;
free(pbuf);
close(sock);
return;
error:
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN);
return;
}
static void qsap_read_wep_key(s8 *pfile, struct Command *pcmd, s8 *presp, u32 *plen, s8 *var)
{
s8 *pwep;
s8 *pkey;
if(eSUCCESS != qsap_read_cfg(pfile, pcmd, presp, plen, var, GET_COMMENTED_VALUE))
return;
pwep = strchr(presp, '=');
if(NULL == pwep)
return;
pwep++;
if(pwep[0] == '"') {
pkey = pwep;
pwep++;
while(*pwep != '\0') {
*pkey = *pwep;
pkey++;
pwep++;
}
*pkey--;
*pkey = '\0';
*plen -= 2;
}
return;
}
void qsap_read_ap_stats(s8 *presp, u32 *plen)
{
int sock, ret;
struct iwreq wrq;
s8 interface[MAX_CONF_LINE_LEN];
u32 len = MAX_CONF_LINE_LEN;
s8 *pif;
s8 *pbuf, *pout;
u32 tlen;
if(ENABLE != is_softap_enabled()) {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_SOFTAP_NOT_STARTED);
return;
}
if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) {
ALOGE("%s :interface error \n", __func__);
goto error;
}
interface[len] = '\0';
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0) {
ALOGE("%s :socket failure \n", __func__);
goto error;
}
pbuf = (s8 *)malloc(MAX_RESP_LEN);
if(NULL == pbuf) {
ALOGE("%s :No memory \n", __func__);
close(sock);
goto error;
}
strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name));
wrq.u.data.length = MAX_RESP_LEN;
wrq.u.data.pointer = (void *)pbuf;
wrq.u.data.flags = 0;
ret = ioctl(sock, QCSAP_IOCTL_AP_STATS, &wrq);
if(ret < 0) {
ALOGE("%s :ioctl failure \n", __func__);
free(pbuf);
close(sock);
goto error;
}
*plen = qsap_scnprintf(presp, *plen, "%s %s=%s", SUCCESS, cmd_list[eCMD_AP_STATISTICS].name, pbuf);
free(pbuf);
close(sock);
return;
error:
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN);
return;
}
void qsap_read_autoshutoff(s8 *presp, u32 *plen)
{
u32 tlen, time = 0;
s8 *ptime;
tlen = *plen;
if(NULL == (ptime = qsap_get_config_value(fIni, &qsap_str[STR_AP_AUTOSHUTOFF], presp, &tlen))) {
/** unable to read the AP shutoff time */
ALOGE("%s :Failed to read AP shutoff time\n", __func__);
}
else {
time = atoi(ptime);
time = time / 60; /** Convert seconds to minutes */
}
*plen = qsap_scnprintf(presp, *plen, "success %s=%ld", cmd_list[eCMD_AP_AUTOSHUTOFF].name, time);
return;
}
/**
* @brief
* Get the configuration information from the softAP configuration
* files
* @param cNum [INPUT]
* @param presp [OUTPUT] presp The command output format :
* On success,
* success <cmd>=<value>
* On failure,
* failure <error message>
* @param plen [IN-OUT] plen
* [IN] The length of the buffer, presp
* [OUT] The length of the response in the buffer, presp
* @return void
**/
static void qsap_get_from_config(esap_cmd_t cNum, s8 *presp, u32 *plen)
{
u32 len;
int status;
s8 * pval;
switch(cNum) {
case eCMD_ENABLE_SOFTAP:
status = is_softap_enabled();
*plen = qsap_scnprintf(presp, *plen, "%s %s=%d", SUCCESS, cmd_list[cNum].name, status);
break;
case eCMD_WPA_PAIRWISE:
case eCMD_RSN_PAIRWISE:
case eCMD_DEFAULT_KEY:
case eCMD_PASSPHRASE:
case eCMD_GTK_TIMEOUT:
qsap_read_cfg(pconffile, &cmd_list[cNum], presp, plen, NULL, GET_COMMENTED_VALUE);
break;
case eCMD_SSID:
case eCMD_BSSID:
case eCMD_BCN_INTERVAL:
case eCMD_DTIM_PERIOD:
case eCMD_HW_MODE:
case eCMD_AUTH_ALGS:
case eCMD_MAC_ACL:
case eCMD_WPS_CONFIG_METHOD:
case eCMD_UUID:
case eCMD_DEVICE_NAME:
case eCMD_MANUFACTURER:
case eCMD_MODEL_NAME:
case eCMD_MODEL_NUMBER:
case eCMD_SERIAL_NUMBER:
case eCMD_DEVICE_TYPE:
case eCMD_OS_VERSION:
case eCMD_FRIENDLY_NAME:
case eCMD_MANUFACTURER_URL:
case eCMD_MODEL_DESC:
case eCMD_MODEL_URL:
case eCMD_UPC:
case eCMD_SDK_VERSION:
case eCMD_COUNTRY_CODE:
qsap_read_cfg(pconffile, &cmd_list[cNum], presp, plen, NULL, GET_ENABLED_ONLY);
break;
case eCMD_WEP_KEY0:
case eCMD_WEP_KEY1:
case eCMD_WEP_KEY2:
case eCMD_WEP_KEY3:
qsap_read_wep_key(pconffile, &cmd_list[cNum], presp, plen, NULL);
break;
case eCMD_CHAN:
qsap_read_channel(pconffile, &cmd_list[cNum], presp, plen, NULL);
break;
case eCMD_FRAG_THRESHOLD:
case eCMD_REGULATORY_DOMAIN:
case eCMD_RTS_THRESHOLD:
case eCMD_IEEE80211H:
qsap_read_cfg(pconffile, &cmd_list[cNum], presp, plen, NULL, GET_ENABLED_ONLY);
break;
case eCMD_ALLOW_LIST: /* fall through */
case eCMD_DENY_LIST:
qsap_get_mac_list(pconffile, cNum, presp, plen);
break;
case eCMD_SEC_MODE:
qsap_read_security_mode(pconffile, presp, plen);
break;
case eCMD_MAC_ADDR:
if(eSUCCESS != qsap_read_mac_address(presp, plen)) {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_NOT_SUPPORTED);
}
break;
case eCMD_WMM_STATE:
qsap_read_cfg(pconffile, &cmd_list[cNum], presp, plen, NULL, GET_ENABLED_ONLY);
break;
case eCMD_WPS_STATE:
qsap_read_wps_state(presp, plen);
break;
case eCMD_PROTECTION_FLAG:
qsap_read_cfg(fIni, &qsap_str[STR_PROT_FLAG_IN_INI], presp, plen, cmd_list[eCMD_PROTECTION_FLAG].name, GET_ENABLED_ONLY);
break;
case eCMD_DATA_RATES:
qsap_read_cfg(fIni, &qsap_str[STR_DATA_RATE_IN_INI], presp, plen, cmd_list[eCMD_DATA_RATES].name, GET_ENABLED_ONLY);
break;
case eCMD_ASSOC_STA_MACS:
qsap_get_associated_sta_mac(presp, plen);
break;
case eCMD_TX_POWER:
qsap_read_cfg(fIni, &qsap_str[STR_TX_POWER_IN_INI], presp, plen, cmd_list[eCMD_TX_POWER].name, GET_ENABLED_ONLY);
break;
case eCMD_INTRA_BSS_FORWARD:
qsap_read_cfg(pconffile, &cmd_list[eCMD_INTRA_BSS_FORWARD], presp, plen, NULL, GET_ENABLED_ONLY);
break;
case eCMD_AP_STATISTICS:
qsap_read_ap_stats(presp, plen);
break;
case eCMD_AP_AUTOSHUTOFF:
qsap_read_autoshutoff(presp, plen);
break;
case eCMD_AP_ENERGY_DETECT_TH:
qsap_read_cfg(fIni, &qsap_str[STR_AP_ENERGY_DETECT_TH], presp, plen, cmd_list[eCMD_AP_ENERGY_DETECT_TH].name, GET_ENABLED_ONLY);
break;
case eCMD_GET_AUTO_CHANNEL:
qsap_read_auto_channel(&cmd_list[cNum],presp, plen);
break;
default:
/** Error case */
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_ARG);
}
len = *plen-1;
/** Remove the space or tabs in the end of response */
while(len) {
if((presp[len] == ' ') || (presp[len] == '\t'))
len--;
else
break;
}
presp[len+1] = '\0';
*plen = len+1;
return;
}
/**
* @brief
* Identify the command number corresponding to the input user command.
* @param cName [INPUT] command name
* @return
* On success,
* command number in the range 0 to (eCMD_INVALID-1)
* On failure,
* eCMD_INVALID
**/
static esap_cmd_t qsap_get_cmd_num(s8 *cName)
{
s16 i, len;
for(i=0; i<eCMD_LAST; i++) {
len = strlen(cmd_list[i].name);
if(!strncmp(cmd_list[i].name, cName, len)) {
if((cName[len] == '=') || (cName[len] == '\0'))
return i;
}
}
return eCMD_INVALID;
}
/**
* @brief
* Handle the user requests of the form,
* "get <cmd num> [<value1> ...]"
* These commands are used to retreive the soft AP
* configuration information
*
* @param pcmd [IN] pointer to the structure, storing the command.
* @param presp [OUT] pointer to the buffer, to store the command response.
* The command output format :
* On success,
* success <cmd>=<value>
* On failure,
* failure <error message>
* @param plen [IN-OUT]
* [IN] : Maximum length of the reponse buffer
* [OUT]: Reponse length
* @return
* void
*/
static void qsap_handle_get_request(s8 *pcmd, s8 *presp, u32 *plen)
{
esap_cmd_t cNum;
pcmd += strlen("get");
SKIP_BLANK_SPACE(pcmd);
cNum = qsap_get_cmd_num(pcmd);
if(cNum == eCMD_INVALID) {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM);
return;
}
qsap_get_from_config(cNum, presp, plen);
return;
}
static s16 is_valid_wep_key(s8 *pwep, s8 *pkey, s16 len)
{
int weplen;
s16 ret = TRUE;
int ascii = FALSE;
weplen = strlen(pwep);
/** Remove the double quotes if any */
if((pwep[0] == '"') && (pwep[weplen-1] == '"')) {
pwep[weplen-1] = '\0';
pwep++;
weplen -= 2;
}
/** The WEP key should be of length 5, 13 or 16 characters
* or 10, 26, or 32 digits */
switch(weplen) {
case WEP_64_KEY_ASCII:
case WEP_128_KEY_ASCII:
case WEP_152_KEY_ASCII:
weplen--;
while(weplen--) {
if(0 == isascii(pwep[weplen])) {
ALOGD("%c not ascii \n", pwep[weplen]);
return FALSE;
}
}
ascii = TRUE;
break;
case WEP_64_KEY_HEX:
case WEP_128_KEY_HEX:
case WEP_152_KEY_HEX:
while(weplen--) {
if(0 == isxdigit(pwep[weplen]))
return FALSE;
}
break;
default:
ret = FALSE;
}
qsap_scnprintf(pkey, len, (ascii == TRUE) ? "\"%s\"" : "%s", pwep);
return ret;
}
s16 wifi_qsap_reset_to_default(s8 *pcfgfile, s8 *pdefault)
{
FILE *fcfg, *ftmp;
char buf[MAX_CONF_LINE_LEN];
int status = eSUCCESS;
fcfg = fopen(pdefault, "r");
if(NULL == fcfg) {
ALOGE("%s : unable to open file \n", __func__);
return eERR_FILE_OPEN;
}
qsap_scnprintf(buf, sizeof(buf), "%s~", pcfgfile);
ftmp = fopen(buf, "w+");
if(NULL == ftmp) {
ALOGE("%s : unable to open file \n", __func__);
fclose(fcfg);
return eERR_FILE_OPEN;
}
while(NULL != fgets(buf, MAX_CONF_LINE_LEN, fcfg)) {
fprintf(ftmp, "%s", buf);
}
fclose(fcfg);
fclose(ftmp);
qsap_scnprintf(buf, sizeof(buf), "%s~", pcfgfile);
if(eERR_UNKNOWN == rename(buf, pcfgfile))
status = eERR_CONF_FILE;
/** Remove the temporary file. Dont care the return value */
unlink(buf);
return status;
}
#define CTRL_IFACE_PATH_LEN (128)
void qsap_del_ctrl_iface(void)
{
u32 len;
s8 dst_path[CTRL_IFACE_PATH_LEN], *pcif, *pif;
s8 interface[64];
s8 path[CTRL_IFACE_PATH_LEN + 64];
len = CTRL_IFACE_PATH_LEN;
if(NULL == (pcif = qsap_get_config_value(pconffile, &qsap_str[STR_CTRL_INTERFACE], dst_path, &len))) {
ALOGE("%s :ctrl_iface path error \n", __func__);
goto error;
}
len = 64;
if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) {
ALOGE("%s :interface error \n", __func__);
goto error;
}
if ((int)sizeof(path) <= snprintf(path, sizeof(path), "%s/%s", pcif, pif)) {
ALOGE("Iface path : truncating error, %s \n", path);
goto error;
}
unlink(path);
error:
return;
}
static int qsap_send_cmd_to_hostapd(s8 *pcmd)
{
int sock;
struct sockaddr_un cli;
struct sockaddr_un ser;
struct timeval timeout;
int ret = eERR_SEND_TO_HOSTAPD;
u32 len;
fd_set read;
s8 dst_path[CTRL_IFACE_PATH_LEN], *pcif, *pif;
s8 interface[64];
s8 *ptr;
u32 retry_cnt = 3;
size_t ptr_size;
len = CTRL_IFACE_PATH_LEN;
ptr_size = (sizeof(ser.sun_path) < CTRL_IFACE_PATH_LEN)?
CTRL_IFACE_PATH_LEN : sizeof(ser.sun_path);
ptr = malloc(ptr_size);
if(NULL == ptr) {
ALOGE("%s :No memory \n", __func__);
return ret;
}
if(NULL == (pcif = qsap_get_config_value(pconffile, &qsap_str[STR_CTRL_INTERFACE], dst_path, &len))) {
ALOGE("%s :ctrl_iface path error \n", __func__);
goto error;
}
len = 64;
if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], interface, &len))) {
ALOGE("%s :interface error \n", __func__);
goto error;
}
if ((int)sizeof(ser.sun_path) <= snprintf(ptr, sizeof(ser.sun_path), "%s/%s", pcif, pif)) {
/* the sun_path is truncated. */
ALOGE("Iface path : truncating error, %s \n", ptr);
goto error;
}
ALOGD("Connect to :%s\n", ptr);
sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if(sock < 0) {
ALOGE("%s :Socket error \n", __func__);
goto error;
}
cli.sun_family = AF_UNIX;
qsap_scnprintf(cli.sun_path, sizeof(cli.sun_path), SDK_CTRL_IF);
ret = bind(sock, (struct sockaddr *)&cli, sizeof(cli));
if(ret < 0) {
ALOGE("Bind Failure\n");
goto close_ret;
}
ser.sun_family = AF_UNIX;
qsap_scnprintf(ser.sun_path, sizeof(ser.sun_path), "%s", ptr);
ALOGD("Connect to: %s,(%d)\n", ser.sun_path, sock);
ret = connect(sock, (struct sockaddr *)&ser, sizeof(ser));
if(ret < 0) {
ALOGE("Connect Failure...\n");
goto close_ret;
}
ret = send(sock, pcmd, strlen(pcmd), 0);
if(ret < 0) {
ALOGE("Unable to send cmd to hostapd \n");
goto close_ret;
}
/* reset the ptr buffer size for recv */
len = ptr_size;
#define HOSTAPD_RECV_TIMEOUT (2)
while(1) {
timeout.tv_sec = HOSTAPD_RECV_TIMEOUT;
timeout.tv_usec = 0;
FD_ZERO(&read);
FD_SET(sock, &read);
ret = select(sock+1, &read, NULL, NULL, &timeout);
if(FD_ISSET(sock, &read)) {
ret = recv(sock, ptr, len, 0);
if(ret < 0) {
ALOGE("%s: recv() failed \n", __func__);
goto close_ret;
}
if ((u32)ret >= len)
ptr[len-1] = 0;
else
ptr[ret] = 0;
if((ret > 0) && (ptr[0] == '<')) {
ALOGE("Not the expected response...\n: %s", ptr);
retry_cnt--;
if(retry_cnt)
continue;
break;
}
if(!strncmp(ptr, "FAIL", 4)) {
ALOGE("Command failed in hostapd \n");
goto close_ret;
}
else {
break;
}
}
else {
ALOGE("%s: Select failed \n", __func__);
goto close_ret;
}
}
ret = eSUCCESS;
close_ret:
close(sock);
error:
free(ptr);
unlink(SDK_CTRL_IF);
return ret;
}
static void qsap_update_wps_config(s8 *pVal, s8 *presp, u32 *plen)
{
u32 tlen = *plen;
s32 status;
s8 pwps_state[MAX_INT_STR+1];
s32 i;
/* Enable/disable the following in hostapd.conf
* 1. Update the wps_state
* 2. Set eap_server=1
* 3. Update UPnP related variables
*/
status = atoi(pVal);
qsap_scnprintf(pwps_state, sizeof(pwps_state), "%d", (status == ENABLE) ? WPS_STATE_ENABLE : WPS_STATE_DISABLE);
qsap_write_cfg(pconffile, &cmd_list[eCMD_WPS_STATE], pwps_state, presp, &tlen, HOSTAPD_CONF_QCOM_FILE);
if(eERR_UNKNOWN == qsap_change_cfg(pconffile, &cmd_list[eCMD_WPS_STATE], status)) {
ALOGE("%s: Failed to enable %s\n", __func__, cmd_list[eCMD_WPS_STATE].name);
goto error;
}
qsap_scnprintf(pwps_state, sizeof(pwps_state), "%d", ENABLE);
/** update the eap_server=1 */
qsap_write_cfg(pconffile, &qsap_str[STR_EAP_SERVER], pwps_state, presp, plen, HOSTAPD_CONF_QCOM_FILE);
for(i=eCMD_UUID; i<=eCMD_UPC; i++) {
if(eERR_UNKNOWN == qsap_change_cfg(pconffile, &cmd_list[i], status)) {
ALOGE("%s: failed to set %s\n", __func__, cmd_list[i].name);
goto error;
}
}
return;
error:
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN);
return;
}
static void qsap_config_wps_method(s8 *pVal, s8 *presp, u32 *plen)
{
s8 buf[64];
s8 *ptr;
int i;
s32 value;
/** INPUT : <0/1> <PIN> */
/** PBC method : WPS_PBC */
/** PIN method : WPS_PIN any <key> */
ptr = pVal;
i = 0;
while((*ptr != '\0') && (*ptr != ' ')) {
buf[i] = *ptr;
ptr++;
i++;
}
buf[i] = '\0';
/** Identify the WPS method */
value = atoi(buf);
if(TRUE != IS_VALID_WPS_CONFIG(value)) {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM);
return;
}
SKIP_BLANK_SPACE(ptr);
if( (value == WPS_CONFIG_PIN) && (*ptr == '\0') ){
ALOGE("%s :Invalid command \n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM);
return;
}
if(value == WPS_CONFIG_PBC)
qsap_scnprintf(buf, sizeof(buf), "WPS_PBC");
else {
if(strlen(ptr) < WPS_KEY_LEN) {
ALOGD("%s :Invalid WPS key length\n", __func__);
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM);
return;
}
qsap_scnprintf(buf, sizeof(buf), "WPS_PIN any %s", ptr);
}
value = qsap_send_cmd_to_hostapd(buf);
*plen = qsap_scnprintf(presp, *plen, "%s", (value == eSUCCESS) ? SUCCESS: ERR_UNKNOWN);
return;
}
s32 atoh(u8 *str)
{
u32 val = 0;
u32 pos = 0;
s32 len = strlen((char *)str) - 1;
while(len >= 0) {
switch(str[len]) {
case '0' ... '9':
val += (str[len] - '0') << pos;
break;
case 'a' ... 'f':
val += (str[len] - 'a' + 10) << pos;
break;
case 'A'... 'F':
val += (str[len] - 'A' + 10) << pos;
break;
}
len--;
pos += 4;
}
return val;
}
int qsap_get_mac_in_bytes(char *psmac, char *pbmac)
{
int val;
u8 str[3];
u32 i;
str[2] = '\0';
if(FALSE == isValid_MAC_address(psmac)) {
return FALSE;
}
for(i=0; i<strlen(psmac); i++) {
if(psmac[i] == ':')
continue;
str[0] = psmac[i];
str[1] = psmac[i+1];
val = atoh(str);
*pbmac = val;
pbmac++;
i += 2;
}
*pbmac = 0;
return TRUE;
}
void qsap_disassociate_sta(s8 *pVal, s8 *presp, u32 *plen)
{
int sock, ret = eERR_UNKNOWN;
struct iwreq wrq;
s8 pbuf[MAX_CONF_LINE_LEN];
u32 len = MAX_CONF_LINE_LEN;
s8 *pif;
if(ENABLE != is_softap_enabled()) {
goto end;
}
if(NULL == (pif = qsap_get_config_value(pconffile, &qsap_str[STR_INTERFACE], pbuf, &len))) {
ALOGE("%s :interface error \n", __func__);
goto end;
}
pbuf[len] = '\0';
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0) {
ALOGE("%s: socket failure \n", __func__);
goto end;
}
strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name));
if (TRUE != qsap_get_mac_in_bytes(pVal, (char *) &wrq.u)) {
ALOGE("%s: Invalid input \n", __func__);
close(sock);
goto end;
}
ret = ioctl(sock, QCSAP_IOCTL_DISASSOC_STA, &wrq);
if(ret < 0) {
ALOGE("%s: ioctl failure \n", __func__);
}
close(sock);
end:
*plen = qsap_scnprintf(presp, *plen, "%s", (ret == eSUCCESS) ? SUCCESS : ERR_UNKNOWN);
return;
}
static int qsap_set_channel(s32 channel, s8 *tbuf, u32 *tlen)
{
u32 ulen;
s8 *pcfgval;
s8 schan[MAX_INT_STR+1];
s8 *pcfg = pconffile;
ulen = *tlen;
/** Read the current operating mode */
if(NULL == (pcfgval = qsap_get_config_value(pconffile, &cmd_list[eCMD_HW_MODE], tbuf, &ulen))) {
return eERR_UNKNOWN;
}
/** If the operating mode is 'A' and the channel to be set is in between 1 and 14
* then change the operating mode to 'G' mode */
if((!strcmp(hw_mode[HW_MODE_A], pcfgval)) && (channel <=14)) {
/** Change the operating mode to 'G' */
ulen = *tlen;
if(eSUCCESS != qsap_write_cfg(pcfg, &cmd_list[eCMD_HW_MODE], hw_mode[HW_MODE_G], tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE)) {
ALOGE("%s :Unable to update the operating mode \n", __func__);
return eERR_UNKNOWN;
}
}
/** If the operating mode is NOT 'B' and the channel to be set is 14
* then change the operating mode to 'B' mode */
if(strcmp(hw_mode[HW_MODE_B], pcfgval) && (channel == 14)) {
/** Change the operating mode to 'B' */
ulen = *tlen;
if(eSUCCESS != qsap_write_cfg(pcfg, &cmd_list[eCMD_HW_MODE], hw_mode[HW_MODE_B], tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE)) {
ALOGE("%s :Unable to update the operating mode \n", __func__);
return eERR_UNKNOWN;
}
}
if(channel > 14) {
/** Change the operating mode to 'A' */
ulen = *tlen;
if(eSUCCESS != qsap_write_cfg(pcfg, &cmd_list[eCMD_HW_MODE], hw_mode[HW_MODE_A], tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE)) {
ALOGE("%s :Unable to update the operating mode \n", __func__);
return eERR_UNKNOWN;
}
}
qsap_scnprintf(schan, sizeof(schan), "%ld", channel);
return qsap_write_cfg(pcfg, &cmd_list[eCMD_CHAN], schan, tbuf, tlen, HOSTAPD_CONF_QCOM_FILE);
}
static int qsap_set_operating_mode(s32 mode, s8 *pmode, int pmode_len, s8 *tbuf, u32 *tlen)
{
u32 ulen;
s8 *pcfgval;
s32 channel;
s8 sconf[MAX_INT_STR+1];
s8 *pcfg = pconffile;
s32 rate_idx;
s8 ieee11n_enable[] = "1";
s8 ieee11n_disable[] = "0";
ulen = *tlen;
/** Update the operating mode */
qsap_change_cfg(pcfg, &cmd_list[eCMD_BASIC_RATES],DISABLE);
qsap_change_cfg(pcfg, &cmd_list[eCMD_REQUIRE_HT],DISABLE);
qsap_write_cfg(pcfg, &cmd_list[eCMD_IEEE80211N],ieee11n_disable, tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE);
switch(mode)
{
case HW_MODE_G_ONLY:
qsap_change_cfg(pcfg, &cmd_list[eCMD_BASIC_RATES],ENABLE);
break;
case HW_MODE_N_ONLY:
qsap_change_cfg(pcfg, &cmd_list[eCMD_REQUIRE_HT],ENABLE);
/* fall through */
case HW_MODE_N:
case HW_MODE_A:
ulen = *tlen;
qsap_write_cfg(pcfg, &cmd_list[eCMD_IEEE80211N],ieee11n_enable, tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE);
break;
case HW_MODE_B:
ulen = *tlen;
qsap_write_cfg(pcfg, &cmd_list[eCMD_IEEE80211N],ieee11n_disable, tbuf, &ulen, HOSTAPD_CONF_QCOM_FILE);
break;
}
if(mode == HW_MODE_G_ONLY || mode == HW_MODE_N_ONLY || mode == HW_MODE_N ) {
qsap_scnprintf(pmode, pmode_len, "%s",hw_mode[HW_MODE_G]);
}
return qsap_write_cfg(pcfg, &cmd_list[eCMD_HW_MODE], pmode, tbuf, tlen, HOSTAPD_CONF_QCOM_FILE);
}
static int qsap_set_data_rate(s32 drate_idx, s8 *presp, u32 *plen)
{
u32 ulen;
s8 *pmode;
s8 sconf[MAX_INT_STR+1];
int ret = eERR_UNKNOWN;
if(TRUE != IS_VALID_DATA_RATE_IDX(drate_idx)) {
ALOGE("%s :Invalid rate index \n", __func__);
goto end;
}
ulen = *plen;
/** Read the current operating mode */
if(NULL == (pmode = qsap_get_config_value(pconffile, &cmd_list[eCMD_HW_MODE], presp, &ulen))) {
ALOGE("%s :Unable to read mode \n", __func__);
goto end;
}
/** Validate the rate index against the current operating mode */
if(((!strcmp(pmode, hw_mode[HW_MODE_B])) && (drate_idx > B_MODE_MAX_DATA_RATE_IDX)) ||
((!strcmp(pmode, hw_mode[HW_MODE_G]) || (!strcmp(pmode, hw_mode[HW_MODE_G_ONLY]))) &&
(drate_idx > G_ONLY_MODE_MAX_DATA_RATE_IDX))) {
ALOGE("%s :Invalid rate index \n", __func__);
goto end;
}
qsap_scnprintf(sconf, sizeof(sconf), "%ld", drate_idx);
/** Update the rate index in the configuration */
return qsap_write_cfg(fIni, &qsap_str[STR_DATA_RATE_IN_INI], sconf, presp, plen, INI_CONF_FILE);
end:
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_UNKNOWN);
return ret;
}
/**
* @brief
* Handle the user requests of the form,
* "set <cmd num> <value1> ..."
* These commands are used to update the soft AP
* configuration information
*
* @param pcmd [IN] pointer to the string, storing the command.
* @param presp [OUT] pointer to the buffer, to store the command response.
* The command output format :
* On success,
* success
* On failure,
* failure <error message>
* @param plen [IN-OUT]
* [IN]: Maximum length of the reponse buffer
* [OUT]: Reponse length
* @return
* void
*/
static void qsap_handle_set_request(s8 *pcmd, s8 *presp, u32 *plen)
{
esap_cmd_t cNum;
esap_str_t sNum = STR_DENY_MAC_FILE;
s8 *pVal, *pfile;
s8 filename[MAX_FILE_PATH_LEN];
u32 ulen;
s32 status;
s32 value;
s16 ini = HOSTAPD_CONF_QCOM_FILE;
s8 *pcfg = pconffile;
pcmd += strlen("set");
SKIP_BLANK_SPACE(pcmd);
cNum = qsap_get_cmd_num(pcmd);
if(cNum == eCMD_INVALID) {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_ARG);
ALOGE("Invalid command number :%d\n", cNum);
return;
}
pVal = pcmd + strlen(cmd_list[cNum].name);
if( (cNum != eCMD_COMMIT) &&
(cNum != eCMD_RESET_TO_DEFAULT) &&
((*pVal != '=') || (strlen(pVal) < 2)) ) {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_ARG);
return;
}
pVal++;
if((cNum != eCMD_COMMIT) && (cNum != eCMD_RESET_TO_DEFAULT)) {
ALOGE("Cmd: %s Argument :%s \n", cmd_list[cNum].name, pVal);
}
switch(cNum) {
case eCMD_ADD_TO_ALLOW:
case eCMD_REMOVE_FROM_ALLOW:
sNum = STR_ACCEPT_MAC_FILE;
/* fall through */
case eCMD_ADD_TO_DENY:
case eCMD_REMOVE_FROM_DENY:
ulen = MAX_FILE_PATH_LEN;
if(NULL != (pfile = qsap_get_allow_deny_file_name(pconffile, &qsap_str[sNum], filename, &ulen))) {
qsap_update_mac_list(pfile, cNum, pVal, presp, plen);
}
else {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_RES_UNAVAILABLE);
}
return;
case eCMD_SEC_MODE:
value = atoi(pVal);
if(FALSE == IS_VALID_SEC_MODE(value))
goto error;
/** Write back the integer value. This is to avoid values like 01, 001, 0001
* being written to the configuration.
*/
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
qsap_set_security_mode(pconffile, value, presp, plen);
return;
case eCMD_MAC_ACL:
value = atoi(pVal);
if(FALSE == IS_VALID_MAC_ACL(value))
goto error;
/** Write back the integer value. This is to avoid values like 01, 001, 0001
* being written to the configuration
*/
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
if(ACL_ALLOW_LIST == value) {
value = ENABLE;
status = DISABLE;
}
else if(ACL_DENY_LIST == value){
value = DISABLE;
status = ENABLE;
}
else {
// must be ACL_ALLOW_AND_DENY_LIST
value = ENABLE;
status = ENABLE;
}
if(eERR_UNKNOWN != qsap_change_cfg(pconffile, &qsap_str[STR_ACCEPT_MAC_FILE], value)) {
if(eERR_UNKNOWN != qsap_change_cfg(pconffile, &qsap_str[STR_DENY_MAC_FILE], status))
{
qsap_write_cfg(pconffile, &cmd_list[cNum], pVal, presp, plen, HOSTAPD_CONF_QCOM_FILE);
}
else {
goto error;
}
}
else {
goto error;
}
return;
case eCMD_COMMIT:
#if 0 // COMMIT is not required currently for ICS framework
if ( gIniUpdated ) {
status = wifi_qsap_reload_softap();
gIniUpdated = 0;
}
else {
status = commit();
}
*plen = qsap_scnprintf(presp, *plen, "%s", (status == eSUCCESS)? SUCCESS : ERR_UNKNOWN);
#endif
*plen = qsap_scnprintf(presp, *plen, "%s", SUCCESS);
return;
case eCMD_ENABLE_SOFTAP:
value = atoi(pVal);
if(TRUE != IS_VALID_SOFTAP_ENABLE(value))
goto error;
if ( *pVal == '0' ) {
status = wifi_qsap_unload_driver();
}
else {
status = wifi_qsap_load_driver();
}
*plen = qsap_scnprintf(presp, *plen, "%s", (status==eSUCCESS) ? SUCCESS : "failure Could not enable softap");
return;
case eCMD_ENABLE_WIGIG_SOFTAP:
value = atoi(pVal);
if (TRUE != IS_VALID_SOFTAP_ENABLE(value))
goto error;
if ( *pVal == '0' ) {
status = wifi_qsap_stop_wigig_softap();
}
else {
status = wifi_qsap_start_wigig_softap();
}
*plen = qsap_scnprintf(presp, *plen, "%s", (status==eSUCCESS) ? SUCCESS : "failure Could not enable Wigig softap");
return;
case eCMD_SSID:
value = strlen(pVal);
if(SSD_MAX_LEN < value)
goto error;
break;
case eCMD_SET_MAX_CLIENTS:
value = strlen(pVal);
break;
case eCMD_BSSID:
value = atoi(pVal);
if(FALSE == IS_VALID_BSSID(value))
goto error;
/** Write back the integer value. This is to avoid values like 01, 001, 0001
* being written to the configuration
*/
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_PASSPHRASE:
value = strlen(pVal);
if(FALSE == IS_VALID_PASSPHRASE_LEN(value))
goto error;
break;
case eCMD_CHAN:
value = atoi(pVal);
ulen = MAX_FILE_PATH_LEN;
value = qsap_set_channel(value, filename, &ulen);
*plen = qsap_scnprintf(presp, *plen, "%s", (value == eSUCCESS) ? SUCCESS : ERR_UNKNOWN);
return;
case eCMD_BCN_INTERVAL:
value = atoi(pVal);
if(FALSE == IS_VALID_BEACON(value))
goto error;
/** Write back the integer value. This is to avoid values like 01, 001, 0001
* being written to the configuration
*/
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_DTIM_PERIOD:
value = atoi(pVal);
if(FALSE == IS_VALID_DTIM_PERIOD(value))
goto error;
/** Write back the integer value. This is to avoid values like 01, 001, 0001
* being written to the configuration
*/
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_HW_MODE:
status = FALSE;
for(value=HW_MODE_B; value<HW_MODE_UNKNOWN; value++) {
if(!strcmp(pVal, hw_mode[value])) {
status = TRUE;
break;
}
}
if(status == FALSE)
goto error;
ulen = MAX_FILE_PATH_LEN;
/* pVal is anull terminated string */
value = qsap_set_operating_mode(value, pVal, strlen(pVal)+1, filename, &ulen);
*plen = qsap_scnprintf(presp, *plen, "%s", (value == eSUCCESS) ? SUCCESS : ERR_UNKNOWN);
return;
case eCMD_AUTH_ALGS:
value = atoi(pVal);
if((value != AHTH_ALG_OPEN) && (value != AUTH_ALG_SHARED) &&
(value != AUTH_ALG_OPEN_SHARED))
goto error;
/** Write back the integer value. This is to avoid values like 01, 001, 0001
* being written to the configuration
*/
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_DEFAULT_KEY:
value = atoi(pVal);
if(FALSE == IS_VALID_WEP_KEY_IDX(value))
goto error;
/** Write back the integer value. This is to avoid values like 01, 001, 0001
* being written to the configuration
*/
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
qsap_write_cfg(pcfg, &cmd_list[cNum], pVal, presp, plen, ini);
ulen = MAX_FILE_PATH_LEN;
if(SEC_MODE_WEP != qsap_read_security_mode(pcfg, filename, &ulen)) {
if(eERR_UNKNOWN == qsap_change_cfg(pcfg, &cmd_list[cNum], 0)) {
ALOGE("%s: eCMD_DEFAULT_KEY \n", __func__);
goto error;
}
}
return;
case eCMD_WPA_PAIRWISE:
case eCMD_RSN_PAIRWISE:
if(FALSE == IS_VALID_PAIRWISE(pVal))
goto error;
/** If the encryption type is TKIP, disable the 802.11 HT */
value = 1;
if(!strcmp(pVal, "TKIP")) {
value = 0;
}
if(eERR_UNKNOWN == qsap_change_cfg(pconffile, &qsap_str[STR_HT_80211N], value)) {
ALOGE("%s: unable to update 802.11 HT\n", __func__);
goto error;
}
break;
case eCMD_WEP_KEY0:
case eCMD_WEP_KEY1:
case eCMD_WEP_KEY2:
case eCMD_WEP_KEY3:
if(FALSE == is_valid_wep_key(pVal, filename, MAX_FILE_PATH_LEN))
goto error;
qsap_write_cfg(pcfg, &cmd_list[cNum], filename, presp, plen, ini);
/** if the security mode is not WEP, update the WEP features, and
do NOT set the WEP security */
ulen = MAX_FILE_PATH_LEN;
if(SEC_MODE_WEP != qsap_read_security_mode(pcfg, filename, &ulen)) {
if(eERR_UNKNOWN == qsap_change_cfg(pcfg, &cmd_list[cNum], 0)) {
ALOGE("%s: CMD_WEP_KEY0 \n", __func__);
goto error;
}
}
return;
case eCMD_RESET_AP:
value = atoi(pVal);
ALOGE("Reset :%ld \n", value);
if(SAP_RESET_BSS == value) {
status = wifi_qsap_stop_softap();
if(status == eSUCCESS) {
status = wifi_qsap_start_softap();
if (eSUCCESS != status)
wifi_qsap_unload_driver();
}
}
else if(SAP_RESET_DRIVER_BSS == value){
status = wifi_qsap_reload_softap();
}
else if(SAP_STOP_BSS == value) {
status = wifi_qsap_stop_bss();
}
else if(SAP_STOP_DRIVER_BSS == value) {
status = wifi_qsap_stop_softap();
if(status == eSUCCESS)
status = wifi_qsap_unload_driver();
}
#ifdef QCOM_WLAN_CONCURRENCY
else if(SAP_INITAP == value) {
status = wifi_qsap_start_softap_in_concurrency();
}
else if(SAP_EXITAP == value) {
status = wifi_qsap_stop_softap_in_concurrency();
}
#endif
else {
status = !eSUCCESS;
}
*plen = qsap_scnprintf(presp, *plen, "%s", (status == eSUCCESS) ? SUCCESS : ERR_UNKNOWN);
return;
case eCMD_DISASSOC_STA:
qsap_disassociate_sta(pVal, presp, plen);
return;
case eCMD_RESET_TO_DEFAULT:
if(eSUCCESS == (status = wifi_qsap_reset_to_default(pconffile, DEFAULT_CONFIG_FILE_PATH))) {
if(eSUCCESS == (status = wifi_qsap_reset_to_default(fIni, DEFAULT_INI_FILE))) {
status = wifi_qsap_reload_softap();
}
}
*plen = qsap_scnprintf(presp, *plen, "%s", (status == eSUCCESS) ? SUCCESS : ERR_UNKNOWN);
return;
case eCMD_DATA_RATES:
value = atoi(pVal);
qsap_set_data_rate(value, presp, plen);
return;
case eCMD_UUID:
value = strlen(pVal);
if(TRUE != IS_VALID_UUID_LEN(value))
goto error;
break;
case eCMD_DEVICE_NAME:
value = strlen(pVal);
if(TRUE != IS_VALID_DEVICENAME_LEN(value))
goto error;
break;
case eCMD_MANUFACTURER:
value = strlen(pVal);
if(TRUE != IS_VALID_MANUFACTURER_LEN(value))
goto error;
break;
case eCMD_MODEL_NAME:
value = strlen(pVal);
if(TRUE != IS_VALID_MODELNAME_LEN(value))
goto error;
break;
case eCMD_MODEL_NUMBER:
value = strlen(pVal);
if(TRUE != IS_VALID_MODELNUM_LEN(value))
goto error;
break;
case eCMD_SERIAL_NUMBER:
value = strlen(pVal);
if(TRUE != IS_VALID_SERIALNUM_LEN(value))
goto error;
break;
case eCMD_DEVICE_TYPE:
value = strlen(pVal);
if(TRUE != IS_VALID_DEV_TYPE_LEN(value))
goto error;
break;
case eCMD_OS_VERSION:
value = strlen(pVal);
if(TRUE != IS_VALID_OS_VERSION_LEN(value))
goto error;
break;
case eCMD_FRIENDLY_NAME:
value = strlen(pVal);
if(TRUE != IS_VALID_FRIENDLY_NAME_LEN(value))
goto error;
break;
case eCMD_MANUFACTURER_URL:
case eCMD_MODEL_URL:
value = strlen(pVal);
if(TRUE != IS_VALID_URL_LEN(value))
goto error;
break;
case eCMD_MODEL_DESC:
value = strlen(pVal);
if(TRUE != IS_VALID_MODEL_DESC_LEN(value))
goto error;
break;
case eCMD_UPC:
value = strlen(pVal);
if(TRUE != IS_VALID_UPC_LEN(value))
goto error;
break;
case eCMD_WMM_STATE:
value = atoi(pVal);
if(TRUE != IS_VALID_WMM_STATE(value))
goto error;
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_WPS_STATE:
value = atoi(pVal);
if(TRUE != IS_VALID_WPS_STATE(value))
goto error;
/** Write back the integer value. This is to avoid values like 01, 001, 0001
* being written to the configuration
*/
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
qsap_update_wps_config(pVal, presp, plen);
return;
case eCMD_WPS_CONFIG_METHOD:
qsap_config_wps_method(pVal, presp, plen);
return;
case eCMD_PROTECTION_FLAG:
value = atoi(pVal);
if(TRUE != IS_VALID_PROTECTION(value))
goto error;
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
cNum = STR_PROT_FLAG_IN_INI;
ini = INI_CONF_FILE;
break;
case eCMD_FRAG_THRESHOLD:
value = atoi(pVal);
if(TRUE != IS_VALID_FRAG_THRESHOLD(value))
goto error;
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_REGULATORY_DOMAIN:
value = atoi(pVal);
if(TRUE != IS_VALID_802DOT11D_STATE(value))
goto error;
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_RTS_THRESHOLD:
value = atoi(pVal);
if(TRUE != IS_VALID_RTS_THRESHOLD(value))
goto error;
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_GTK_TIMEOUT:
value = atoi(pVal);
if(TRUE != IS_VALID_GTK(value))
goto error;
break;
case eCMD_TX_POWER:
value = atoi(pVal);
if(TRUE != IS_VALID_TX_POWER(value))
goto error;
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
cNum = STR_TX_POWER_IN_INI;
ini = INI_CONF_FILE;
break;
case eCMD_INTRA_BSS_FORWARD:
value = atoi(pVal);
if(TRUE != IS_VALID_INTRA_BSS_STATUS(value))
goto error;
if(DISABLE == value) {
status = qsap_change_cfg(pcfg,
&cmd_list[eCMD_INTRA_BSS_FORWARD],DISABLE);
}
else {
status = qsap_change_cfg(pcfg,
&cmd_list[eCMD_INTRA_BSS_FORWARD],ENABLE);
}
*plen = qsap_scnprintf(presp, *plen, "%s", (status == eSUCCESS) ?
SUCCESS : ERR_UNKNOWN);
return;
case eCMD_COUNTRY_CODE:
value = strlen(pVal);
if(value > CTRY_MAX_LEN )
goto error;
break;
case eCMD_AP_AUTOSHUTOFF:
value = atoi(pVal);
if(TRUE != IS_VALID_APSHUTOFFTIME(value))
goto error;
/* copy a larger value back to pVal. Please pay special care
* in caller to make sure that the buffer has sufficient size. */
qsap_scnprintf(pVal, MAX_INT_STR, "%ld", value*60);
cNum = STR_AP_AUTOSHUTOFF;
ini = INI_CONF_FILE;
break;
case eCMD_AP_ENERGY_DETECT_TH:
value = atoi(pVal);
if(TRUE != IS_VALID_ENERGY_DETECT_TH(value))
goto error;
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
cNum = STR_AP_ENERGY_DETECT_TH;
ini = INI_CONF_FILE;
break;
case eCMD_IEEE80211H:
value = atoi(pVal);
if(TRUE != IS_VALID_DFS_STATE(value))
goto error;
qsap_scnprintf(pVal, strlen(pVal)+1, "%ld", value);
break;
case eCMD_SET_CHANNEL_RANGE:
ALOGE("eCMD_SET_CHANNEL_RANGE pcmd :%s\n", pcmd);
value = qsap_set_channel_range(pcmd);
*plen = qsap_scnprintf(presp, *plen, "%s", (value == eSUCCESS) ? SUCCESS :
ERR_UNKNOWN);
return;
default: ;
/** Do not goto error, in default case */
}
if(ini == INI_CONF_FILE) {
ALOGD("WRITE TO INI FILE :%s\n", qsap_str[cNum].name);
qsap_write_cfg(fIni, &qsap_str[cNum], pVal, presp, plen, ini);
}
else {
qsap_write_cfg(pcfg, &cmd_list[cNum], pVal, presp, plen, ini);
}
return;
error:
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALID_PARAM);
return;
}
/**
* @brief
* Initiate the command and return response
* @param pcmd string containing the command request
* The format of the command is
* get param=value
* or
* set param=value
* @param presp buffer to store the command response
* @param plen length of the respone buffer
* @return
* void
*/
void qsap_hostd_exec_cmd(s8 *pcmd, s8 *presp, u32 *plen)
{
ALOGD("CMD INPUT [%s][%lu]\n", pcmd, *plen);
/* Skip any blank spaces */
SKIP_BLANK_SPACE(pcmd);
check_for_configuration_files();
if(fIni == NULL)
qsap_set_ini_filename();
if(!strncmp(pcmd, Cmd_req[eCMD_GET], strlen(Cmd_req[eCMD_GET])) && isblank(pcmd[strlen(Cmd_req[eCMD_GET])])) {
qsap_handle_get_request(pcmd, presp, plen);
}
else if(!(strncmp(pcmd, Cmd_req[eCMD_SET], strlen(Cmd_req[eCMD_SET]))) && isblank(pcmd[strlen(Cmd_req[eCMD_SET])]) ) {
qsap_handle_set_request(pcmd, presp, plen);
}
else {
*plen = qsap_scnprintf(presp, *plen, "%s", ERR_INVALIDREQ);
}
ALOGD("CMD OUTPUT [%s]\nlen :%lu\n\n", presp, *plen);
return;
}
/* netd and Froyo Native UI specific API */
#define DEFAULT_INTFERACE "wlan0"
#define DEFAULT_SSID "SOFTAP_SSID"
#define DEFAULT_CHANNEL 4
#define DEFAULT_PASSPHRASE "12345678"
#define DEFAULT_AUTH_ALG 1
#define RECV_BUF_LEN 255
#define CMD_BUF_LEN 255
/** Command input
argv[3] = SSID,
argv[4] = BROADCAST/HIDDEN,
argv[5] = CHANNEL
argv[6] = SECURITY,
argv[7] = KEY,
*/
int qsapsetSoftap(int argc, char *argv[])
{
char cmdbuf[CMD_BUF_LEN];
char respbuf[RECV_BUF_LEN];
u32 rlen = RECV_BUF_LEN;
int i;
int hidden = 0;
int sec = SEC_MODE_NONE;
ALOGD("%s, %s, %s, %d\n", __FUNCTION__, argv[0], argv[1], argc);
for ( i=0; i<argc;i++) {
ALOGD("ARG: %d - %s\n", i+1, argv[i]);
}
/* set interface */
if (argc > 2) {
snprintf(cmdbuf, CMD_BUF_LEN, "set interface=%s",argv[2]);
}
else {
snprintf(cmdbuf, CMD_BUF_LEN, "set interface=%s", DEFAULT_INTFERACE);
}
(void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen);
/** set SSID */
if(argc > 3) {
qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "set ssid=%s",argv[3]);
}
else {
qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "set ssid=%s_%d", DEFAULT_SSID, rand());
}
(void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen);
if(strncmp("success", respbuf, rlen) != 0) {
ALOGE("Failed to set ssid\n");
return eERR_UNKNOWN;
}
rlen = RECV_BUF_LEN;
if (argc > 4) {
if (strcmp(argv[4], "hidden") == 0) {
hidden = 1;
}
snprintf(cmdbuf, CMD_BUF_LEN, "set ignore_broadcast_ssid=%d", hidden);
(void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen);
if(strncmp("success", respbuf, rlen) != 0) {
ALOGE("Failed to set ignore_broadcast_ssid \n");
return -1;
}
}
/** channel */
rlen = RECV_BUF_LEN;
if(argc > 5) {
snprintf(cmdbuf, CMD_BUF_LEN, "set channel=%d", atoi(argv[5]));
(void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen);
if(strncmp("success", respbuf, rlen) != 0) {
ALOGE("Failed to set channel \n");
return -1;
}
}
/** Security */
rlen = RECV_BUF_LEN;
if(argc > 6) {
/**TODO : need to identify the SEC strings for "wep", "wpa", "wpa2" */
if(!strcmp(argv[6], "open"))
sec = SEC_MODE_NONE;
else if(!strcmp(argv[6], "wep"))
sec = SEC_MODE_WEP;
else if(!strcmp(argv[6], "wpa-psk"))
sec = SEC_MODE_WPA_PSK;
else if(!strcmp(argv[6], "wpa2-psk"))
sec = SEC_MODE_WPA2_PSK;
qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "set security_mode=%d",sec);
}
else {
qsap_scnprintf(cmdbuf, sizeof(cmdbuf) , "set security_mode=%d", DEFAULT_AUTH_ALG);
}
(void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen);
if(strncmp("success", respbuf, rlen) != 0) {
ALOGE("Failed to set security mode\n");
return -1;
}
/** Key -- passphrase */
rlen = RECV_BUF_LEN;
if ( (sec == SEC_MODE_WPA_PSK) || (sec == SEC_MODE_WPA2_PSK) ) {
if(argc > 7) {
/* If the input passphrase is more than 63 characters, consider first 63 characters only*/
if ( strlen(argv[7]) > 63 ) argv[7][63] = '\0';
qsap_scnprintf(cmdbuf, CMD_BUF_LEN, "set wpa_passphrase=%s",argv[7]);
}
else {
qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "set wpa_passphrase=%s", DEFAULT_PASSPHRASE);
}
}
(void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen);
if(strncmp("success", respbuf, rlen) != 0) {
ALOGE("Failed to set passphrase \n");
return -1;
}
rlen = RECV_BUF_LEN;
if(argc > 8) {
qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "set max_num_sta=%d",atoi(argv[8]));
}
(void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen);
if(strncmp("success", respbuf, rlen) != 0) {
ALOGE("Failed to set maximun client connections number \n");
return -1;
}
rlen = RECV_BUF_LEN;
qsap_scnprintf(cmdbuf, sizeof(cmdbuf), "set commit");
(void) qsap_hostd_exec_cmd(cmdbuf, respbuf, &rlen);
if(strncmp("success", respbuf, rlen) != 0) {
ALOGE("Failed to COMMIT \n");
return -1;
}
return 0;
}
static int check_for_config_file_size(FILE *fp)
{
int length = 0;
if( NULL != fp )
{
fseek(fp, 0L, SEEK_END);
length = ftell(fp);
}
return length;
}
void check_for_configuration_files(void)
{
FILE * fp;
char *pfile;
/* Check if configuration files are present, if not create the default files */
/* If configuration file does not exist copy the default file */
if ( NULL == (fp = fopen(CONFIG_FILE, "r")) ) {
wifi_qsap_reset_to_default(CONFIG_FILE, DEFAULT_CONFIG_FILE_PATH);
}
else {
/* The configuration file could be of 0 byte size, replace with default */
if (check_for_config_file_size(fp) <= 0)
wifi_qsap_reset_to_default(CONFIG_FILE, DEFAULT_CONFIG_FILE_PATH);
fclose(fp);
}
/* If Accept MAC list file does not exist, copy the default file */
if ( NULL == (fp = fopen(ACCEPT_LIST_FILE, "r")) ) {
wifi_qsap_reset_to_default(ACCEPT_LIST_FILE, DEFAULT_ACCEPT_LIST_FILE_PATH);
}
else {
/* The configuration file could be of 0 byte size, replace with default */
if (check_for_config_file_size(fp) <= 0)
wifi_qsap_reset_to_default(ACCEPT_LIST_FILE, DEFAULT_ACCEPT_LIST_FILE_PATH);
fclose(fp);
}
/* Provide read and write permissions to the owner */
pfile = ACCEPT_LIST_FILE;
if (chmod(pfile, 0660) < 0) {
ALOGE("Error changing permissions of %s to 0660: %s",
pfile, strerror(errno));
}
/* If deny MAC list file does not exist, copy the default file */
if ( NULL == (fp = fopen(DENY_LIST_FILE, "r")) ) {
wifi_qsap_reset_to_default(DENY_LIST_FILE, DEFAULT_DENY_LIST_FILE_PATH);
}
else {
/* The configuration file could be of 0 byte size, replace with default */
if (check_for_config_file_size(fp) <= 0)
wifi_qsap_reset_to_default(DENY_LIST_FILE, DEFAULT_DENY_LIST_FILE_PATH);
fclose(fp);
}
/* Provide read and write permissions to the owner */
pfile = DENY_LIST_FILE;
if (chmod(pfile, 0660) < 0) {
ALOGE("Error changing permissions of %s to 0660: %s",
pfile, strerror(errno));
}
return;
}
void qsap_set_ini_filename(void)
{
if (property_get("wlan.driver.config", ini_file, NULL)) {
fIni = ini_file;
ALOGE("INI FILE PROP PRESENT %s\n", fIni);
} else
ALOGE("INI FILE PROP NOT PRESENT: Use default path %s\n", fIni);
return;
}
// IOCTL command to create and delete bridge interface //
#ifndef SIOCBRADDBR
#define SIOCBRADDBR 0x89a0
#endif
#ifndef SIOCBRDELBR
#define SIOCBRDELBR 0x89a1
#endif
static int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
{
struct ifreq ifr;
int ret;
if (sock < 0)
return -1;
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
ret = errno ? -errno : -999;
ALOGE("Could not read interface %s flags: %s",
ifname, strerror(errno));
return ret;
}
if (dev_up) {
if (ifr.ifr_flags & IFF_UP)
return 0;
ifr.ifr_flags |= IFF_UP;
} else {
if (!(ifr.ifr_flags & IFF_UP))
return 0;
ifr.ifr_flags &= ~IFF_UP;
}
if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
ret = errno ? -errno : -999;
ALOGE("Could not set interface %s flags (%s): %s",
ifname, dev_up ? "UP" : "DOWN", strerror(errno));
return ret;
}
return 0;
}
int qsap_control_bridge(int argc, char ** argv)
{
int br_socket;
if (argc < 4) {
ALOGE("Command not supported");
return -1;
}
br_socket = socket(PF_INET, SOCK_DGRAM, 0);
if (br_socket < 0) {
ALOGE("socket(PF_INET,SOCK_DGRAM): %s",strerror(errno));
return -1;
}
if (!strncmp(argv[2],"create",6)) {
if (ioctl(br_socket, SIOCBRADDBR, argv[3]) < 0) {
ALOGE("Could not add bridge %s: %s", argv[3], strerror(errno));
return -1;
}
} else if (!strncmp(argv[2],"remove",6)) {
if (ioctl(br_socket, SIOCBRDELBR, argv[3]) < 0) {
ALOGE("Could not add remove %s: %s", argv[3], strerror(errno));
return -1;
}
} else if (!strncmp(argv[2],"up",2)) {
return linux_set_iface_flags(br_socket, argv[3], 1);
} else if (!strncmp(argv[2],"down",4)) {
return linux_set_iface_flags(br_socket, argv[3], 0);
} else {
ALOGE("Command %s not handled.", argv[2]);
return -1;
}
return 0;
}
int qsap_add_or_remove_interface(const char *newIface , int createIface)
{
const char *wlanIface = "wlan0";
int retVal = 0;
struct nl_msg *msg = NULL;
struct nl_cb *cb = NULL;
struct nl_cb *s_cb = NULL;
struct nl_sock *nl_sock = NULL;
int nl80211_id;
enum nl80211_iftype type = NL80211_IFTYPE_AP;
/* Allocate a netlink socket */
s_cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!s_cb) {
ALOGE( "Failed to allocate Netlink Socket");
retVal = -ENOMEM;
goto nla_put_failure;
}
nl_sock = nl_socket_alloc_cb(s_cb);
if (!nl_sock) {
ALOGE( "Netlink socket Allocation failure");
retVal = -ENOMEM;
goto nla_put_failure;
}
/* connect to generic netlink socket */
if (genl_connect(nl_sock)) {
ALOGE( "Netlink socket Connection failure");
retVal = -ENOLINK;
goto nla_put_failure;
}
nl80211_id = genl_ctrl_resolve(nl_sock, "nl80211");
if (nl80211_id < 0) {
ALOGE( "nl80211 generic netlink not found");
retVal = -ENOENT;
goto nla_put_failure;
}
msg = nlmsg_alloc();
if(!msg) {
ALOGE( "Failed to allocate netlink message");
retVal = -ENOMEM;
goto nla_put_failure;
}
cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb) {
ALOGE( "Failed to allocate netlink callback");
retVal = -ENOMEM;
goto nla_put_failure;
}
if (createIface == 1) {
/* Issue NL80211_CMD_NEW_INTERFACE */
genlmsg_put( msg, 0, 0, nl80211_id, 0, 0, NL80211_CMD_NEW_INTERFACE, 0);
nla_put_u32( msg, NL80211_ATTR_IFINDEX, if_nametoindex( wlanIface ));
NLA_PUT_STRING( msg, NL80211_ATTR_IFNAME, newIface);
nla_put_u32( msg, NL80211_ATTR_IFTYPE, type);
} else {
genlmsg_put( msg, 0, 0, nl80211_id, 0, 0, NL80211_CMD_DEL_INTERFACE, 0);
nla_put_u32( msg, NL80211_ATTR_IFINDEX, if_nametoindex( newIface ));
}
retVal = nl_send_auto_complete(nl_sock, msg );
if (retVal < 0 ) {
goto nla_put_failure;
}
else {
ALOGD("Interface %s is %s - Ok", (createIface == 1 ? "created":"removed") ,newIface);
}
nla_put_failure:
if (nl_sock)
nl_socket_free(nl_sock);
if (s_cb)
nl_cb_put(s_cb);
if (msg)
nlmsg_free(msg);
if (cb)
nl_cb_put(cb);
return retVal;
}