1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
4  * All rights reserved.
5  */
6 
7 #include <linux/bitfield.h>
8 #include "wlan_if.h"
9 #include "wlan.h"
10 #include "wlan_cfg.h"
11 #include "netdev.h"
12 
13 enum cfg_cmd_type {
14 	CFG_BYTE_CMD	= 0,
15 	CFG_HWORD_CMD	= 1,
16 	CFG_WORD_CMD	= 2,
17 	CFG_STR_CMD	= 3,
18 	CFG_BIN_CMD	= 4
19 };
20 
21 static const struct wilc_cfg_byte g_cfg_byte[] = {
22 	{WID_STATUS, 0},
23 	{WID_RSSI, 0},
24 	{WID_LINKSPEED, 0},
25 	{WID_NIL, 0}
26 };
27 
28 static const struct wilc_cfg_hword g_cfg_hword[] = {
29 	{WID_NIL, 0}
30 };
31 
32 static const struct wilc_cfg_word g_cfg_word[] = {
33 	{WID_FAILED_COUNT, 0},
34 	{WID_RECEIVED_FRAGMENT_COUNT, 0},
35 	{WID_SUCCESS_FRAME_COUNT, 0},
36 	{WID_GET_INACTIVE_TIME, 0},
37 	{WID_NIL, 0}
38 
39 };
40 
41 static const struct wilc_cfg_str g_cfg_str[] = {
42 	{WID_FIRMWARE_VERSION, NULL},
43 	{WID_MAC_ADDR, NULL},
44 	{WID_ASSOC_RES_INFO, NULL},
45 	{WID_NIL, NULL}
46 };
47 
48 #define WILC_RESP_MSG_TYPE_CONFIG_REPLY		'R'
49 #define WILC_RESP_MSG_TYPE_STATUS_INFO		'I'
50 #define WILC_RESP_MSG_TYPE_NETWORK_INFO		'N'
51 #define WILC_RESP_MSG_TYPE_SCAN_COMPLETE	'S'
52 
53 /********************************************
54  *
55  *      Configuration Functions
56  *
57  ********************************************/
58 
59 static int wilc_wlan_cfg_set_byte(u8 *frame, u32 offset, u16 id, u8 val8)
60 {
61 	if ((offset + 4) >= WILC_MAX_CFG_FRAME_SIZE)
62 		return 0;
63 
64 	put_unaligned_le16(id, &frame[offset]);
65 	put_unaligned_le16(1, &frame[offset + 2]);
66 	frame[offset + 4] = val8;
67 	return 5;
68 }
69 
70 static int wilc_wlan_cfg_set_hword(u8 *frame, u32 offset, u16 id, u16 val16)
71 {
72 	if ((offset + 5) >= WILC_MAX_CFG_FRAME_SIZE)
73 		return 0;
74 
75 	put_unaligned_le16(id, &frame[offset]);
76 	put_unaligned_le16(2, &frame[offset + 2]);
77 	put_unaligned_le16(val16, &frame[offset + 4]);
78 
79 	return 6;
80 }
81 
82 static int wilc_wlan_cfg_set_word(u8 *frame, u32 offset, u16 id, u32 val32)
83 {
84 	if ((offset + 7) >= WILC_MAX_CFG_FRAME_SIZE)
85 		return 0;
86 
87 	put_unaligned_le16(id, &frame[offset]);
88 	put_unaligned_le16(4, &frame[offset + 2]);
89 	put_unaligned_le32(val32, &frame[offset + 4]);
90 
91 	return 8;
92 }
93 
94 static int wilc_wlan_cfg_set_str(u8 *frame, u32 offset, u16 id, u8 *str,
95 				 u32 size)
96 {
97 	if ((offset + size + 4) >= WILC_MAX_CFG_FRAME_SIZE)
98 		return 0;
99 
100 	put_unaligned_le16(id, &frame[offset]);
101 	put_unaligned_le16(size, &frame[offset + 2]);
102 	if (str && size != 0)
103 		memcpy(&frame[offset + 4], str, size);
104 
105 	return (size + 4);
106 }
107 
108 static int wilc_wlan_cfg_set_bin(u8 *frame, u32 offset, u16 id, u8 *b, u32 size)
109 {
110 	u32 i;
111 	u8 checksum = 0;
112 
113 	if ((offset + size + 5) >= WILC_MAX_CFG_FRAME_SIZE)
114 		return 0;
115 
116 	put_unaligned_le16(id, &frame[offset]);
117 	put_unaligned_le16(size, &frame[offset + 2]);
118 
119 	if ((b) && size != 0) {
120 		memcpy(&frame[offset + 4], b, size);
121 		for (i = 0; i < size; i++)
122 			checksum += frame[offset + i + 4];
123 	}
124 
125 	frame[offset + size + 4] = checksum;
126 
127 	return (size + 5);
128 }
129 
130 /********************************************
131  *
132  *      Configuration Response Functions
133  *
134  ********************************************/
135 
136 static void wilc_wlan_parse_response_frame(struct wilc *wl, u8 *info, int size)
137 {
138 	u16 wid;
139 	u32 len = 0, i = 0;
140 	struct wilc_cfg *cfg = &wl->cfg;
141 
142 	while (size > 0) {
143 		i = 0;
144 		wid = get_unaligned_le16(info);
145 
146 		switch (FIELD_GET(WILC_WID_TYPE, wid)) {
147 		case WID_CHAR:
148 			while (cfg->b[i].id != WID_NIL && cfg->b[i].id != wid)
149 				i++;
150 
151 			if (cfg->b[i].id == wid)
152 				cfg->b[i].val = info[4];
153 
154 			len = 3;
155 			break;
156 
157 		case WID_SHORT:
158 			while (cfg->hw[i].id != WID_NIL && cfg->hw[i].id != wid)
159 				i++;
160 
161 			if (cfg->hw[i].id == wid)
162 				cfg->hw[i].val = get_unaligned_le16(&info[4]);
163 
164 			len = 4;
165 			break;
166 
167 		case WID_INT:
168 			while (cfg->w[i].id != WID_NIL && cfg->w[i].id != wid)
169 				i++;
170 
171 			if (cfg->w[i].id == wid)
172 				cfg->w[i].val = get_unaligned_le32(&info[4]);
173 
174 			len = 6;
175 			break;
176 
177 		case WID_STR:
178 			while (cfg->s[i].id != WID_NIL && cfg->s[i].id != wid)
179 				i++;
180 
181 			if (cfg->s[i].id == wid)
182 				memcpy(cfg->s[i].str, &info[2], info[2] + 2);
183 
184 			len = 2 + info[2];
185 			break;
186 
187 		default:
188 			break;
189 		}
190 		size -= (2 + len);
191 		info += (2 + len);
192 	}
193 }
194 
195 static void wilc_wlan_parse_info_frame(struct wilc *wl, u8 *info)
196 {
197 	u32 wid, len;
198 
199 	wid = get_unaligned_le16(info);
200 
201 	len = info[2];
202 
203 	if (len == 1 && wid == WID_STATUS) {
204 		int i = 0;
205 
206 		while (wl->cfg.b[i].id != WID_NIL &&
207 		       wl->cfg.b[i].id != wid)
208 			i++;
209 
210 		if (wl->cfg.b[i].id == wid)
211 			wl->cfg.b[i].val = info[3];
212 	}
213 }
214 
215 /********************************************
216  *
217  *      Configuration Exported Functions
218  *
219  ********************************************/
220 
221 int wilc_wlan_cfg_set_wid(u8 *frame, u32 offset, u16 id, u8 *buf, int size)
222 {
223 	u8 type = FIELD_GET(WILC_WID_TYPE, id);
224 	int ret = 0;
225 
226 	switch (type) {
227 	case CFG_BYTE_CMD:
228 		if (size >= 1)
229 			ret = wilc_wlan_cfg_set_byte(frame, offset, id, *buf);
230 		break;
231 
232 	case CFG_HWORD_CMD:
233 		if (size >= 2)
234 			ret = wilc_wlan_cfg_set_hword(frame, offset, id,
235 						      *((u16 *)buf));
236 		break;
237 
238 	case CFG_WORD_CMD:
239 		if (size >= 4)
240 			ret = wilc_wlan_cfg_set_word(frame, offset, id,
241 						     *((u32 *)buf));
242 		break;
243 
244 	case CFG_STR_CMD:
245 		ret = wilc_wlan_cfg_set_str(frame, offset, id, buf, size);
246 		break;
247 
248 	case CFG_BIN_CMD:
249 		ret = wilc_wlan_cfg_set_bin(frame, offset, id, buf, size);
250 		break;
251 	}
252 
253 	return ret;
254 }
255 
256 int wilc_wlan_cfg_get_wid(u8 *frame, u32 offset, u16 id)
257 {
258 	if ((offset + 2) >= WILC_MAX_CFG_FRAME_SIZE)
259 		return 0;
260 
261 	put_unaligned_le16(id, &frame[offset]);
262 
263 	return 2;
264 }
265 
266 int wilc_wlan_cfg_get_val(struct wilc *wl, u16 wid, u8 *buffer,
267 			  u32 buffer_size)
268 {
269 	u8 type = FIELD_GET(WILC_WID_TYPE, wid);
270 	int i, ret = 0;
271 	struct wilc_cfg *cfg = &wl->cfg;
272 
273 	i = 0;
274 	if (type == CFG_BYTE_CMD) {
275 		while (cfg->b[i].id != WID_NIL && cfg->b[i].id != wid)
276 			i++;
277 
278 		if (cfg->b[i].id == wid) {
279 			memcpy(buffer, &cfg->b[i].val, 1);
280 			ret = 1;
281 		}
282 	} else if (type == CFG_HWORD_CMD) {
283 		while (cfg->hw[i].id != WID_NIL && cfg->hw[i].id != wid)
284 			i++;
285 
286 		if (cfg->hw[i].id == wid) {
287 			memcpy(buffer, &cfg->hw[i].val, 2);
288 			ret = 2;
289 		}
290 	} else if (type == CFG_WORD_CMD) {
291 		while (cfg->w[i].id != WID_NIL && cfg->w[i].id != wid)
292 			i++;
293 
294 		if (cfg->w[i].id == wid) {
295 			memcpy(buffer, &cfg->w[i].val, 4);
296 			ret = 4;
297 		}
298 	} else if (type == CFG_STR_CMD) {
299 		while (cfg->s[i].id != WID_NIL && cfg->s[i].id != wid)
300 			i++;
301 
302 		if (cfg->s[i].id == wid) {
303 			u16 size = get_unaligned_le16(cfg->s[i].str);
304 
305 			if (buffer_size >= size) {
306 				memcpy(buffer, &cfg->s[i].str[2], size);
307 				ret = size;
308 			}
309 		}
310 	}
311 	return ret;
312 }
313 
314 void wilc_wlan_cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
315 			       struct wilc_cfg_rsp *rsp)
316 {
317 	u8 msg_type;
318 	u8 msg_id;
319 
320 	msg_type = frame[0];
321 	msg_id = frame[1];      /* seq no */
322 	frame += 4;
323 	size -= 4;
324 	rsp->type = 0;
325 
326 	switch (msg_type) {
327 	case WILC_RESP_MSG_TYPE_CONFIG_REPLY:
328 		wilc_wlan_parse_response_frame(wilc, frame, size);
329 		rsp->type = WILC_CFG_RSP;
330 		rsp->seq_no = msg_id;
331 		break;
332 
333 	case WILC_RESP_MSG_TYPE_STATUS_INFO:
334 		wilc_wlan_parse_info_frame(wilc, frame);
335 		rsp->type = WILC_CFG_RSP_STATUS;
336 		rsp->seq_no = msg_id;
337 		/* call host interface info parse as well */
338 		wilc_gnrl_async_info_received(wilc, frame - 4, size + 4);
339 		break;
340 
341 	case WILC_RESP_MSG_TYPE_NETWORK_INFO:
342 		wilc_network_info_received(wilc, frame - 4, size + 4);
343 		break;
344 
345 	case WILC_RESP_MSG_TYPE_SCAN_COMPLETE:
346 		wilc_scan_complete_received(wilc, frame - 4, size + 4);
347 		break;
348 
349 	default:
350 		rsp->seq_no = msg_id;
351 		break;
352 	}
353 }
354 
355 int wilc_wlan_cfg_init(struct wilc *wl)
356 {
357 	struct wilc_cfg_str_vals *str_vals;
358 	int i = 0;
359 
360 	wl->cfg.b = kmemdup(g_cfg_byte, sizeof(g_cfg_byte), GFP_KERNEL);
361 	if (!wl->cfg.b)
362 		return -ENOMEM;
363 
364 	wl->cfg.hw = kmemdup(g_cfg_hword, sizeof(g_cfg_hword), GFP_KERNEL);
365 	if (!wl->cfg.hw)
366 		goto out_b;
367 
368 	wl->cfg.w = kmemdup(g_cfg_word, sizeof(g_cfg_word), GFP_KERNEL);
369 	if (!wl->cfg.w)
370 		goto out_hw;
371 
372 	wl->cfg.s = kmemdup(g_cfg_str, sizeof(g_cfg_str), GFP_KERNEL);
373 	if (!wl->cfg.s)
374 		goto out_w;
375 
376 	str_vals = kzalloc(sizeof(*str_vals), GFP_KERNEL);
377 	if (!str_vals)
378 		goto out_s;
379 
380 	wl->cfg.str_vals = str_vals;
381 	/* store the string cfg parameters */
382 	wl->cfg.s[i].id = WID_FIRMWARE_VERSION;
383 	wl->cfg.s[i].str = str_vals->firmware_version;
384 	i++;
385 	wl->cfg.s[i].id = WID_MAC_ADDR;
386 	wl->cfg.s[i].str = str_vals->mac_address;
387 	i++;
388 	wl->cfg.s[i].id = WID_ASSOC_RES_INFO;
389 	wl->cfg.s[i].str = str_vals->assoc_rsp;
390 	i++;
391 	wl->cfg.s[i].id = WID_NIL;
392 	wl->cfg.s[i].str = NULL;
393 	return 0;
394 
395 out_s:
396 	kfree(wl->cfg.s);
397 out_w:
398 	kfree(wl->cfg.w);
399 out_hw:
400 	kfree(wl->cfg.hw);
401 out_b:
402 	kfree(wl->cfg.b);
403 	return -ENOMEM;
404 }
405 
406 void wilc_wlan_cfg_deinit(struct wilc *wl)
407 {
408 	kfree(wl->cfg.b);
409 	kfree(wl->cfg.hw);
410 	kfree(wl->cfg.w);
411 	kfree(wl->cfg.s);
412 	kfree(wl->cfg.str_vals);
413 }
414