1 /*-------------------------------------------------------------------------- 2 Copyright (c) 2013, The Linux Foundation. All rights reserved. 3 4 Redistribution and use in source and binary forms, with or without 5 modification, 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 16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 ADVISED 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 "/system/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 82 unsigned 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 92 int 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 157 exit_remove: 158 close(fd_file); 159 remove("WCNSS_CAL_FILE"); 160 return rc; 161 162 exit_close: 163 close(fd_file); 164 165 exit: 166 return rc; 167 } 168 169 170 int 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 223 exit_remove: 224 close(fd_file); 225 remove(WCNSS_CAL_FILE); 226 227 exit: 228 return rc; 229 } 230 231 232 void 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 270 void 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 331 out: 332 close(rfd); 333 close(wfd); 334 return; 335 336 out_nocopy: 337 property_set("wlan.driver.config", WLAN_INI_FILE_DEST); 338 return; 339 } 340 unsigned 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 363 void 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 rc = property_get("ro.serialno", serial, ""); 378 if (rc) { 379 serial_num = convert_string_to_hex(serial); 380 ALOGE("Serial Number is %x", serial_num); 381 382 msg[pos++] = WCNSS_USR_SERIAL_NUM >> BYTE_1; 383 msg[pos++] = WCNSS_USR_SERIAL_NUM >> BYTE_0; 384 msg[pos++] = serial_num >> BYTE_3; 385 msg[pos++] = serial_num >> BYTE_2; 386 msg[pos++] = serial_num >> BYTE_1; 387 msg[pos++] = serial_num >> BYTE_0; 388 389 if (write(fd, msg, pos) < 0) { 390 ALOGE("Failed to write to %s : %s", WCNSS_CTRL, 391 strerror(errno)); 392 goto fail; 393 } 394 } 395 396 #ifdef WCNSS_QMI 397 if (SUCCESS == nv_mac_addr) 398 { 399 pos = 0; 400 msg[pos++] = WCNSS_USR_WLAN_MAC_ADDR >> BYTE_1; 401 msg[pos++] = WCNSS_USR_WLAN_MAC_ADDR >> BYTE_0; 402 msg[pos++] = wlan_nv_mac_addr[0]; 403 msg[pos++] = wlan_nv_mac_addr[1]; 404 msg[pos++] = wlan_nv_mac_addr[2]; 405 msg[pos++] = wlan_nv_mac_addr[3]; 406 msg[pos++] = wlan_nv_mac_addr[4]; 407 msg[pos++] = wlan_nv_mac_addr[5]; 408 409 ALOGI("WLAN MAC Addr:" MAC_ADDRESS_STR, 410 MAC_ADDR_ARRAY(wlan_nv_mac_addr)); 411 412 if (write(fd, msg, pos) < 0) { 413 ALOGE("Failed to write to %s : %s", WCNSS_CTRL, 414 strerror(errno)); 415 goto fail; 416 } 417 } 418 #endif 419 420 pos = 0; 421 msg[pos++] = WCNSS_USR_HAS_CAL_DATA >> BYTE_1; 422 msg[pos++] = WCNSS_USR_HAS_CAL_DATA >> BYTE_0; 423 424 rc = stat(WCNSS_FACT_FILE, &st); 425 if (rc == 0) { 426 ALOGE("Factory file found, deleting cal file"); 427 unlink(WCNSS_CAL_FILE); 428 goto fail_resp; 429 } 430 431 rc = stat(WCNSS_CAL_FILE, &st); 432 if (rc != 0) { 433 ALOGE("CAL file not found"); 434 goto fail_resp; 435 } 436 437 /* has cal data */ 438 msg[pos++] = 1; 439 440 if (write(fd, msg, pos) < 0) { 441 ALOGE("Failed to write to %s : %s", WCNSS_CTRL, 442 strerror(errno)); 443 goto fail; 444 } 445 446 ALOGI("Correctly triggered cal file"); 447 *cal = SUCCESS; 448 close(fd); 449 return; 450 451 fail_resp: 452 msg[pos++] = 0; 453 if (write(fd, msg, pos) < 0) 454 ALOGE("Failed to write to %s : %s", WCNSS_CTRL, 455 strerror(errno)); 456 457 fail: 458 *cal = FAILED; 459 close(fd); 460 return; 461 } 462 463 void setup_wlan_driver_ath_prop() 464 { 465 property_set("wlan.driver.ath", WLAN_DRIVER_ATH_DEFAULT_VAL); 466 } 467 468 #ifdef WCNSS_QMI 469 int check_modem_compatability(struct dev_info *mdm_detect_info) 470 { 471 char args[MODEM_BASEBAND_PROPERTY_SIZE] = {0}; 472 int ret = 0; 473 /* Get the hardware property */ 474 ret = property_get(MODEM_BASEBAND_PROPERTY, args, ""); 475 if (ret > MODEM_BASEBAND_PROPERTY_SIZE) { 476 ALOGE("property [%s] has size [%d] that exceeds max [%d]", 477 MODEM_BASEBAND_PROPERTY, ret, MODEM_BASEBAND_PROPERTY_SIZE); 478 return 0; 479 } 480 /* This will check for the type of hardware, and if the 481 hardware type needs external modem, it will check if the 482 modem type is external*/ 483 if(!strncmp(MODEM_BASEBAND_VALUE_APQ, args, 3)) { 484 485 for (ret = 0; ret < mdm_detect_info->num_modems; ret++) { 486 if (mdm_detect_info->mdm_list[ret].type == MDM_TYPE_EXTERNAL) { 487 ALOGE("Hardware supports external modem"); 488 return 1; 489 } 490 } 491 ALOGE("Hardware does not support external modem"); 492 return 0; 493 } 494 return 1; 495 } 496 #endif 497 498 int main(int argc, char *argv[]) 499 { 500 int rc; 501 int fd_dev, ret_cal; 502 int nv_mac_addr = FAILED; 503 #ifdef WCNSS_QMI 504 struct dev_info mdm_detect_info; 505 int nom = 0; 506 #endif 507 508 setup_wlan_config_file(); 509 510 #ifdef WCNSS_QMI 511 /* Call ESOC API to get the number of modems. 512 If the number of modems is not zero, only then proceed 513 with the eap_proxy intialization.*/ 514 515 nom = get_system_info(&mdm_detect_info); 516 517 if (nom > 0) 518 ALOGE("Failed to get system info, ret %d", nom); 519 520 if (mdm_detect_info.num_modems == 0) { 521 ALOGE("wcnss_service: No Modem support for this target" 522 " number of modems is %d", mdm_detect_info.num_modems); 523 goto nomodem; 524 } 525 526 ALOGE("wcnss_service: num_modems = %d", mdm_detect_info.num_modems); 527 528 if(!check_modem_compatability(&mdm_detect_info)) { 529 ALOGE("wcnss_service: Target does not have external modem"); 530 goto nomodem; 531 } 532 533 /* initialize the DMS client and request the wlan mac address */ 534 535 if (SUCCESS == wcnss_init_qmi()) { 536 537 rc = wcnss_qmi_get_wlan_address(wlan_nv_mac_addr); 538 539 if (rc == SUCCESS) { 540 nv_mac_addr = SUCCESS; 541 ALOGE("WLAN MAC Addr:" MAC_ADDRESS_STR, 542 MAC_ADDR_ARRAY(wlan_nv_mac_addr)); 543 } else 544 ALOGE("Failed to Get MAC addr from modem"); 545 546 wcnss_qmi_deinit(); 547 } 548 else 549 ALOGE("Failed to Initialize wcnss QMI Interface"); 550 551 nomodem: 552 #endif 553 setup_wcnss_parameters(&ret_cal, nv_mac_addr); 554 555 fd_dev = open(WCNSS_DEVICE, O_RDWR); 556 if (fd_dev < 0) { 557 ALOGE("Failed to open wcnss device : %s", 558 strerror(errno)); 559 return fd_dev; 560 } 561 562 if (ret_cal != FAILED) { 563 rc = wcnss_write_cal_data(fd_dev); 564 if (rc != SUCCESS) 565 ALOGE("No cal data is written to WCNSS %d", rc); 566 else 567 ALOGE("Cal data is successfully written to WCNSS"); 568 } 569 570 setup_wlan_driver_ath_prop(); 571 572 rc = wcnss_read_and_store_cal_data(fd_dev); 573 if (rc != SUCCESS) 574 ALOGE("Failed to read and save cal data %d", rc); 575 else 576 ALOGI("Calibration data was successfull written to %s", 577 WCNSS_CAL_FILE); 578 579 close(fd_dev); 580 581 return rc; 582 } 583