wl1251: add wl1251 prefix to all 1251 files
[pandora-kernel.git] / drivers / net / wireless / wl12xx / wl1251_netlink.c
1 /*
2  * This file is part of wl12xx
3  *
4  * Copyright (C) 2008 Nokia Corporation
5  *
6  * Contact: Kalle Valo <kalle.valo@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * version 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  *
22  */
23 #include "wl1251_netlink.h"
24
25 #include <linux/mutex.h>
26 #include <linux/socket.h>
27 #include <net/net_namespace.h>
28 #include <net/sock.h>
29 #include <net/genetlink.h>
30 #include <net/wireless.h>
31 #include <net/mac80211.h>
32
33 #include "wl12xx.h"
34 #include "wl1251_spi.h"
35 #include "wl1251_acx.h"
36
37 /* FIXME: this should be changed as soon as user space catches up */
38 #define WL12XX_NL_NAME "wl1251"
39 #define WL12XX_NL_VERSION 1
40
41 #define WL12XX_MAX_TEST_LENGTH 1024
42 #define WL12XX_MAX_NVS_LENGTH 1024
43
44 enum wl12xx_nl_commands {
45         WL12XX_NL_CMD_UNSPEC,
46         WL12XX_NL_CMD_TEST,
47         WL12XX_NL_CMD_INTERROGATE,
48         WL12XX_NL_CMD_CONFIGURE,
49         WL12XX_NL_CMD_PHY_REG_READ,
50         WL12XX_NL_CMD_NVS_PUSH,
51         WL12XX_NL_CMD_REG_WRITE,
52         WL12XX_NL_CMD_REG_READ,
53         WL12XX_NL_CMD_SET_PLT_MODE,
54
55         __WL12XX_NL_CMD_AFTER_LAST
56 };
57 #define WL12XX_NL_CMD_MAX (__WL12XX_NL_CMD_AFTER_LAST - 1)
58
59 enum wl12xx_nl_attrs {
60         WL12XX_NL_ATTR_UNSPEC,
61         WL12XX_NL_ATTR_IFNAME,
62         WL12XX_NL_ATTR_CMD_TEST_PARAM,
63         WL12XX_NL_ATTR_CMD_TEST_ANSWER,
64         WL12XX_NL_ATTR_CMD_IE,
65         WL12XX_NL_ATTR_CMD_IE_LEN,
66         WL12XX_NL_ATTR_CMD_IE_BUFFER,
67         WL12XX_NL_ATTR_CMD_IE_ANSWER,
68         WL12XX_NL_ATTR_REG_ADDR,
69         WL12XX_NL_ATTR_REG_VAL,
70         WL12XX_NL_ATTR_NVS_BUFFER,
71         WL12XX_NL_ATTR_NVS_LEN,
72         WL12XX_NL_ATTR_PLT_MODE,
73
74         __WL12XX_NL_ATTR_AFTER_LAST
75 };
76 #define WL12XX_NL_ATTR_MAX (__WL12XX_NL_ATTR_AFTER_LAST - 1)
77
78 static struct genl_family wl12xx_nl_family = {
79         .id = GENL_ID_GENERATE,
80         .name = WL12XX_NL_NAME,
81         .hdrsize = 0,
82         .version = WL12XX_NL_VERSION,
83         .maxattr = WL12XX_NL_ATTR_MAX,
84 };
85
86 static struct net_device *ifname_to_netdev(struct net *net,
87                                            struct genl_info *info)
88 {
89         char *ifname;
90
91         if (!info->attrs[WL12XX_NL_ATTR_IFNAME])
92                 return NULL;
93
94         ifname = nla_data(info->attrs[WL12XX_NL_ATTR_IFNAME]);
95
96         wl12xx_debug(DEBUG_NETLINK, "Looking for %s", ifname);
97
98         return dev_get_by_name(net, ifname);
99 }
100
101 static struct wl12xx *ifname_to_wl12xx(struct net *net, struct genl_info *info)
102 {
103         struct net_device *netdev;
104         struct wireless_dev *wdev;
105         struct wiphy *wiphy;
106         struct ieee80211_hw *hw;
107
108         netdev = ifname_to_netdev(net, info);
109         if (netdev == NULL) {
110                 wl12xx_error("Wrong interface");
111                 return NULL;
112         }
113
114         wdev = netdev->ieee80211_ptr;
115         if (wdev == NULL) {
116                 wl12xx_error("ieee80211_ptr is NULL");
117                 return NULL;
118         }
119
120         wiphy = wdev->wiphy;
121         if (wiphy == NULL) {
122                 wl12xx_error("wiphy is NULL");
123                 return NULL;
124         }
125
126         hw = wiphy_priv(wiphy);
127         if (hw == NULL) {
128                 wl12xx_error("hw is NULL");
129                 return NULL;
130         }
131
132         dev_put(netdev);
133
134         return hw->priv;
135 }
136
137 static int wl12xx_nl_test_cmd(struct sk_buff *skb, struct genl_info *info)
138 {
139         struct wl12xx *wl;
140         struct wl12xx_command *cmd;
141         char *buf;
142         int buf_len, ret, cmd_len;
143         u8 answer;
144
145         if (!info->attrs[WL12XX_NL_ATTR_CMD_TEST_PARAM])
146                 return -EINVAL;
147
148         wl = ifname_to_wl12xx(&init_net, info);
149         if (wl == NULL) {
150                 wl12xx_error("wl12xx not found");
151                 return -EINVAL;
152         }
153
154         cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
155         if (!cmd)
156                 return -ENOMEM;
157
158         buf = nla_data(info->attrs[WL12XX_NL_ATTR_CMD_TEST_PARAM]);
159         buf_len = nla_len(info->attrs[WL12XX_NL_ATTR_CMD_TEST_PARAM]);
160         answer = nla_get_u8(info->attrs[WL12XX_NL_ATTR_CMD_TEST_ANSWER]);
161
162         cmd->header.id = CMD_TEST;
163         memcpy(cmd->parameters, buf, buf_len);
164         cmd_len = sizeof(struct wl12xx_cmd_header) + buf_len;
165
166         mutex_lock(&wl->mutex);
167         ret = wl12xx_cmd_test(wl, cmd, cmd_len, answer);
168         mutex_unlock(&wl->mutex);
169
170         if (ret < 0) {
171                 wl12xx_error("%s() failed", __func__);
172                 goto out;
173         }
174
175         if (answer) {
176                 struct sk_buff *msg;
177                 void *hdr;
178
179                 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
180                 if (!msg) {
181                         ret = -ENOMEM;
182                         goto out;
183                 }
184
185                 hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
186                                   &wl12xx_nl_family, 0, WL12XX_NL_CMD_TEST);
187                 if (IS_ERR(hdr)) {
188                         ret = PTR_ERR(hdr);
189                         goto nla_put_failure;
190                 }
191
192                 NLA_PUT_STRING(msg, WL12XX_NL_ATTR_IFNAME,
193                                nla_data(info->attrs[WL12XX_NL_ATTR_IFNAME]));
194                 NLA_PUT(msg, WL12XX_NL_ATTR_CMD_TEST_ANSWER,
195                         sizeof(*cmd), cmd);
196
197                 ret = genlmsg_end(msg, hdr);
198                 if (ret < 0) {
199                         wl12xx_error("%s() failed", __func__);
200                         goto nla_put_failure;
201                 }
202
203                 wl12xx_debug(DEBUG_NETLINK, "TEST cmd sent, answer");
204                 ret = genlmsg_reply(msg, info);
205                 goto out;
206
207  nla_put_failure:
208                 nlmsg_free(msg);
209         } else
210                 wl12xx_debug(DEBUG_NETLINK, "TEST cmd sent");
211
212 out:
213         kfree(cmd);
214         return ret;
215 }
216
217 static int wl12xx_nl_interrogate(struct sk_buff *skb, struct genl_info *info)
218 {
219         struct wl12xx *wl;
220         struct sk_buff *msg;
221         int ret = -ENOBUFS, cmd_ie, cmd_ie_len;
222         struct wl12xx_command *cmd;
223         void *hdr;
224
225         if (!info->attrs[WL12XX_NL_ATTR_CMD_IE])
226                 return -EINVAL;
227
228         if (!info->attrs[WL12XX_NL_ATTR_CMD_IE_LEN])
229                 return -EINVAL;
230
231         cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
232         if (!cmd)
233                 return -ENOMEM;
234
235         msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
236         if (!msg)
237                 return -ENOMEM;
238
239         wl = ifname_to_wl12xx(&init_net, info);
240         if (wl == NULL) {
241                 wl12xx_error("wl12xx not found");
242                 ret = -EINVAL;
243                 goto nla_put_failure;
244         }
245
246         /* acx id */
247         cmd_ie = nla_get_u32(info->attrs[WL12XX_NL_ATTR_CMD_IE]);
248
249         /* maximum length of acx, including all headers */
250         cmd_ie_len = nla_get_u32(info->attrs[WL12XX_NL_ATTR_CMD_IE_LEN]);
251
252         wl12xx_debug(DEBUG_NETLINK, "Getting IE 0x%x (len %d)",
253                      cmd_ie, cmd_ie_len);
254
255         mutex_lock(&wl->mutex);
256         ret = wl12xx_cmd_interrogate(wl, cmd_ie, cmd, cmd_ie_len);
257         mutex_unlock(&wl->mutex);
258
259         if (ret < 0) {
260                 wl12xx_error("%s() failed", __func__);
261                 goto nla_put_failure;
262         }
263
264         hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
265                           &wl12xx_nl_family, 0, WL12XX_NL_CMD_INTERROGATE);
266         if (IS_ERR(hdr)) {
267                 ret = PTR_ERR(hdr);
268                 goto nla_put_failure;
269         }
270
271         NLA_PUT_STRING(msg, WL12XX_NL_ATTR_IFNAME,
272                        nla_data(info->attrs[WL12XX_NL_ATTR_IFNAME]));
273         NLA_PUT(msg, WL12XX_NL_ATTR_CMD_IE_ANSWER, cmd_ie_len, cmd);
274
275         ret = genlmsg_end(msg, hdr);
276         if (ret < 0) {
277                 wl12xx_error("%s() failed", __func__);
278                 goto nla_put_failure;
279         }
280
281         kfree(cmd);
282         return genlmsg_reply(msg, info);
283
284  nla_put_failure:
285         kfree(cmd);
286         nlmsg_free(msg);
287
288         return ret;
289 }
290
291 static int wl12xx_nl_configure(struct sk_buff *skb, struct genl_info *info)
292 {
293         int ret = 0, cmd_ie_len, acx_len;
294         struct acx_header *acx = NULL;
295         struct sk_buff *msg;
296         struct wl12xx *wl;
297         void *cmd_ie;
298         u16 *id;
299
300         if (!info->attrs[WL12XX_NL_ATTR_CMD_IE_BUFFER])
301                 return -EINVAL;
302
303         if (!info->attrs[WL12XX_NL_ATTR_CMD_IE_LEN])
304                 return -EINVAL;
305
306         msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
307         if (!msg)
308                 return -ENOMEM;
309
310         wl = ifname_to_wl12xx(&init_net, info);
311         if (wl == NULL) {
312                 wl12xx_error("wl12xx not found");
313                 ret = -EINVAL;
314                 goto nla_put_failure;
315         }
316
317         /* contains the acx header but not the cmd header */
318         cmd_ie = nla_data(info->attrs[WL12XX_NL_ATTR_CMD_IE_BUFFER]);
319
320         cmd_ie_len = nla_get_u32(info->attrs[WL12XX_NL_ATTR_CMD_IE_LEN]);
321
322         /* acx id is in the first two bytes */
323         id = cmd_ie;
324
325         /* need to add acx_header before cmd_ie, so create a new command */
326         acx_len = sizeof(struct acx_header) + cmd_ie_len;
327         acx = kzalloc(acx_len, GFP_KERNEL);
328         if (!acx) {
329                 ret = -ENOMEM;
330                 goto nla_put_failure;
331         }
332
333         /* copy the acx header and the payload */
334         memcpy(&acx->id, cmd_ie, cmd_ie_len);
335
336         mutex_lock(&wl->mutex);
337         ret = wl12xx_cmd_configure(wl, *id, acx, acx_len);
338         mutex_unlock(&wl->mutex);
339
340         if (ret < 0) {
341                 wl12xx_error("%s() failed", __func__);
342                 goto nla_put_failure;
343         }
344
345         wl12xx_debug(DEBUG_NETLINK, "CONFIGURE cmd sent");
346
347  nla_put_failure:
348         kfree(acx);
349         nlmsg_free(msg);
350
351         return ret;
352 }
353
354 static int wl12xx_nl_phy_reg_read(struct sk_buff *skb, struct genl_info *info)
355 {
356         struct wl12xx *wl;
357         struct sk_buff *msg;
358         u32 reg_addr, *reg_value = NULL;
359         int ret = 0;
360         void *hdr;
361
362         if (!info->attrs[WL12XX_NL_ATTR_REG_ADDR])
363                 return -EINVAL;
364
365         msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
366         if (!msg)
367                 return -ENOMEM;
368
369         wl = ifname_to_wl12xx(&init_net, info);
370         if (wl == NULL) {
371                 wl12xx_error("wl12xx not found");
372                 ret = -EINVAL;
373                 goto nla_put_failure;
374         }
375
376         reg_value = kmalloc(sizeof(*reg_value), GFP_KERNEL);
377         if (!reg_value) {
378                 ret = -ENOMEM;
379                 goto nla_put_failure;
380         }
381
382         reg_addr = nla_get_u32(info->attrs[WL12XX_NL_ATTR_REG_ADDR]);
383
384         wl12xx_debug(DEBUG_NETLINK, "Reading PHY reg 0x%x", reg_addr);
385
386         mutex_lock(&wl->mutex);
387         ret = wl12xx_cmd_read_memory(wl, reg_addr, reg_value,
388                                      sizeof(*reg_value));
389         mutex_unlock(&wl->mutex);
390
391         if (ret < 0) {
392                 wl12xx_error("%s() failed", __func__);
393                 goto nla_put_failure;
394         }
395
396
397         hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
398                           &wl12xx_nl_family, 0, WL12XX_NL_CMD_PHY_REG_READ);
399         if (IS_ERR(hdr)) {
400                 ret = PTR_ERR(hdr);
401                 goto nla_put_failure;
402         }
403
404         NLA_PUT_STRING(msg, WL12XX_NL_ATTR_IFNAME,
405                        nla_data(info->attrs[WL12XX_NL_ATTR_IFNAME]));
406
407         NLA_PUT_U32(msg, WL12XX_NL_ATTR_REG_VAL, *reg_value);
408
409         ret = genlmsg_end(msg, hdr);
410         if (ret < 0) {
411                 wl12xx_error("%s() failed", __func__);
412                 goto nla_put_failure;
413         }
414
415         kfree(reg_value);
416
417         return genlmsg_reply(msg, info);
418
419  nla_put_failure:
420         nlmsg_free(msg);
421         kfree(reg_value);
422
423         return ret;
424 }
425
426 static int wl12xx_nl_nvs_push(struct sk_buff *skb, struct genl_info *info)
427 {
428         struct wl12xx *wl;
429         int ret = 0;
430
431         if (!info->attrs[WL12XX_NL_ATTR_NVS_BUFFER])
432                 return -EINVAL;
433
434         if (!info->attrs[WL12XX_NL_ATTR_NVS_LEN])
435                 return -EINVAL;
436
437         wl = ifname_to_wl12xx(&init_net, info);
438         if (wl == NULL) {
439                 wl12xx_error("wl12xx not found");
440                 return -EINVAL;
441         }
442
443         mutex_lock(&wl->mutex);
444         wl->nvs_len = nla_get_u32(info->attrs[WL12XX_NL_ATTR_NVS_LEN]);
445         if (wl->nvs_len % 4) {
446                 wl12xx_error("NVS size is not multiple of 32: %d", wl->nvs_len);
447                 ret = -EILSEQ;
448                 goto out;
449         }
450
451         /* If we already have an NVS, we should free it */
452         kfree(wl->nvs);
453
454         wl->nvs = kzalloc(wl->nvs_len, GFP_KERNEL);
455         if (wl->nvs == NULL) {
456                 wl12xx_error("Can't allocate NVS");
457                 ret = -ENOMEM;
458                 goto out;
459         }
460
461         memcpy(wl->nvs,
462                nla_data(info->attrs[WL12XX_NL_ATTR_NVS_BUFFER]),
463                wl->nvs_len);
464
465         wl12xx_debug(DEBUG_NETLINK, "got NVS from userspace, %d bytes",
466                      wl->nvs_len);
467
468 out:
469         mutex_unlock(&wl->mutex);
470
471         return ret;
472 }
473
474 static int wl12xx_nl_reg_read(struct sk_buff *skb, struct genl_info *info)
475 {
476         struct wl12xx *wl;
477         u32 addr, val;
478         int ret = 0;
479         struct sk_buff *msg;
480         void *hdr;
481
482         if (!info->attrs[WL12XX_NL_ATTR_REG_ADDR])
483                 return -EINVAL;
484
485         msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
486         if (!msg)
487                 return -ENOMEM;
488
489         wl = ifname_to_wl12xx(&init_net, info);
490         if (wl == NULL) {
491                 wl12xx_error("wl12xx not found");
492                 return -EINVAL;
493         }
494
495         addr = nla_get_u32(info->attrs[WL12XX_NL_ATTR_REG_ADDR]);
496
497         mutex_lock(&wl->mutex);
498         val = wl12xx_reg_read32(wl, addr);
499         mutex_unlock(&wl->mutex);
500
501         hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
502                           &wl12xx_nl_family, 0, WL12XX_NL_CMD_PHY_REG_READ);
503         if (IS_ERR(hdr)) {
504                 ret = PTR_ERR(hdr);
505                 goto nla_put_failure;
506         }
507
508         NLA_PUT_STRING(msg, WL12XX_NL_ATTR_IFNAME,
509                        nla_data(info->attrs[WL12XX_NL_ATTR_IFNAME]));
510
511         NLA_PUT_U32(msg, WL12XX_NL_ATTR_REG_VAL, val);
512
513         ret = genlmsg_end(msg, hdr);
514         if (ret < 0) {
515                 wl12xx_error("%s() failed", __func__);
516                 goto nla_put_failure;
517         }
518
519         return genlmsg_reply(msg, info);
520
521  nla_put_failure:
522         nlmsg_free(msg);
523
524         return ret;
525 }
526
527 static int wl12xx_nl_reg_write(struct sk_buff *skb, struct genl_info *info)
528 {
529         struct wl12xx *wl;
530         u32 addr, val;
531
532         if (!info->attrs[WL12XX_NL_ATTR_REG_ADDR])
533                 return -EINVAL;
534
535         if (!info->attrs[WL12XX_NL_ATTR_REG_VAL])
536                 return -EINVAL;
537
538         wl = ifname_to_wl12xx(&init_net, info);
539         if (wl == NULL) {
540                 wl12xx_error("wl12xx not found");
541                 return -EINVAL;
542         }
543
544         addr = nla_get_u32(info->attrs[WL12XX_NL_ATTR_REG_ADDR]);
545         val = nla_get_u32(info->attrs[WL12XX_NL_ATTR_REG_VAL]);
546
547         mutex_lock(&wl->mutex);
548         wl12xx_reg_write32(wl, addr, val);
549         mutex_unlock(&wl->mutex);
550
551         return 0;
552 }
553
554 static int wl12xx_nl_set_plt_mode(struct sk_buff *skb, struct genl_info *info)
555 {
556         struct wl12xx *wl;
557         u32 val;
558         int ret;
559
560         if (!info->attrs[WL12XX_NL_ATTR_PLT_MODE])
561                 return -EINVAL;
562
563         wl = ifname_to_wl12xx(&init_net, info);
564         if (wl == NULL) {
565                 wl12xx_error("wl12xx not found");
566                 return -EINVAL;
567         }
568
569         val = nla_get_u32(info->attrs[WL12XX_NL_ATTR_PLT_MODE]);
570
571         switch (val) {
572         case 0:
573                 ret = wl12xx_plt_stop(wl);
574                 break;
575         case 1:
576                 ret = wl12xx_plt_start(wl);
577                 break;
578         default:
579                 ret = -EINVAL;
580                 break;
581         }
582
583         return ret;
584 }
585
586 static struct nla_policy wl12xx_nl_policy[WL12XX_NL_ATTR_MAX + 1] = {
587         [WL12XX_NL_ATTR_IFNAME] =            { .type = NLA_NUL_STRING,
588                                                .len = IFNAMSIZ-1 },
589         [WL12XX_NL_ATTR_CMD_TEST_PARAM] =    { .type = NLA_BINARY,
590                                                .len = WL12XX_MAX_TEST_LENGTH },
591         [WL12XX_NL_ATTR_CMD_TEST_ANSWER] =   { .type = NLA_U8 },
592         [WL12XX_NL_ATTR_CMD_IE] =            { .type = NLA_U32 },
593         [WL12XX_NL_ATTR_CMD_IE_LEN] =        { .type = NLA_U32 },
594         [WL12XX_NL_ATTR_CMD_IE_BUFFER] =     { .type = NLA_BINARY,
595                                                .len = WL12XX_MAX_TEST_LENGTH },
596         [WL12XX_NL_ATTR_CMD_IE_ANSWER] =     { .type = NLA_BINARY,
597                                                .len = WL12XX_MAX_TEST_LENGTH },
598         [WL12XX_NL_ATTR_REG_ADDR] =          { .type = NLA_U32 },
599         [WL12XX_NL_ATTR_REG_VAL] =           { .type = NLA_U32 },
600         [WL12XX_NL_ATTR_NVS_BUFFER] =        { .type = NLA_BINARY,
601                                                .len = WL12XX_MAX_NVS_LENGTH },
602         [WL12XX_NL_ATTR_NVS_LEN] =           { .type = NLA_U32 },
603         [WL12XX_NL_ATTR_PLT_MODE] =          { .type = NLA_U32 },
604 };
605
606 static struct genl_ops wl12xx_nl_ops[] = {
607         {
608                 .cmd = WL12XX_NL_CMD_TEST,
609                 .doit = wl12xx_nl_test_cmd,
610                 .policy = wl12xx_nl_policy,
611                 .flags = GENL_ADMIN_PERM,
612         },
613         {
614                 .cmd = WL12XX_NL_CMD_INTERROGATE,
615                 .doit = wl12xx_nl_interrogate,
616                 .policy = wl12xx_nl_policy,
617                 .flags = GENL_ADMIN_PERM,
618         },
619         {
620                 .cmd = WL12XX_NL_CMD_CONFIGURE,
621                 .doit = wl12xx_nl_configure,
622                 .policy = wl12xx_nl_policy,
623                 .flags = GENL_ADMIN_PERM,
624         },
625         {
626                 .cmd = WL12XX_NL_CMD_PHY_REG_READ,
627                 .doit = wl12xx_nl_phy_reg_read,
628                 .policy = wl12xx_nl_policy,
629                 .flags = GENL_ADMIN_PERM,
630         },
631         {
632                 .cmd = WL12XX_NL_CMD_NVS_PUSH,
633                 .doit = wl12xx_nl_nvs_push,
634                 .policy = wl12xx_nl_policy,
635                 .flags = GENL_ADMIN_PERM,
636         },
637         {
638                 .cmd = WL12XX_NL_CMD_REG_WRITE,
639                 .doit = wl12xx_nl_reg_write,
640                 .policy = wl12xx_nl_policy,
641                 .flags = GENL_ADMIN_PERM,
642         },
643         {
644                 .cmd = WL12XX_NL_CMD_REG_READ,
645                 .doit = wl12xx_nl_reg_read,
646                 .policy = wl12xx_nl_policy,
647                 .flags = GENL_ADMIN_PERM,
648         },
649         {
650                 .cmd = WL12XX_NL_CMD_SET_PLT_MODE,
651                 .doit = wl12xx_nl_set_plt_mode,
652                 .policy = wl12xx_nl_policy,
653                 .flags = GENL_ADMIN_PERM,
654         },
655 };
656
657 int wl12xx_nl_register(void)
658 {
659         int err, i;
660
661         err = genl_register_family(&wl12xx_nl_family);
662         if (err)
663                 return err;
664
665         for (i = 0; i < ARRAY_SIZE(wl12xx_nl_ops); i++) {
666                 err = genl_register_ops(&wl12xx_nl_family, &wl12xx_nl_ops[i]);
667                 if (err)
668                         goto err_out;
669         }
670         return 0;
671  err_out:
672         genl_unregister_family(&wl12xx_nl_family);
673         return err;
674 }
675
676 void wl12xx_nl_unregister(void)
677 {
678         genl_unregister_family(&wl12xx_nl_family);
679 }