1/*
2 * PLT utility for wireless chip supported by TI's driver wl12xx
3 *
4 * See README and COPYING for more details.
5 */
6
7#include <sys/ioctl.h>
8#include <errno.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/stat.h>
13#include <fcntl.h>
14#include <unistd.h>
15#include <stdbool.h>
16
17#include <netlink/genl/genl.h>
18#include <netlink/genl/family.h>
19#include <netlink/genl/ctrl.h>
20#include <netlink/msg.h>
21#include <netlink/attr.h>
22#include <linux/wireless.h>
23#include "nl80211.h"
24
25#include "calibrator.h"
26#include "plt.h"
27#include "ini.h"
28#include "nvs.h"
29
30static char *ini_get_line(char *s, int size, FILE *stream, int *line,
31				  char **_pos)
32{
33	char *pos, *end, *sstart;
34
35	while (fgets(s, size, stream)) {
36		s[size - 1] = '\0';
37		pos = s;
38
39		/* Skip white space from the beginning of line. */
40		while (*pos == ' ' || *pos == '\t' || *pos == '\r') {
41			pos++;
42                }
43
44		/* Skip comment lines and empty lines */
45		if (*pos == '#' || *pos == '\n' || *pos == '\0') {
46			continue;
47                }
48
49		/*
50		 * Remove # comments unless they are within a double quoted
51		 * string.
52		 */
53		sstart = strchr(pos, '"');
54		if (sstart) {
55			sstart = strrchr(sstart + 1, '"');
56                }
57		if (!sstart) {
58			sstart = pos;
59                }
60		end = strchr(sstart, '#');
61		if (end) {
62			*end-- = '\0';
63                } else {
64			end = pos + strlen(pos) - 1;
65                }
66
67		/* Remove trailing white space. */
68		while (end > pos &&
69		       (*end == '\n' || *end == ' ' || *end == '\t' ||
70			*end == '\r')) {
71			*end-- = '\0';
72                }
73
74		if (*pos == '\0') {
75			continue;
76                }
77
78		(*line)++;
79
80		if (_pos) {
81			*_pos = pos;
82                }
83		return pos;
84	}
85
86	if (_pos) {
87		*_pos = NULL;
88        }
89
90	return NULL;
91}
92
93static int split_line(char *line, char **name, char **value)
94{
95	char *pos = line;
96
97	*value = strchr(pos, '=');
98	if (!*value) {
99		fprintf(stderr, "Wrong format of line\n");
100		return 1;
101	}
102
103	*name = *value;
104
105	(*name)--;
106	while (**name == ' ' || **name == '\t' || **name == '\r') {
107		(*name)--;
108        }
109
110	*++(*name) = '\0';
111
112	(*value)++;
113	while (**value == ' ' || **value == '\t' || **value == '\r') {
114		(*value)++;
115        }
116
117	return 0;
118}
119
120#define COMPARE_N_ADD(temp, str, val, ptr, size)		\
121	if (strncmp(temp, str, sizeof(temp)) == 0) {		\
122		int i;						\
123		unsigned char *p = ptr;				\
124		for (i = 0; i < size; i++) {			\
125			*p = strtol(val, NULL, 16);		\
126			if (i != sizeof(ptr)-1) {		\
127				val += 3; p++;			\
128			}					\
129		}						\
130		return 0;					\
131	}
132
133#define DBG_COMPARE_N_ADD(temp, str, val, ptr, size)		\
134	if (strncmp(temp, str, sizeof(temp)) == 0) {		\
135		int i;						\
136		unsigned char *p = ptr;				\
137		for (i = 0; i < size; i++) {			\
138			*p = strtol(val, NULL, 16);		\
139			if (i != sizeof(ptr)-1) {		\
140				val += 3; p++;			\
141			}					\
142		}						\
143		p = ptr;					\
144		printf("%s ", temp);				\
145		for (i = 0; i < size; i++) {			\
146			printf("%02X ", *p);			\
147			p++;					\
148		}						\
149		printf("\n");					\
150		return 0;					\
151	}
152
153#define COMPARE_N_ADD2(temp, str, val, ptr, size)		\
154	if (strncmp(temp, str, sizeof(temp)) == 0) {		\
155		int i;						\
156		unsigned short *p = ptr;			\
157		for (i = 0; i < size; i++) {			\
158			*p = strtol(val, NULL, 16);		\
159			if (i != sizeof(ptr)-1) {		\
160				val += 5; p++;			\
161			}					\
162		}						\
163		return 0;					\
164	}
165
166#define DBG_COMPARE_N_ADD2(temp, str, val, ptr, size)		\
167	if (strncmp(temp, str, sizeof(temp)) == 0) {		\
168		int i;						\
169		unsigned short *p = ptr;			\
170		for (i = 0; i < size; i++) {			\
171			*p = strtol(val, NULL, 16);		\
172			if (i != sizeof(ptr)-1) {		\
173				val += 5; p++;			\
174			}					\
175		}						\
176		p = ptr;					\
177		printf("%s ", temp);				\
178		for (i = 0; i < size; i++) {			\
179			printf("%04X ", *p);			\
180			p++;					\
181		}						\
182		printf("\n");					\
183		return 0;					\
184	}
185
186static int parse_general_prms(char *l, struct wl12xx_common *cmn,
187	struct wl12xx_ini *p)
188{
189	char *name, *val;
190	struct wl1271_ini_general_params *gp = &(p->ini1271.general_params);
191
192	if (split_line(l, &name, &val)) {
193		return 1;
194        }
195
196	COMPARE_N_ADD("TXBiPFEMAutoDetect", l, val,
197		&gp->tx_bip_fem_auto_detect, 1);
198
199	COMPARE_N_ADD("TXBiPFEMManufacturer", l, val,
200		&gp->tx_bip_fem_manufacturer, 1);
201
202	COMPARE_N_ADD("RefClk", l, val, &gp->ref_clock, 1);
203
204	COMPARE_N_ADD("SettlingTime", l, val, &gp->settling_time, 1);
205
206	COMPARE_N_ADD("ClockValidOnWakeup", l, val,
207		&gp->clk_valid_on_wakeup, 1);
208
209	COMPARE_N_ADD("DC2DCMode", l, val, &gp->dc2dc_mode, 1);
210
211	COMPARE_N_ADD("Single_Dual_Band_Solution", l, val,
212		&gp->dual_mode_select, 1);
213
214	if (cmn->dual_mode == DUAL_MODE_UNSET) {
215		cmn->dual_mode = gp->dual_mode_select;
216        }
217	else if (cmn->dual_mode != gp->dual_mode_select) {
218		fprintf(stderr, "Error, FEMs with different dual modes\n");
219		return 1;
220	}
221
222	COMPARE_N_ADD("Settings", l, val, &gp->general_settings, 1);
223
224	COMPARE_N_ADD("SRState", l, val, &gp->sr_state, 1);
225
226	COMPARE_N_ADD("SRF1", l, val,
227		gp->srf1, WL1271_INI_MAX_SMART_REFLEX_PARAM);
228
229	COMPARE_N_ADD("SRF2", l, val,
230		gp->srf2, WL1271_INI_MAX_SMART_REFLEX_PARAM);
231
232	COMPARE_N_ADD("SRF3", l, val,
233		gp->srf3, WL1271_INI_MAX_SMART_REFLEX_PARAM);
234
235	fprintf(stderr, "Unable to parse: (%s)\n", l);
236
237	return 1;
238}
239
240static int parse_general_prms_128x(char *l, struct wl12xx_common *cmn,
241	struct wl12xx_ini *p)
242{
243	char *name, *val;
244	struct wl128x_ini_general_params *gp =
245		&(p->ini128x.general_params);
246
247	if (split_line(l, &name, &val)) {
248		return 1;
249        }
250
251	COMPARE_N_ADD("TXBiPFEMAutoDetect", l, val,
252		&gp->tx_bip_fem_auto_detect, 1);
253
254	COMPARE_N_ADD("TXBiPFEMManufacturer", l, val,
255		&gp->tx_bip_fem_manufacturer, 1);
256
257	COMPARE_N_ADD("RefClk", l, val, &gp->ref_clock, 1);
258
259	COMPARE_N_ADD("SettlingTime", l, val, &gp->settling_time, 1);
260
261	COMPARE_N_ADD("ClockValidOnWakeup", l, val,
262		&gp->clk_valid_on_wakeup, 1);
263
264	COMPARE_N_ADD("TCXO_Clk", l, val, &gp->tcxo_ref_clock, 1);
265
266	COMPARE_N_ADD("TCXO_SettlingTime", l, val, &gp->tcxo_settling_time, 1);
267
268	COMPARE_N_ADD("TCXO_ClockValidOnWakeup", l, val,
269		&gp->tcxo_valid_on_wakeup, 1);
270
271	COMPARE_N_ADD("TCXO_LDO_Voltage", l, val,
272		&gp->tcxo_ldo_voltage, 1);
273
274	COMPARE_N_ADD("Platform_configuration", l, val,
275		&gp->platform_conf, 1);
276
277	COMPARE_N_ADD("Single_Dual_Band_Solution", l, val,
278		&gp->dual_mode_select, 1);
279
280	if (cmn->dual_mode == DUAL_MODE_UNSET) {
281		cmn->dual_mode = gp->dual_mode_select;
282        } else if (cmn->dual_mode != gp->dual_mode_select) {
283		fprintf(stderr, "Error, FEMs with diferent dual modes\n");
284		return 1;
285	}
286
287	COMPARE_N_ADD("Settings", l, val,
288		gp->general_settings, WL128X_INI_MAX_SETTINGS_PARAM);
289
290	COMPARE_N_ADD("XTALItrimVal", l, val, &gp->xtal_itrim_val, 1);
291
292	COMPARE_N_ADD("SRState", l, val, &gp->sr_state, 1);
293
294	COMPARE_N_ADD("SRF1", l, val,
295		gp->srf1, WL1271_INI_MAX_SMART_REFLEX_PARAM);
296
297	COMPARE_N_ADD("SRF2", l, val,
298		gp->srf2, WL1271_INI_MAX_SMART_REFLEX_PARAM);
299
300	COMPARE_N_ADD("SRF3", l, val,
301		gp->srf3, WL1271_INI_MAX_SMART_REFLEX_PARAM);
302
303	fprintf(stderr, "Unable to parse: (%s)\n", l);
304
305	return 1;
306}
307
308static int parse_band2_prms(char *l, struct wl12xx_ini *p)
309{
310	char *name, *val;
311	struct wl1271_ini_band_params_2 *gp =
312		&(p->ini1271.stat_radio_params_2);
313
314	if (split_line(l, &name, &val)) {
315		return 1;
316        }
317
318	COMPARE_N_ADD("RxTraceInsertionLoss_2_4G", l, val,
319		&gp->rx_trace_insertion_loss, 1);
320
321	COMPARE_N_ADD("TXTraceLoss_2_4G", l, val,
322		&gp->tx_trace_loss, 1);
323
324	COMPARE_N_ADD("RxRssiAndProcessCompensation_2_4G", l, val,
325		gp->rx_rssi_process_compens,
326		WL1271_INI_RSSI_PROCESS_COMPENS_SIZE);
327
328	fprintf(stderr, "Unable to parse: (%s)\n", l);
329
330	return 1;
331}
332
333static int parse_band2_prms_128x(char *l, struct wl12xx_ini *p)
334{
335	char *name, *val;
336	struct wl128x_ini_band_params_2 *gp = &(p->ini128x.stat_radio_params_2);
337
338	if (split_line(l, &name, &val)) {
339		return 1;
340        }
341
342	COMPARE_N_ADD("RxTraceInsertionLoss_2_4G", l, val,
343		&gp->rx_trace_insertion_loss, 1);
344
345	COMPARE_N_ADD("TxTraceLoss_2_4G", l, val,
346		gp->tx_trace_loss, WL1271_INI_CHANNEL_COUNT_2);
347
348	fprintf(stderr, "Unable to parse: (%s)\n", l);
349
350	return 1;
351}
352
353static int parse_band5_prms(char *l, struct wl12xx_ini *p)
354{
355	char *name, *val;
356	struct wl1271_ini_band_params_5 *gp =
357		&(p->ini1271.stat_radio_params_5);
358
359	if (split_line(l, &name, &val)) {
360		return 1;
361        }
362
363	COMPARE_N_ADD("RxTraceInsertionLoss_5G", l, val,
364		gp->rx_trace_insertion_loss, 7);
365
366	COMPARE_N_ADD("TXTraceLoss_5G", l, val,
367		gp->tx_trace_loss, 7);
368
369	COMPARE_N_ADD("RxRssiAndProcessCompensation_5G", l, val,
370		gp->rx_rssi_process_compens,
371		WL1271_INI_RSSI_PROCESS_COMPENS_SIZE);
372
373	fprintf(stderr, "Unable to parse: (%s)\n", l);
374
375	return 1;
376}
377
378static int parse_band5_prms_128x(char *l, struct wl12xx_ini *p)
379{
380	char *name, *val;
381	struct wl128x_ini_band_params_5 *gp = &(p->ini128x.stat_radio_params_5);
382
383	if (split_line(l, &name, &val)) {
384		return 1;
385        }
386
387	COMPARE_N_ADD("RxTraceInsertionLoss_5G", l, val,
388		gp->rx_trace_insertion_loss, 7);
389
390	COMPARE_N_ADD("TxTraceLoss_5G", l, val,
391		gp->tx_trace_loss, 7);
392
393	fprintf(stderr, "Unable to parse: (%s)\n", l);
394
395	return 1;
396}
397
398static int parse_fem0_band2_prms(char *l, struct wl12xx_ini *p)
399{
400	char *name, *val;
401	struct wl1271_ini_fem_params_2 *gp =
402		&(p->ini1271.dyn_radio_params_2[0].params);
403
404	if (split_line(l, &name, &val)) {
405		return 1;
406        }
407
408	COMPARE_N_ADD2("FEM0_TXBiPReferencePDvoltage_2_4G", l, val,
409		&gp->tx_bip_ref_pd_voltage, 1);
410
411	COMPARE_N_ADD("FEM0_TxBiPReferencePower_2_4G", l, val,
412		&gp->tx_bip_ref_power, 1);
413
414	COMPARE_N_ADD("FEM0_TxBiPOffsetdB_2_4G", l, val,
415		&gp->tx_bip_ref_offset, 1);
416
417	COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Normal", l, val,
418		gp->tx_per_rate_pwr_limits_normal,
419		WL1271_INI_RATE_GROUP_COUNT);
420
421	COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Degraded", l, val,
422		gp->tx_per_rate_pwr_limits_degraded,
423		WL1271_INI_RATE_GROUP_COUNT);
424
425	COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Extreme", l, val,
426		gp->tx_per_rate_pwr_limits_extreme,
427		WL1271_INI_RATE_GROUP_COUNT);
428
429	COMPARE_N_ADD("FEM0_DegradedLowToNormalThr_2_4G", l, val,
430		&gp->degraded_low_to_normal_thr, 1);
431
432	COMPARE_N_ADD("FEM0_NormalToDegradedHighThr_2_4G", l, val,
433		&gp->normal_to_degraded_high_thr, 1);
434
435	COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_2_4G_11b", l, val,
436		gp->tx_per_chan_pwr_limits_11b,
437		WL1271_INI_CHANNEL_COUNT_2);
438
439	COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_2_4G_OFDM", l, val,
440		gp->tx_per_chan_pwr_limits_ofdm,
441		WL1271_INI_CHANNEL_COUNT_2);
442
443	COMPARE_N_ADD("FEM0_TxPDVsRateOffsets_2_4G", l, val,
444		gp->tx_pd_vs_rate_offsets,
445		WL1271_INI_RATE_GROUP_COUNT);
446
447	COMPARE_N_ADD("FEM0_TxIbiasTable_2_4G", l, val,
448		gp->tx_ibias,
449		WL1271_INI_RATE_GROUP_COUNT);
450
451	COMPARE_N_ADD("FEM0_RxFemInsertionLoss_2_4G", l, val,
452		&gp->rx_fem_insertion_loss, 1);
453
454	fprintf(stderr, "Unable to parse: (%s)\n", l);
455
456	return 1;
457}
458
459static int parse_fem0_band2_prms_128x(char *l, struct wl12xx_ini *p)
460{
461	char *name, *val;
462	struct wl128x_ini_fem_params_2 *gp =
463		&(p->ini128x.dyn_radio_params_2[0].params);
464
465	if (split_line(l, &name, &val)) {
466		return 1;
467        }
468
469	COMPARE_N_ADD2("FEM0_TxBiPReferencePDvoltage_2_4G", l, val,
470		&gp->tx_bip_ref_pd_voltage, 1);
471
472	COMPARE_N_ADD("FEM0_TxBiPReferencePower_2_4G", l, val,
473		&gp->tx_bip_ref_power, 1);
474
475	COMPARE_N_ADD("FEM0_TxBiPOffsetdB_2_4G", l, val,
476		&gp->tx_bip_ref_offset, 1);
477
478	COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Normal", l, val,
479		gp->tx_per_rate_pwr_limits_normal,
480		WL1271_INI_RATE_GROUP_COUNT);
481
482	COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Degraded", l, val,
483		gp->tx_per_rate_pwr_limits_degraded,
484		WL1271_INI_RATE_GROUP_COUNT);
485
486	COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Extreme", l, val,
487		gp->tx_per_rate_pwr_limits_extreme,
488		WL1271_INI_RATE_GROUP_COUNT);
489
490	COMPARE_N_ADD("FEM0_DegradedLowToNormalThr_2_4G", l, val,
491		&gp->degraded_low_to_normal_thr, 1);
492
493	COMPARE_N_ADD("FEM0_NormalToDegradedHighThr_2_4G", l, val,
494		&gp->normal_to_degraded_high_thr, 1);
495
496	COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_2_4G_11b", l, val,
497		gp->tx_per_chan_pwr_limits_11b,
498		WL1271_INI_CHANNEL_COUNT_2);
499
500	COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_2_4G_OFDM", l, val,
501		gp->tx_per_chan_pwr_limits_ofdm,
502		WL1271_INI_CHANNEL_COUNT_2);
503
504	COMPARE_N_ADD("FEM0_TxPDVsRateOffsets_2_4G", l, val,
505		gp->tx_pd_vs_rate_offsets,
506		WL1271_INI_RATE_GROUP_COUNT);
507
508	COMPARE_N_ADD("FEM0_TxPDVsChannelOffsets_2_4G", l, val,
509		gp->tx_pd_vs_chan_offsets,
510		WL1271_INI_CHANNEL_COUNT_2);
511
512	COMPARE_N_ADD("FEM0_TxPDVsTemperature_2_4G", l, val,
513		gp->tx_pd_vs_temperature,
514		WL128X_INI_PD_VS_TEMPERATURE_RANGES);
515
516	COMPARE_N_ADD("FEM0_TxIbiasTable_2_4G", l, val,
517		gp->tx_ibias,
518		WL1271_INI_RATE_GROUP_COUNT);
519
520	COMPARE_N_ADD("FEM0_RxFemInsertionLoss_2_4G", l, val,
521		&gp->rx_fem_insertion_loss, 1);
522
523	fprintf(stderr, "Unable to parse: (%s)\n", l);
524
525	return 1;
526}
527
528static int parse_fem1_band2_prms(char *l, struct wl12xx_ini *p)
529{
530	char *name, *val;
531	struct wl1271_ini_fem_params_2 *gp =
532		&(p->ini1271.dyn_radio_params_2[1].params);
533
534	if (split_line(l, &name, &val)) {
535		return 1;
536        }
537
538	COMPARE_N_ADD2("FEM1_TXBiPReferencePDvoltage_2_4G", l, val,
539		&gp->tx_bip_ref_pd_voltage, 1);
540
541	COMPARE_N_ADD("FEM1_TxBiPReferencePower_2_4G", l, val,
542		&gp->tx_bip_ref_power, 1);
543
544	COMPARE_N_ADD("FEM1_TxBiPOffsetdB_2_4G", l, val,
545		&gp->tx_bip_ref_offset, 1);
546
547	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Normal", l, val,
548		gp->tx_per_rate_pwr_limits_normal,
549		WL1271_INI_RATE_GROUP_COUNT);
550
551	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Degraded", l, val,
552		gp->tx_per_rate_pwr_limits_degraded,
553		WL1271_INI_RATE_GROUP_COUNT);
554
555	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Extreme", l, val,
556		gp->tx_per_rate_pwr_limits_extreme,
557		WL1271_INI_RATE_GROUP_COUNT);
558
559	COMPARE_N_ADD("FEM1_DegradedLowToNormalThr_2_4G", l, val,
560		&gp->degraded_low_to_normal_thr, 1);
561
562	COMPARE_N_ADD("FEM1_NormalToDegradedHighThr_2_4G", l, val,
563		&gp->normal_to_degraded_high_thr, 1);
564
565	COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_2_4G_11b", l, val,
566		gp->tx_per_chan_pwr_limits_11b,
567		WL1271_INI_CHANNEL_COUNT_2);
568
569	COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_2_4G_OFDM", l, val,
570		gp->tx_per_chan_pwr_limits_ofdm,
571		WL1271_INI_CHANNEL_COUNT_2);
572
573	COMPARE_N_ADD("FEM1_TxPDVsRateOffsets_2_4G", l, val,
574		gp->tx_pd_vs_rate_offsets,
575		WL1271_INI_RATE_GROUP_COUNT);
576
577	COMPARE_N_ADD("FEM1_TxIbiasTable_2_4G", l, val,
578		gp->tx_ibias,
579		WL1271_INI_RATE_GROUP_COUNT);
580
581	COMPARE_N_ADD("FEM1_RxFemInsertionLoss_2_4G", l, val,
582		&gp->rx_fem_insertion_loss, 1);
583
584	fprintf(stderr, "Unable to parse: (%s)\n", l);
585
586	return 1;
587}
588
589static int parse_fem1_band2_prms_128x(char *l, struct wl12xx_ini *p)
590{
591	char *name, *val;
592	struct wl128x_ini_fem_params_2 *gp =
593		&(p->ini128x.dyn_radio_params_2[1].params);
594
595	if (split_line(l, &name, &val)) {
596		return 1;
597        }
598
599	COMPARE_N_ADD2("FEM1_TxBiPReferencePDvoltage_2_4G", l, val,
600		&gp->tx_bip_ref_pd_voltage, 1);
601
602	COMPARE_N_ADD("FEM1_TxBiPReferencePower_2_4G", l, val,
603		&gp->tx_bip_ref_power, 1);
604
605	COMPARE_N_ADD("FEM1_TxBiPOffsetdB_2_4G", l, val,
606		&gp->tx_bip_ref_offset, 1);
607
608	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Normal", l, val,
609		gp->tx_per_rate_pwr_limits_normal,
610		WL128X_INI_RATE_GROUP_COUNT);
611
612	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Degraded", l, val,
613		gp->tx_per_rate_pwr_limits_degraded,
614		WL128X_INI_RATE_GROUP_COUNT);
615
616	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Extreme", l, val,
617		gp->tx_per_rate_pwr_limits_extreme,
618		WL128X_INI_RATE_GROUP_COUNT);
619
620	COMPARE_N_ADD("FEM1_DegradedLowToNormalThr_2_4G", l, val,
621		&gp->degraded_low_to_normal_thr, 1);
622
623	COMPARE_N_ADD("FEM1_NormalToDegradedHighThr_2_4G", l, val,
624		&gp->normal_to_degraded_high_thr, 1);
625
626	COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_2_4G_11b", l, val,
627		gp->tx_per_chan_pwr_limits_11b,
628		WL1271_INI_CHANNEL_COUNT_2);
629
630	COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_2_4G_OFDM", l, val,
631		gp->tx_per_chan_pwr_limits_ofdm,
632		WL1271_INI_CHANNEL_COUNT_2);
633
634	COMPARE_N_ADD("FEM1_TxPDVsRateOffsets_2_4G", l, val,
635		gp->tx_pd_vs_rate_offsets,
636		WL128X_INI_RATE_GROUP_COUNT);
637
638	COMPARE_N_ADD("FEM1_TxPDVsChannelOffsets_2_4G", l, val,
639		gp->tx_pd_vs_chan_offsets,
640		WL1271_INI_CHANNEL_COUNT_2);
641
642	COMPARE_N_ADD("FEM1_TxPDVsTemperature_2_4G", l, val,
643		gp->tx_pd_vs_temperature,
644		WL128X_INI_PD_VS_TEMPERATURE_RANGES);
645
646	COMPARE_N_ADD("FEM1_TxIbiasTable_2_4G", l, val,
647		gp->tx_ibias,
648		WL128X_INI_RATE_GROUP_COUNT);
649
650	COMPARE_N_ADD("FEM1_RxFemInsertionLoss_2_4G", l, val,
651		&gp->rx_fem_insertion_loss, 1);
652
653	fprintf(stderr, "Unable to parse: (%s)\n", l);
654
655	return 1;
656}
657
658static int parse_fem1_band5_prms(char *l, struct wl12xx_ini *p)
659{
660	char *name, *val;
661	struct wl1271_ini_fem_params_5 *gp =
662		&(p->ini1271.dyn_radio_params_5[1].params);
663
664	if (split_line(l, &name, &val)) {
665		return 1;
666        }
667
668	COMPARE_N_ADD2("FEM1_TXBiPReferencePDvoltage_5G", l, val,
669		gp->tx_bip_ref_pd_voltage, WL1271_INI_SUB_BAND_COUNT_5);
670
671	COMPARE_N_ADD("FEM1_TxBiPReferencePower_5G", l, val,
672		gp->tx_bip_ref_power, WL1271_INI_SUB_BAND_COUNT_5);
673
674	COMPARE_N_ADD("FEM1_TxBiPOffsetdB_5G", l, val,
675		gp->tx_bip_ref_offset, WL1271_INI_SUB_BAND_COUNT_5);
676
677	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Normal", l, val,
678		gp->tx_per_rate_pwr_limits_normal,
679		WL1271_INI_RATE_GROUP_COUNT);
680
681	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Degraded", l, val,
682		gp->tx_per_rate_pwr_limits_degraded,
683		WL1271_INI_RATE_GROUP_COUNT);
684
685	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Extreme", l, val,
686		gp->tx_per_rate_pwr_limits_extreme,
687		WL1271_INI_RATE_GROUP_COUNT);
688
689	COMPARE_N_ADD("FEM1_DegradedLowToNormalThr_5G", l, val,
690		&gp->degraded_low_to_normal_thr, 1);
691
692	COMPARE_N_ADD("FEM1_NormalToDegradedHighThr_5G", l, val,
693		&gp->normal_to_degraded_high_thr, 1);
694
695	COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_5G_OFDM", l, val,
696		gp->tx_per_chan_pwr_limits_ofdm,
697		WL1271_INI_CHANNEL_COUNT_5);
698
699	COMPARE_N_ADD("FEM1_TxPDVsRateOffsets_5G", l, val,
700		gp->tx_pd_vs_rate_offsets,
701		WL1271_INI_RATE_GROUP_COUNT);
702
703	COMPARE_N_ADD("FEM1_TxIbiasTable_5G", l, val,
704		gp->tx_ibias,
705		WL1271_INI_RATE_GROUP_COUNT);
706
707	COMPARE_N_ADD("FEM1_RxFemInsertionLoss_5G", l, val,
708		gp->rx_fem_insertion_loss, WL1271_INI_SUB_BAND_COUNT_5);
709
710	fprintf(stderr, "Unable to parse: (%s)\n", l);
711
712	return 1;
713}
714
715static int parse_fem1_band5_prms_128x(char *l, struct wl12xx_ini *p)
716{
717	char *name, *val;
718	struct wl128x_ini_fem_params_5 *gp =
719		&(p->ini128x.dyn_radio_params_5[1].params);
720
721	if (split_line(l, &name, &val)) {
722		return 1;
723        }
724
725	COMPARE_N_ADD2("FEM1_TxBiPReferencePDvoltage_5G", l, val,
726		gp->tx_bip_ref_pd_voltage, WL1271_INI_SUB_BAND_COUNT_5);
727
728	COMPARE_N_ADD("FEM1_TxBiPReferencePower_5G", l, val,
729		gp->tx_bip_ref_power, WL1271_INI_SUB_BAND_COUNT_5);
730
731	COMPARE_N_ADD("FEM1_TxBiPOffsetdB_5G", l, val,
732		gp->tx_bip_ref_offset, WL1271_INI_SUB_BAND_COUNT_5);
733
734	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Normal", l, val,
735		gp->tx_per_rate_pwr_limits_normal,
736		WL128X_INI_RATE_GROUP_COUNT);
737
738	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Degraded", l, val,
739		gp->tx_per_rate_pwr_limits_degraded,
740		WL128X_INI_RATE_GROUP_COUNT);
741
742	COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Extreme", l, val,
743		gp->tx_per_rate_pwr_limits_extreme,
744		WL128X_INI_RATE_GROUP_COUNT);
745
746	COMPARE_N_ADD("FEM1_DegradedLowToNormalThr_5G", l, val,
747		&gp->degraded_low_to_normal_thr, 1);
748
749	COMPARE_N_ADD("FEM1_NormalToDegradedHighThr_5G", l, val,
750		&gp->normal_to_degraded_high_thr, 1);
751
752	COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_5G_OFDM", l, val,
753		gp->tx_per_chan_pwr_limits_ofdm,
754		WL1271_INI_CHANNEL_COUNT_5);
755
756	COMPARE_N_ADD("FEM1_TxPDVsRateOffsets_5G", l, val,
757		gp->tx_pd_vs_rate_offsets,
758		WL128X_INI_RATE_GROUP_COUNT);
759
760	COMPARE_N_ADD("FEM1_TxPDVsChannelOffsets_5G", l, val,
761		gp->tx_pd_vs_chan_offsets,
762		WL1271_INI_CHANNEL_COUNT_5);
763
764	COMPARE_N_ADD("FEM1_TxPDVsTemperature_5G", l, val,
765		gp->tx_pd_vs_temperature,
766		WL1271_INI_SUB_BAND_COUNT_5 * WL128X_INI_PD_VS_TEMPERATURE_RANGES);
767
768	COMPARE_N_ADD("FEM1_TxIbiasTable_5G", l, val,
769		gp->tx_ibias,
770		WL128X_INI_RATE_GROUP_COUNT);
771
772	COMPARE_N_ADD("FEM1_RxFemInsertionLoss_5G", l, val,
773		gp->rx_fem_insertion_loss, WL1271_INI_SUB_BAND_COUNT_5);
774
775	fprintf(stderr, "Unable to parse: (%s)\n", l);
776
777	return 1;
778}
779
780static int parse_fem_prms_128x(char *l, struct wl12xx_ini *p)
781{
782	char *name, *val;
783	struct wl128x_ini *gp = &p->ini128x;
784
785	if (split_line(l, &name, &val)) {
786		return 1;
787        }
788
789	COMPARE_N_ADD("FemVendorAndOptions", l, val,
790		&gp->fem_vendor_and_options, 1);
791
792	fprintf(stderr, "Unable to parse: (%s)\n", l);
793
794	return 1;
795}
796
797static int find_section(const char *l, enum wl1271_ini_section *st, int *cntr,
798	enum wl12xx_arch arch)
799{
800	if (strncmp("TXBiPFEMAutoDetect", l, 18) == 0) {
801		*st = GENERAL_PRMS;
802		if (arch == WL128X_ARCH) {
803			*cntr = 17;
804                } else {
805			*cntr = 12;
806                }
807
808		return 0;
809	}
810
811	if (strncmp("RxTraceInsertionLoss_2_4G", l, 25) == 0) {
812		*st = BAND2_PRMS;
813		if (arch == WL128X_ARCH){
814			*cntr = 2;
815                } else {
816			*cntr = 3;
817                }
818
819		return 0;
820	}
821
822	if (strncmp("FemVendorAndOptions", l, 19) == 0) {
823		*st = FEM_PRMS;
824		*cntr = 1;
825		return 0;
826	}
827
828	if (strncmp("RxTraceInsertionLoss_5G", l, 23) == 0) {
829		*st = BAND5_PRMS;
830		if (arch == WL128X_ARCH) {
831			*cntr = 2;
832		} else {
833			*cntr = 3;
834                }
835
836		return 0;
837	}
838
839	if (strncmp("FEM0_TXBiPReferencePDvoltage_2_4G", l, 33) == 0 ||
840		strncmp("FEM0_TxBiPReferencePDvoltage_2_4G", l, 33) == 0) {
841		*st = FEM0_BAND2_PRMS;
842		if (arch == WL128X_ARCH) {
843			*cntr = 15;
844		} else {
845			*cntr = 13;
846                }
847
848		return 0;
849	}
850
851	if (strncmp("FEM1_TXBiPReferencePDvoltage_2_4G", l, 33) == 0 ||
852		strncmp("FEM1_TxBiPReferencePDvoltage_2_4G", l, 33) == 0) {
853		*st = FEM1_BAND2_PRMS;
854		if (arch == WL128X_ARCH) {
855			*cntr = 15;
856                } else {
857			*cntr = 13;
858                }
859
860		return 0;
861	}
862
863	if (strncmp("FEM1_TXBiPReferencePDvoltage_5G", l, 31) == 0 ||
864		strncmp("FEM1_TxBiPReferencePDvoltage_5G", l, 31) == 0) {
865		*st = FEM1_BAND5_PRMS;
866		if (arch == WL128X_ARCH) {
867			*cntr = 14;
868		} else {
869			*cntr = 12;
870                }
871
872		return 0;
873	}
874
875	return 1;
876}
877
878static int ini_parse_line(char *l, int nbr, struct wl12xx_common *cmn)
879{
880	static enum wl1271_ini_section status;
881	static int cntr;
882
883	if (!cntr && find_section(l, &status, &cntr, cmn->arch)) {
884		fprintf(stderr, "Uknown ini section %s\n", l);
885		return 1;
886	}
887
888	switch (status) {
889	case GENERAL_PRMS:	/* general parameters */
890		cntr--;
891		return cmn->parse_ops->prs_general_prms(l, cmn, &cmn->ini);
892	case FEM_PRMS:	/* FEM parameters */
893		if (cmn->arch == WL1271_ARCH) {
894			fprintf(stderr, "The parameter not from 127x architecture\n");
895			return 1;
896		}
897		cntr--;
898		return parse_fem_prms_128x(l, &cmn->ini);
899	case BAND2_PRMS:	/* band 2.4GHz parameters */
900		cntr--;
901		return cmn->parse_ops->prs_band2_prms(l, &cmn->ini);
902	case BAND5_PRMS:	/* band 5GHz parameters */
903		cntr--;
904		return cmn->parse_ops->prs_band5_prms(l, &cmn->ini);
905	case FEM0_BAND2_PRMS:	/* FEM0 band 2.4GHz parameters */
906		cntr--;
907		return cmn->parse_ops->prs_fem0_band2_prms(l, &cmn->ini);
908	case FEM1_BAND2_PRMS:	/* FEM1 band 2.4GHz parameters */
909		cntr--;
910		return cmn->parse_ops->prs_fem1_band2_prms(l, &cmn->ini);
911	case FEM1_BAND5_PRMS:	/* FEM1 band 5GHz parameters */
912		cntr--;
913		return cmn->parse_ops->prs_fem1_band5_prms(l, &cmn->ini);
914	case UKNOWN_SECTION:
915		/* added because of compilation warning. handeled in find_section() */
916		break;
917	}
918
919	return 1;
920}
921
922#if 0
923static void ini_dump(struct wl1271_ini *ini)
924{
925	int i;
926
927	printf("\n");
928	printf("General params:\n");
929	printf("ref clock:                 %02X\n",
930		ini->general_params.ref_clock);
931	printf("settling time:             %02X\n",
932		ini->general_params.settling_time);
933	printf("clk valid on wakeup:       %02X\n",
934		ini->general_params.clk_valid_on_wakeup);
935	printf("dc2dc mode:                %02X\n",
936		ini->general_params.dc2dc_mode);
937	printf("dual band mode:            %02X\n",
938		ini->general_params.dual_mode_select);
939	printf("tx bip fem auto detect:    %02X\n",
940		ini->general_params.tx_bip_fem_auto_detect);
941	printf("tx bip fem manufacturer:   %02X\n",
942		ini->general_params.tx_bip_fem_manufacturer);
943	printf("general settings:          %02X\n",
944		ini->general_params.general_settings);
945	printf("sr state:                  %02X\n",
946		ini->general_params.sr_state);
947
948	printf("srf1:");
949	for (i = 0; i < WL1271_INI_MAX_SMART_REFLEX_PARAM; i++)
950		printf(" %02X", ini->general_params.srf1[i]);
951	printf("\n");
952
953	printf("srf2:");
954	for (i = 0; i < WL1271_INI_MAX_SMART_REFLEX_PARAM; i++)
955		printf(" %02X", ini->general_params.srf2[i]);
956	printf("\n");
957
958	printf("srf3:");
959	for (i = 0; i < WL1271_INI_MAX_SMART_REFLEX_PARAM; i++)
960		printf(" %02X", ini->general_params.srf3[i]);
961	printf("\n");
962
963	printf("Static 2.4 band params:\n");
964
965	printf("rx trace insertion loss: %02X\n",
966		ini->stat_radio_params_2.rx_trace_insertion_loss);
967
968	printf("rx rssi n process compensation:");
969	for (i = 0; i < WL1271_INI_RSSI_PROCESS_COMPENS_SIZE; i++)
970		printf(" %02X",
971			ini->stat_radio_params_2.rx_rssi_process_compens[i]);
972	printf("\n");
973
974	printf("tx trace: %02X\n",
975		ini->stat_radio_params_2.tx_trace_loss);
976
977	printf("Dynamic 2.4 band params for FEM\n");
978
979	printf("Static 5 band params:\n");
980
981	printf("rx trace insertion loss:");
982	for (i = 0; i < WL1271_INI_SUB_BAND_COUNT_5; i++)
983		printf(" %02X",
984			ini->stat_radio_params_5.rx_rssi_process_compens[i]);
985	printf("\n");
986
987	printf("rx rssi n process compensation:");
988	for (i = 0; i < WL1271_INI_RSSI_PROCESS_COMPENS_SIZE; i++)
989		printf(" %02X",
990			ini->stat_radio_params_5.rx_rssi_process_compens[i]);
991	printf("\n");
992
993	printf("tx trace:");
994	for (i = 0; i < WL1271_INI_SUB_BAND_COUNT_5; i++)
995		printf(" %02X",
996			ini->stat_radio_params_5.tx_trace_loss[i]);
997	printf("\n");
998
999	printf("Dynamic 5 band params for FEM\n");
1000
1001}
1002#endif
1003
1004static struct wl12xx_parse_ops wl1271_parse_ops = {
1005	.prs_general_prms       = parse_general_prms,
1006	.prs_band2_prms         = parse_band2_prms,
1007	.prs_band5_prms         = parse_band5_prms,
1008	.prs_fem0_band2_prms    = parse_fem0_band2_prms,
1009	.prs_fem1_band2_prms    = parse_fem1_band2_prms,
1010	.prs_fem1_band5_prms    = parse_fem1_band5_prms,
1011};
1012
1013static struct wl12xx_parse_ops wl128x_parse_ops = {
1014	.prs_general_prms       = parse_general_prms_128x,
1015	.prs_band2_prms         = parse_band2_prms_128x,
1016	.prs_band5_prms         = parse_band5_prms_128x,
1017	.prs_fem0_band2_prms    = parse_fem0_band2_prms_128x,
1018	.prs_fem1_band2_prms    = parse_fem1_band2_prms_128x,
1019	.prs_fem1_band5_prms    = parse_fem1_band5_prms_128x,
1020};
1021
1022int nvs_get_arch(int file_size, struct wl12xx_common *cmn)
1023{
1024	enum wl12xx_arch arch = UNKNOWN_ARCH;
1025
1026	switch (file_size) {
1027		case WL127X_NVS_FILE_SZ:
1028			arch = WL1271_ARCH;
1029			cmn->parse_ops = &wl1271_parse_ops;
1030			break;
1031		case WL128X_NVS_FILE_SZ:
1032			arch = WL128X_ARCH;
1033			cmn->parse_ops = &wl128x_parse_ops;
1034			break;
1035	}
1036
1037	if (cmn->arch != UNKNOWN_ARCH && cmn->arch != arch) {
1038		cmn->parse_ops = NULL;
1039		return 1;
1040	}
1041
1042	cmn->arch = arch;
1043
1044	return 0;
1045}
1046
1047static int ini_get_arch(FILE *f, struct wl12xx_common *cmn)
1048{
1049	char buf[1024], *pos;
1050	int line = 0;
1051	enum wl12xx_arch arch = UNKNOWN_ARCH;
1052
1053	while (ini_get_line(buf, sizeof(buf), f, &line, &pos)) {
1054		if (strncmp("TCXO_Clk", pos, 8) == 0) {
1055			arch = WL128X_ARCH;
1056			break;
1057		}
1058	}
1059
1060	if (arch == UNKNOWN_ARCH) {
1061		arch = WL1271_ARCH;
1062        }
1063
1064	if (cmn->arch != UNKNOWN_ARCH && cmn->arch != arch) {
1065		return 1;
1066        }
1067
1068	cmn->arch = arch;
1069
1070	if (cmn->arch == WL1271_ARCH) {
1071		cmn->parse_ops = &wl1271_parse_ops;
1072        } else {
1073		cmn->parse_ops = &wl128x_parse_ops;
1074        }
1075
1076	fseek(f, 0L, SEEK_SET);
1077
1078	return 0;
1079}
1080
1081int read_ini(const char *filename, struct wl12xx_common *cmn)
1082{
1083	FILE *f;
1084	char buf[1024], *pos;
1085	int ret = 0, line = 0;
1086
1087	f = fopen(filename, "r");
1088	if (f == NULL) {
1089		fprintf(stderr, "Unable to open file %s (%s)\n",
1090			filename, strerror(errno));
1091		return 1;
1092	}
1093
1094	/* check if it 127x or 128x */
1095	if (ini_get_arch(f, cmn)) {
1096		fprintf(stderr, "Unable to define wireless architecture\n");
1097		ret = 1;
1098		goto out;
1099	}
1100
1101	/* start parsing */
1102	while (ini_get_line(buf, sizeof(buf), f, &line, &pos)) {
1103		ret = ini_parse_line(pos, line, cmn);
1104		if (ret) break;
1105	}
1106
1107out:
1108	fclose(f);
1109#if 0
1110	ini_dump(ini);
1111#endif
1112	return ret;
1113}
1114