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