Merge tag 'staging-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[sfrench/cifs-2.6.git] / drivers / staging / wilc1000 / wilc_wlan_cfg.c
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 "wilc_wlan_if.h"
8 #include "wilc_wlan.h"
9 #include "wilc_wlan_cfg.h"
10 #include "wilc_wfi_netdevice.h"
11
12 enum cfg_cmd_type {
13         CFG_BYTE_CMD    = 0,
14         CFG_HWORD_CMD   = 1,
15         CFG_WORD_CMD    = 2,
16         CFG_STR_CMD     = 3,
17         CFG_BIN_CMD     = 4
18 };
19
20 static const struct wilc_cfg_byte g_cfg_byte[] = {
21         {WID_STATUS, 0},
22         {WID_RSSI, 0},
23         {WID_LINKSPEED, 0},
24         {WID_NIL, 0}
25 };
26
27 static const struct wilc_cfg_hword g_cfg_hword[] = {
28         {WID_NIL, 0}
29 };
30
31 static const struct wilc_cfg_word g_cfg_word[] = {
32         {WID_FAILED_COUNT, 0},
33         {WID_RECEIVED_FRAGMENT_COUNT, 0},
34         {WID_SUCCESS_FRAME_COUNT, 0},
35         {WID_GET_INACTIVE_TIME, 0},
36         {WID_NIL, 0}
37
38 };
39
40 static const struct wilc_cfg_str g_cfg_str[] = {
41         {WID_FIRMWARE_VERSION, NULL},
42         {WID_MAC_ADDR, NULL},
43         {WID_ASSOC_RES_INFO, NULL},
44         {WID_NIL, NULL}
45 };
46
47 /********************************************
48  *
49  *      Configuration Functions
50  *
51  ********************************************/
52
53 static int wilc_wlan_cfg_set_byte(u8 *frame, u32 offset, u16 id, u8 val8)
54 {
55         u8 *buf;
56
57         if ((offset + 4) >= MAX_CFG_FRAME_SIZE)
58                 return 0;
59
60         buf = &frame[offset];
61
62         buf[0] = (u8)id;
63         buf[1] = (u8)(id >> 8);
64         buf[2] = 1;
65         buf[3] = 0;
66         buf[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         u8 *buf;
73
74         if ((offset + 5) >= MAX_CFG_FRAME_SIZE)
75                 return 0;
76
77         buf = &frame[offset];
78
79         buf[0] = (u8)id;
80         buf[1] = (u8)(id >> 8);
81         buf[2] = 2;
82         buf[3] = 0;
83         buf[4] = (u8)val16;
84         buf[5] = (u8)(val16 >> 8);
85
86         return 6;
87 }
88
89 static int wilc_wlan_cfg_set_word(u8 *frame, u32 offset, u16 id, u32 val32)
90 {
91         u8 *buf;
92
93         if ((offset + 7) >= MAX_CFG_FRAME_SIZE)
94                 return 0;
95
96         buf = &frame[offset];
97
98         buf[0] = (u8)id;
99         buf[1] = (u8)(id >> 8);
100         buf[2] = 4;
101         buf[3] = 0;
102         buf[4] = (u8)val32;
103         buf[5] = (u8)(val32 >> 8);
104         buf[6] = (u8)(val32 >> 16);
105         buf[7] = (u8)(val32 >> 24);
106
107         return 8;
108 }
109
110 static int wilc_wlan_cfg_set_str(u8 *frame, u32 offset, u16 id, u8 *str,
111                                  u32 size)
112 {
113         u8 *buf;
114
115         if ((offset + size + 4) >= MAX_CFG_FRAME_SIZE)
116                 return 0;
117
118         buf = &frame[offset];
119
120         buf[0] = (u8)id;
121         buf[1] = (u8)(id >> 8);
122         buf[2] = (u8)size;
123         buf[3] = (u8)(size >> 8);
124
125         if (str && size != 0)
126                 memcpy(&buf[4], str, size);
127
128         return (size + 4);
129 }
130
131 static int wilc_wlan_cfg_set_bin(u8 *frame, u32 offset, u16 id, u8 *b, u32 size)
132 {
133         u8 *buf;
134         u32 i;
135         u8 checksum = 0;
136
137         if ((offset + size + 5) >= MAX_CFG_FRAME_SIZE)
138                 return 0;
139
140         buf = &frame[offset];
141         buf[0] = (u8)id;
142         buf[1] = (u8)(id >> 8);
143         buf[2] = (u8)size;
144         buf[3] = (u8)(size >> 8);
145
146         if ((b) && size != 0) {
147                 memcpy(&buf[4], b, size);
148                 for (i = 0; i < size; i++)
149                         checksum += buf[i + 4];
150         }
151
152         buf[size + 4] = checksum;
153
154         return (size + 5);
155 }
156
157 /********************************************
158  *
159  *      Configuration Response Functions
160  *
161  ********************************************/
162
163 #define GET_WID_TYPE(wid)               (((wid) >> 12) & 0x7)
164 static void wilc_wlan_parse_response_frame(struct wilc *wl, u8 *info, int size)
165 {
166         u16 wid;
167         u32 len = 0, i = 0;
168
169         while (size > 0) {
170                 i = 0;
171                 wid = info[0] | (info[1] << 8);
172
173                 switch (GET_WID_TYPE(wid)) {
174                 case WID_CHAR:
175                         do {
176                                 if (wl->cfg.b[i].id == WID_NIL)
177                                         break;
178
179                                 if (wl->cfg.b[i].id == wid) {
180                                         wl->cfg.b[i].val = info[4];
181                                         break;
182                                 }
183                                 i++;
184                         } while (1);
185                         len = 3;
186                         break;
187
188                 case WID_SHORT:
189                         do {
190                                 if (wl->cfg.hw[i].id == WID_NIL)
191                                         break;
192
193                                 if (wl->cfg.hw[i].id == wid) {
194                                         wl->cfg.hw[i].val = (info[4] |
195                                                              (info[5] << 8));
196                                         break;
197                                 }
198                                 i++;
199                         } while (1);
200                         len = 4;
201                         break;
202
203                 case WID_INT:
204                         do {
205                                 if (wl->cfg.w[i].id == WID_NIL)
206                                         break;
207
208                                 if (wl->cfg.w[i].id == wid) {
209                                         wl->cfg.w[i].val = (info[4] |
210                                                             (info[5] << 8) |
211                                                             (info[6] << 16) |
212                                                             (info[7] << 24));
213                                         break;
214                                 }
215                                 i++;
216                         } while (1);
217                         len = 6;
218                         break;
219
220                 case WID_STR:
221                         do {
222                                 if (wl->cfg.s[i].id == WID_NIL)
223                                         break;
224
225                                 if (wl->cfg.s[i].id == wid) {
226                                         memcpy(wl->cfg.s[i].str, &info[2],
227                                                (info[2] + 2));
228                                         break;
229                                 }
230                                 i++;
231                         } while (1);
232                         len = 2 + info[2];
233                         break;
234
235                 default:
236                         break;
237                 }
238                 size -= (2 + len);
239                 info += (2 + len);
240         }
241 }
242
243 static void wilc_wlan_parse_info_frame(struct wilc *wl, u8 *info)
244 {
245         u32 wid, len;
246
247         wid = info[0] | (info[1] << 8);
248
249         len = info[2];
250
251         if (len == 1 && wid == WID_STATUS) {
252                 int i = 0;
253
254                 do {
255                         if (wl->cfg.b[i].id == WID_NIL)
256                                 break;
257
258                         if (wl->cfg.b[i].id == wid) {
259                                 wl->cfg.b[i].val = info[3];
260                                 break;
261                         }
262                         i++;
263                 } while (1);
264         }
265 }
266
267 /********************************************
268  *
269  *      Configuration Exported Functions
270  *
271  ********************************************/
272
273 int wilc_wlan_cfg_set_wid(u8 *frame, u32 offset, u16 id, u8 *buf, int size)
274 {
275         u8 type = (id >> 12) & 0xf;
276         int ret = 0;
277
278         switch (type) {
279         case CFG_BYTE_CMD:
280                 if (size >= 1)
281                         ret = wilc_wlan_cfg_set_byte(frame, offset, id, *buf);
282                 break;
283
284         case CFG_HWORD_CMD:
285                 if (size >= 2)
286                         ret = wilc_wlan_cfg_set_hword(frame, offset, id,
287                                                       *((u16 *)buf));
288                 break;
289
290         case CFG_WORD_CMD:
291                 if (size >= 4)
292                         ret = wilc_wlan_cfg_set_word(frame, offset, id,
293                                                      *((u32 *)buf));
294                 break;
295
296         case CFG_STR_CMD:
297                 ret = wilc_wlan_cfg_set_str(frame, offset, id, buf, size);
298                 break;
299
300         case CFG_BIN_CMD:
301                 ret = wilc_wlan_cfg_set_bin(frame, offset, id, buf, size);
302                 break;
303         }
304
305         return ret;
306 }
307
308 int wilc_wlan_cfg_get_wid(u8 *frame, u32 offset, u16 id)
309 {
310         u8 *buf;
311
312         if ((offset + 2) >= MAX_CFG_FRAME_SIZE)
313                 return 0;
314
315         buf = &frame[offset];
316
317         buf[0] = (u8)id;
318         buf[1] = (u8)(id >> 8);
319
320         return 2;
321 }
322
323 int wilc_wlan_cfg_get_wid_value(struct wilc *wl, u16 wid, u8 *buffer,
324                                 u32 buffer_size)
325 {
326         u32 type = (wid >> 12) & 0xf;
327         int i, ret = 0;
328
329         i = 0;
330         if (type == CFG_BYTE_CMD) {
331                 do {
332                         if (wl->cfg.b[i].id == WID_NIL)
333                                 break;
334
335                         if (wl->cfg.b[i].id == wid) {
336                                 memcpy(buffer, &wl->cfg.b[i].val, 1);
337                                 ret = 1;
338                                 break;
339                         }
340                         i++;
341                 } while (1);
342         } else if (type == CFG_HWORD_CMD) {
343                 do {
344                         if (wl->cfg.hw[i].id == WID_NIL)
345                                 break;
346
347                         if (wl->cfg.hw[i].id == wid) {
348                                 memcpy(buffer, &wl->cfg.hw[i].val, 2);
349                                 ret = 2;
350                                 break;
351                         }
352                         i++;
353                 } while (1);
354         } else if (type == CFG_WORD_CMD) {
355                 do {
356                         if (wl->cfg.w[i].id == WID_NIL)
357                                 break;
358
359                         if (wl->cfg.w[i].id == wid) {
360                                 memcpy(buffer, &wl->cfg.w[i].val, 4);
361                                 ret = 4;
362                                 break;
363                         }
364                         i++;
365                 } while (1);
366         } else if (type == CFG_STR_CMD) {
367                 do {
368                         u32 id = wl->cfg.s[i].id;
369
370                         if (id == WID_NIL)
371                                 break;
372
373                         if (id == wid) {
374                                 u32 size = (wl->cfg.s[i].str[0] |
375                                             (wl->cfg.s[i].str[1] << 8));
376
377                                 if (buffer_size >= size) {
378                                         memcpy(buffer, &wl->cfg.s[i].str[2],
379                                                size);
380                                         ret = size;
381                                 }
382                                 break;
383                         }
384                         i++;
385                 } while (1);
386         }
387         return ret;
388 }
389
390 void wilc_wlan_cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
391                                struct wilc_cfg_rsp *rsp)
392 {
393         u8 msg_type;
394         u8 msg_id;
395
396         msg_type = frame[0];
397         msg_id = frame[1];      /* seq no */
398         frame += 4;
399         size -= 4;
400         rsp->type = 0;
401
402         /*
403          * The valid types of response messages are
404          * 'R' (Response),
405          * 'I' (Information), and
406          * 'N' (Network Information)
407          */
408
409         switch (msg_type) {
410         case 'R':
411                 wilc_wlan_parse_response_frame(wilc, frame, size);
412                 rsp->type = WILC_CFG_RSP;
413                 rsp->seq_no = msg_id;
414                 break;
415
416         case 'I':
417                 wilc_wlan_parse_info_frame(wilc, frame);
418                 rsp->type = WILC_CFG_RSP_STATUS;
419                 rsp->seq_no = msg_id;
420                 /*call host interface info parse as well*/
421                 wilc_gnrl_async_info_received(wilc, frame - 4, size + 4);
422                 break;
423
424         case 'N':
425                 wilc_network_info_received(wilc, frame - 4, size + 4);
426                 break;
427
428         case 'S':
429                 wilc_scan_complete_received(wilc, frame - 4, size + 4);
430                 break;
431
432         default:
433                 rsp->seq_no = msg_id;
434                 break;
435         }
436 }
437
438 int wilc_wlan_cfg_init(struct wilc *wl)
439 {
440         struct wilc_cfg_str_vals *str_vals;
441         int i = 0;
442
443         wl->cfg.b = kmemdup(g_cfg_byte, sizeof(g_cfg_byte), GFP_KERNEL);
444         if (!wl->cfg.b)
445                 return -ENOMEM;
446
447         wl->cfg.hw = kmemdup(g_cfg_hword, sizeof(g_cfg_hword), GFP_KERNEL);
448         if (!wl->cfg.hw)
449                 goto out_b;
450
451         wl->cfg.w = kmemdup(g_cfg_word, sizeof(g_cfg_word), GFP_KERNEL);
452         if (!wl->cfg.w)
453                 goto out_hw;
454
455         wl->cfg.s = kmemdup(g_cfg_str, sizeof(g_cfg_str), GFP_KERNEL);
456         if (!wl->cfg.s)
457                 goto out_w;
458
459         str_vals = kzalloc(sizeof(*str_vals), GFP_KERNEL);
460         if (!str_vals)
461                 goto out_s;
462
463         wl->cfg.str_vals = str_vals;
464         /* store the string cfg parameters */
465         wl->cfg.s[i].id = WID_FIRMWARE_VERSION;
466         wl->cfg.s[i].str = str_vals->firmware_version;
467         i++;
468         wl->cfg.s[i].id = WID_MAC_ADDR;
469         wl->cfg.s[i].str = str_vals->mac_address;
470         i++;
471         wl->cfg.s[i].id = WID_ASSOC_RES_INFO;
472         wl->cfg.s[i].str = str_vals->assoc_rsp;
473         i++;
474         wl->cfg.s[i].id = WID_NIL;
475         wl->cfg.s[i].str = NULL;
476         return 0;
477
478 out_s:
479         kfree(wl->cfg.s);
480 out_w:
481         kfree(wl->cfg.w);
482 out_hw:
483         kfree(wl->cfg.hw);
484 out_b:
485         kfree(wl->cfg.b);
486         return -ENOMEM;
487 }
488
489 void wilc_wlan_cfg_deinit(struct wilc *wl)
490 {
491         kfree(wl->cfg.b);
492         kfree(wl->cfg.hw);
493         kfree(wl->cfg.w);
494         kfree(wl->cfg.s);
495         kfree(wl->cfg.str_vals);
496 }