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