1/*-------------------------------------------------------------------------- 2Copyright (c) 2013, The Linux Foundation. All rights reserved. 3 4Redistribution and use in source and binary forms, with or without 5modification, are permitted provided that the following conditions are met: 6 * Redistributions of source code must retain the above copyright 7 notice, this list of conditions and the following disclaimer. 8 * Redistributions in binary form must reproduce the above copyright 9 notice, this list of conditions and the following disclaimer in the 10 documentation and/or other materials provided with the distribution. 11 * Neither the name of The Linux Foundation nor 12 the names of its contributors may be used to endorse or promote 13 products derived from this software without specific prior written 14 permission. 15 16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27--------------------------------------------------------------------------*/ 28 29#include <stdio.h> 30#include <fcntl.h> 31#include <errno.h> 32#include <dirent.h> 33#include <ctype.h> 34#include <grp.h> 35#include <utime.h> 36#include <sys/stat.h> 37#include <sys/sendfile.h> 38#define LOG_TAG "wcnss_service" 39#include <cutils/log.h> 40#include <cutils/properties.h> 41#ifdef WCNSS_QMI 42#include "wcnss_qmi_client.h" 43#include "mdm_detect.h" 44#endif 45 46#define SUCCESS 0 47#define FAILED -1 48#define BYTE_0 0 49#define BYTE_1 8 50#define BYTE_2 16 51#define BYTE_3 24 52 53#define MAX_FILE_LENGTH (1024) 54#define WCNSS_MAX_CMD_LEN (128) 55 56/* control messages to wcnss driver */ 57#define WCNSS_USR_CTRL_MSG_START 0x00000000 58#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1) 59#define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2) 60#define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3) 61 62 63#define WCNSS_CAL_CHUNK (3*1024) 64#define WCNSS_CAL_FILE "/data/misc/wifi/WCNSS_qcom_wlan_cal.bin" 65#define WCNSS_FACT_FILE "/data/misc/wifi/WCN_FACTORY" 66#define WCNSS_DEVICE "/dev/wcnss_wlan" 67#define WCNSS_CTRL "/dev/wcnss_ctrl" 68#define WLAN_INI_FILE_DEST "/data/misc/wifi/WCNSS_qcom_cfg.ini" 69#define WLAN_INI_FILE_SOURCE "/vendor/etc/wifi/WCNSS_qcom_cfg.ini" 70#define WCNSS_HAS_CAL_DATA\ 71 "/sys/module/wcnsscore/parameters/has_calibrated_data" 72#define WLAN_DRIVER_ATH_DEFAULT_VAL "0" 73 74#define ASCII_A 65 75#define ASCII_a 97 76#define ASCII_0 48 77#define HEXA_A 10 78#define HEX_BASE 16 79 80#ifdef WCNSS_QMI 81#define WLAN_ADDR_SIZE 6 82unsigned char wlan_nv_mac_addr[WLAN_ADDR_SIZE]; 83#define MAC_ADDR_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] 84#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x" 85 86/* As we Want to write in 00:0a:f5:11:22:33 format in sysfs file 87 so taking mac length as 12 char + 5 for ":" + NULL 88 */ 89#define WLAN_MAC_ADDR_STRING 18 90#endif 91 92int wcnss_write_cal_data(int fd_dev) 93{ 94 int rcount = 0; 95 int size = 0; 96 int rc = 0; 97 int wcount = 0; 98 int fd_file; 99 struct stat st; 100 101 char buf[WCNSS_CAL_CHUNK]; 102 103 ALOGI("wcnss_write_cal_data trying to write cal"); 104 105 rc = stat(WCNSS_CAL_FILE, &st); 106 if (rc < 0) { 107 ALOGE("Failed to stat cal file : %s", 108 strerror(errno)); 109 goto exit; 110 } 111 112 size = st.st_size; 113 114 fd_file = open(WCNSS_CAL_FILE, O_RDONLY); 115 if (fd_file < 0) { 116 ALOGE("cal file doesn't exist: %s", 117 strerror(errno)); 118 rc = fd_file; 119 goto exit; 120 } 121 122 /* write the file size first, so that platform driver knows 123 * when it recieves the full data */ 124 wcount = write(fd_dev, (void *)&size, 4); 125 if (wcount != 4) { 126 ALOGE("Failed to write to wcnss device : %s", 127 strerror(errno)); 128 rc = wcount; 129 goto exit_close; 130 } 131 132 do { 133 rcount = read(fd_file, (void *)buf, sizeof(buf)); 134 if (rcount < 0) { 135 ALOGE("Failed to read from cal file ; %s", 136 strerror(errno)); 137 rc = rcount; 138 goto exit_remove; 139 } 140 141 if (!rcount) 142 break; 143 144 wcount = write(fd_dev, buf, rcount); 145 if (wcount < 0) { 146 ALOGE("Failed to write to wcnss device : %s", 147 strerror(errno)); 148 rc = wcount; 149 goto exit_close; 150 } 151 152 } while (rcount); 153 close(fd_file); 154 155 return SUCCESS; 156 157exit_remove: 158 close(fd_file); 159 remove("WCNSS_CAL_FILE"); 160 return rc; 161 162exit_close: 163 close(fd_file); 164 165exit: 166 return rc; 167} 168 169 170int wcnss_read_and_store_cal_data(int fd_dev) 171{ 172 int rcount = 0; 173 int wcount = 0; 174 int fd_file = -1; 175 int rc = 0; 176 177 char buf[WCNSS_CAL_CHUNK]; 178 179 ALOGI("wcnss_read_and_store_cal_data trying to read cal"); 180 181 do { 182 /* wait on this read until data comes from fw */ 183 rcount = read(fd_dev, (void *)buf, sizeof(buf)); 184 if (rcount < 0) { 185 ALOGE("Failed to read from wcnss device : %s", 186 strerror(errno)); 187 rc = rcount; 188 goto exit; 189 } 190 191 /* truncate the file only if there is fw data, this read 192 * may never return if the fw decides that no more cal is 193 * required; and the data we have now is good enough. 194 */ 195 if (fd_file < 0) { 196 fd_file = open(WCNSS_CAL_FILE, O_WRONLY 197 | O_CREAT | O_TRUNC, 0664); 198 if (fd_file < 0) { 199 ALOGE("Failed to open cal file : %s", 200 strerror(errno)); 201 rc = fd_file; 202 goto exit; 203 } 204 } 205 206 if (!rcount) 207 break; 208 209 wcount = write(fd_file, buf, rcount); 210 if (wcount < 0) { 211 ALOGE("Failed to write to cal file : %s", 212 strerror(errno)); 213 rc = wcount; 214 goto exit_remove; 215 } 216 217 } while (rcount); 218 219 close(fd_file); 220 221 return SUCCESS; 222 223exit_remove: 224 close(fd_file); 225 remove(WCNSS_CAL_FILE); 226 227exit: 228 return rc; 229} 230 231 232void find_full_path(char *cur_dir, char *file_to_find, char *full_path) 233{ 234 DIR *dir; 235 struct stat st; 236 struct dirent *dr; 237 char cwd[1024]; 238 int rc; 239 240 chdir(cur_dir); 241 242 dir = opendir("."); 243 244 if (dir != NULL) { 245 while ((dr = readdir(dir))) { 246 247 rc = lstat(dr->d_name, &st); 248 if (rc < 0) { 249 ALOGE("lstat failed %s", strerror(errno)); 250 return; 251 } 252 if (S_ISDIR(st.st_mode)) { 253 if ((strcmp(dr->d_name, ".")) && 254 (strcmp(dr->d_name, ".."))) { 255 find_full_path(dr->d_name, 256 file_to_find, full_path); 257 } 258 } else if (!strcmp(file_to_find, dr->d_name)) { 259 getcwd(cwd, sizeof(cwd)); 260 snprintf(full_path, MAX_FILE_LENGTH, "%s/%s", 261 cwd, file_to_find); 262 } 263 } 264 closedir(dir); 265 } 266 267 chdir(".."); 268} 269 270void setup_wlan_config_file() 271{ 272 int rfd; 273 int wfd; 274 struct stat st_dest, st_src; 275 int rc_dest; 276 int rc; 277 struct group *grp; 278 struct utimbuf new_time; 279 280 rc = stat(WLAN_INI_FILE_SOURCE, &st_src); 281 if (rc != 0) { 282 ALOGE("source file do not exist %s", WLAN_INI_FILE_SOURCE); 283 return; 284 } 285 286 rc_dest = stat(WLAN_INI_FILE_DEST, &st_dest); 287 if (rc_dest == 0 && st_dest.st_size && 288 (st_dest.st_mtime > st_src.st_mtime)) { 289 ALOGE("wlan ini file exists %s and is newer than %s", 290 WLAN_INI_FILE_DEST, WLAN_INI_FILE_SOURCE); 291 goto out_nocopy; 292 } 293 294 rfd = open(WLAN_INI_FILE_SOURCE, O_RDONLY); 295 if (rfd < 0) { 296 ALOGE("Failed to open ini source file: %s", strerror(errno)); 297 return; 298 } 299 300 wfd = open(WLAN_INI_FILE_DEST, O_WRONLY | O_CREAT | O_TRUNC, 0660); 301 if (wfd < 0) { 302 ALOGE("Failed to open ini dest file: %s", strerror(errno)); 303 close(rfd); 304 return; 305 } 306 307 rc = sendfile(wfd, rfd, 0, st_src.st_size); 308 if (rc != st_src.st_size) { 309 ALOGE("Failed to copy ini file: %s", strerror(errno)); 310 goto out; 311 } 312 313 new_time.actime = st_src.st_atime; 314 new_time.modtime = st_src.st_mtime; 315 316 rc = utime(WLAN_INI_FILE_DEST, &new_time); 317 if (rc != 0) 318 ALOGE("could not preserve the timestamp %s", strerror(errno)); 319 320 grp = getgrnam("wifi"); 321 if (grp != NULL) { 322 rc = chown(WLAN_INI_FILE_DEST, -1, grp->gr_gid); 323 if (rc != 0) 324 ALOGE("Failed change group of ini file %s", strerror(errno)); 325 } else { 326 ALOGE("Failed to get group wifi %s", strerror(errno)); 327 } 328 329 property_set("wlan.driver.config", WLAN_INI_FILE_DEST); 330 331out: 332 close(rfd); 333 close(wfd); 334 return; 335 336out_nocopy: 337 property_set("wlan.driver.config", WLAN_INI_FILE_DEST); 338 return; 339} 340unsigned int convert_string_to_hex(char* string) 341{ 342 int idx = 0; 343 unsigned long int hex_num = 0; 344 for(idx; string[idx] != '\0'; idx++){ 345 if(isalpha(string[idx])) { 346 if(string[idx] >='a' && string[idx] <='f') { 347 hex_num = hex_num * HEX_BASE + ((int)string[idx] 348 - ASCII_a + HEXA_A); 349 } else if ( string[idx] >='A' && string[idx] <='F') { 350 hex_num = hex_num * HEX_BASE + ((int)string[idx] 351 - ASCII_A + HEXA_A); 352 } else 353 hex_num = hex_num * HEX_BASE + (int)string[idx]; 354 } else { 355 hex_num = hex_num * HEX_BASE + (string[idx]- ASCII_0); 356 } 357 } 358 hex_num = hex_num & 0xFFFFFFFF; 359 return hex_num; 360} 361 362 363void setup_wcnss_parameters(int *cal, int nv_mac_addr) 364{ 365 char msg[WCNSS_MAX_CMD_LEN]; 366 char serial[PROPERTY_VALUE_MAX]; 367 int fd, rc, pos = 0; 368 struct stat st; 369 unsigned int serial_num = 0; 370 371 fd = open(WCNSS_CTRL, O_WRONLY); 372 if (fd < 0) { 373 ALOGE("Failed to open %s : %s", WCNSS_CTRL, strerror(errno)); 374 return; 375 } 376 377#ifdef WCNSS_QMI 378 if (SUCCESS == nv_mac_addr) 379 { 380 pos = 0; 381 msg[pos++] = WCNSS_USR_WLAN_MAC_ADDR >> BYTE_1; 382 msg[pos++] = WCNSS_USR_WLAN_MAC_ADDR >> BYTE_0; 383 msg[pos++] = wlan_nv_mac_addr[0]; 384 msg[pos++] = wlan_nv_mac_addr[1]; 385 msg[pos++] = wlan_nv_mac_addr[2]; 386 msg[pos++] = wlan_nv_mac_addr[3]; 387 msg[pos++] = wlan_nv_mac_addr[4]; 388 msg[pos++] = wlan_nv_mac_addr[5]; 389 390 ALOGI("WLAN MAC Addr:" MAC_ADDRESS_STR, 391 MAC_ADDR_ARRAY(wlan_nv_mac_addr)); 392 393 if (write(fd, msg, pos) < 0) { 394 ALOGE("Failed to write to %s : %s", WCNSS_CTRL, 395 strerror(errno)); 396 goto fail; 397 } 398 } 399#endif 400 401 pos = 0; 402 msg[pos++] = WCNSS_USR_HAS_CAL_DATA >> BYTE_1; 403 msg[pos++] = WCNSS_USR_HAS_CAL_DATA >> BYTE_0; 404 405 rc = stat(WCNSS_FACT_FILE, &st); 406 if (rc == 0) { 407 ALOGE("Factory file found, deleting cal file"); 408 unlink(WCNSS_CAL_FILE); 409 goto fail_resp; 410 } 411 412 rc = stat(WCNSS_CAL_FILE, &st); 413 if (rc != 0) { 414 ALOGE("CAL file not found"); 415 goto fail_resp; 416 } 417 418 /* has cal data */ 419 msg[pos++] = 1; 420 421 if (write(fd, msg, pos) < 0) { 422 ALOGE("Failed to write to %s : %s", WCNSS_CTRL, 423 strerror(errno)); 424 goto fail; 425 } 426 427 ALOGI("Correctly triggered cal file"); 428 *cal = SUCCESS; 429 close(fd); 430 return; 431 432fail_resp: 433 msg[pos++] = 0; 434 if (write(fd, msg, pos) < 0) 435 ALOGE("Failed to write to %s : %s", WCNSS_CTRL, 436 strerror(errno)); 437 438fail: 439 *cal = FAILED; 440 close(fd); 441 return; 442} 443 444void setup_wlan_driver_ath_prop() 445{ 446 property_set("wlan.driver.ath", WLAN_DRIVER_ATH_DEFAULT_VAL); 447} 448 449#ifdef WCNSS_QMI 450int check_modem_compatability(struct dev_info *mdm_detect_info) 451{ 452 char args[MODEM_BASEBAND_PROPERTY_SIZE] = {0}; 453 int ret = 0; 454 /* Get the hardware property */ 455 ret = property_get(MODEM_BASEBAND_PROPERTY, args, ""); 456 if (ret > MODEM_BASEBAND_PROPERTY_SIZE) { 457 ALOGE("property [%s] has size [%d] that exceeds max [%d]", 458 MODEM_BASEBAND_PROPERTY, ret, MODEM_BASEBAND_PROPERTY_SIZE); 459 return 0; 460 } 461 /* This will check for the type of hardware, and if the 462 hardware type needs external modem, it will check if the 463 modem type is external*/ 464 if(!strncmp(MODEM_BASEBAND_VALUE_APQ, args, 3)) { 465 466 for (ret = 0; ret < mdm_detect_info->num_modems; ret++) { 467 if (mdm_detect_info->mdm_list[ret].type == MDM_TYPE_EXTERNAL) { 468 ALOGE("Hardware supports external modem"); 469 return 1; 470 } 471 } 472 ALOGE("Hardware does not support external modem"); 473 return 0; 474 } 475 return 1; 476} 477#endif 478 479int main(int argc, char *argv[]) 480{ 481 int rc; 482 int fd_dev, ret_cal; 483 int nv_mac_addr = FAILED; 484#ifdef WCNSS_QMI 485 struct dev_info mdm_detect_info; 486 int nom = 0; 487#endif 488 489 setup_wlan_config_file(); 490 491#ifdef WCNSS_QMI 492 /* Call ESOC API to get the number of modems. 493 If the number of modems is not zero, only then proceed 494 with the eap_proxy intialization.*/ 495 496 nom = get_system_info(&mdm_detect_info); 497 498 if (nom > 0) 499 ALOGE("Failed to get system info, ret %d", nom); 500 501 if (mdm_detect_info.num_modems == 0) { 502 ALOGE("wcnss_service: No Modem support for this target" 503 " number of modems is %d", mdm_detect_info.num_modems); 504 goto nomodem; 505 } 506 507 ALOGE("wcnss_service: num_modems = %d", mdm_detect_info.num_modems); 508 509 if(!check_modem_compatability(&mdm_detect_info)) { 510 ALOGE("wcnss_service: Target does not have external modem"); 511 goto nomodem; 512 } 513 514 /* initialize the DMS client and request the wlan mac address */ 515 516 if (SUCCESS == wcnss_init_qmi()) { 517 518 rc = wcnss_qmi_get_wlan_address(wlan_nv_mac_addr); 519 520 if (rc == SUCCESS) { 521 nv_mac_addr = SUCCESS; 522 ALOGE("WLAN MAC Addr:" MAC_ADDRESS_STR, 523 MAC_ADDR_ARRAY(wlan_nv_mac_addr)); 524 } else 525 ALOGE("Failed to Get MAC addr from modem"); 526 527 wcnss_qmi_deinit(); 528 } 529 else 530 ALOGE("Failed to Initialize wcnss QMI Interface"); 531 532nomodem: 533#endif 534 setup_wcnss_parameters(&ret_cal, nv_mac_addr); 535 536 fd_dev = open(WCNSS_DEVICE, O_RDWR); 537 if (fd_dev < 0) { 538 ALOGE("Failed to open wcnss device : %s", 539 strerror(errno)); 540 return fd_dev; 541 } 542 543 if (ret_cal != FAILED) { 544 rc = wcnss_write_cal_data(fd_dev); 545 if (rc != SUCCESS) 546 ALOGE("No cal data is written to WCNSS %d", rc); 547 else 548 ALOGE("Cal data is successfully written to WCNSS"); 549 } 550 551 setup_wlan_driver_ath_prop(); 552 553 rc = wcnss_read_and_store_cal_data(fd_dev); 554 if (rc != SUCCESS) 555 ALOGE("Failed to read and save cal data %d", rc); 556 else 557 ALOGI("Calibration data was successfull written to %s", 558 WCNSS_CAL_FILE); 559 560 close(fd_dev); 561 562 return rc; 563} 564