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/vendor/wifi/WCNSS_qcom_wlan_cal.bin"
65 #define WCNSS_FACT_FILE "/data/vendor/wifi/WCN_FACTORY"
66 #define WCNSS_DEVICE    "/dev/wcnss_wlan"
67 #define WCNSS_CTRL      "/dev/wcnss_ctrl"
68 #define WLAN_INI_FILE_DEST   "/data/vendor/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
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 
wcnss_write_cal_data(int fd_dev)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 
wcnss_read_and_store_cal_data(int fd_dev)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 
find_full_path(char * cur_dir,char * file_to_find,char * full_path)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 
setup_wlan_config_file()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 }
convert_string_to_hex(char * string)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 
setup_wcnss_parameters(int * cal,int nv_mac_addr)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 #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 
432 fail_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 
438 fail:
439 	*cal = FAILED;
440 	close(fd);
441 	return;
442 }
443 
setup_wlan_driver_ath_prop()444 void setup_wlan_driver_ath_prop()
445 {
446 	property_set("wlan.driver.ath", WLAN_DRIVER_ATH_DEFAULT_VAL);
447 }
448 
449 #ifdef WCNSS_QMI
check_modem_compatability(struct dev_info * mdm_detect_info)450 int 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 
main(int argc,char * argv[])479 int 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 
532 nomodem:
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