Merge branch develop-3.10-next
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / rockchip_wlan / rkwifi / bcmdhd / dhd_cfg_vendor.c
1 /*
2  * Linux cfg80211 vendor command/event handlers of DHD
3  *
4  * $Copyright Open Broadcom Corporation$
5  *
6  * $Id: dhd_cfg_vendor.c 495605 2014-08-07 18:41:34Z $
7  */
8
9 #include <linux/vmalloc.h>
10 #include <linuxver.h>
11 #include <net/cfg80211.h>
12 #include <net/netlink.h>
13
14 #include <bcmutils.h>
15 #include <wl_cfg80211.h>
16 #include <wl_cfgvendor.h>
17 #include <dngl_stats.h>
18 #include <dhd.h>
19 #include <dhd_dbg.h>
20 #include <dhdioctl.h>
21 #include <brcm_nl80211.h>
22
23 #ifdef VENDOR_EXT_SUPPORT
24 static int dhd_cfgvendor_priv_string_handler(struct wiphy *wiphy,
25         struct wireless_dev *wdev, const void  *data, int len)
26 {
27         const struct bcm_nlmsg_hdr *nlioc = data;
28         struct net_device *ndev = NULL;
29         struct bcm_cfg80211 *cfg;
30         struct sk_buff *reply;
31         void *buf = NULL, *cur;
32         dhd_pub_t *dhd;
33         dhd_ioctl_t ioc = { 0 };
34         int ret = 0, ret_len, payload, msglen;
35         int maxmsglen = PAGE_SIZE - 0x100;
36         int8 index;
37
38         WL_TRACE(("entry: cmd = %d\n", nlioc->cmd));
39         DHD_ERROR(("entry: cmd = %d\n", nlioc->cmd));
40
41         cfg = wiphy_priv(wiphy);
42         dhd = cfg->pub;
43
44         DHD_OS_WAKE_LOCK(dhd);
45
46         /* send to dongle only if we are not waiting for reload already */
47         if (dhd->hang_was_sent) {
48                 WL_ERR(("HANG was sent up earlier\n"));
49                 DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhd, DHD_EVENT_TIMEOUT_MS);
50                 DHD_OS_WAKE_UNLOCK(dhd);
51                 return OSL_ERROR(BCME_DONGLE_DOWN);
52         }
53
54         len -= sizeof(struct bcm_nlmsg_hdr);
55         ret_len = nlioc->len;
56         if (ret_len > 0 || len > 0) {
57                 if (len > DHD_IOCTL_MAXLEN) {
58                         WL_ERR(("oversize input buffer %d\n", len));
59                         len = DHD_IOCTL_MAXLEN;
60                 }
61                 if (ret_len > DHD_IOCTL_MAXLEN) {
62                         WL_ERR(("oversize return buffer %d\n", ret_len));
63                         ret_len = DHD_IOCTL_MAXLEN;
64                 }
65                 payload = max(ret_len, len) + 1;
66                 buf = vzalloc(payload);
67                 if (!buf) {
68                         DHD_OS_WAKE_UNLOCK(dhd);
69                         return -ENOMEM;
70                 }
71                 memcpy(buf, (void *)nlioc + nlioc->offset, len);
72                 *(char *)(buf + len) = '\0';
73         }
74
75         ndev = wdev_to_wlc_ndev(wdev, cfg);
76         index = dhd_net2idx(dhd->info, ndev);
77         if (index == DHD_BAD_IF) {
78                 WL_ERR(("Bad ifidx from wdev:%p\n", wdev));
79                 ret = BCME_ERROR;
80                 goto done;
81         }
82
83         ioc.cmd = nlioc->cmd;
84         ioc.len = nlioc->len;
85         ioc.set = nlioc->set;
86         ioc.driver = nlioc->magic;
87         ret = dhd_ioctl_process(dhd, index, &ioc, buf);
88         if (ret) {
89                 WL_TRACE(("dhd_ioctl_process return err %d\n", ret));
90                 ret = OSL_ERROR(ret);
91                 goto done;
92         }
93
94         cur = buf;
95         while (ret_len > 0) {
96                 msglen = nlioc->len > maxmsglen ? maxmsglen : ret_len;
97                 ret_len -= msglen;
98                 payload = msglen + sizeof(msglen);
99                 reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
100                 if (!reply) {
101                         WL_ERR(("Failed to allocate reply msg\n"));
102                         ret = -ENOMEM;
103                         break;
104                 }
105
106                 if (nla_put(reply, BCM_NLATTR_DATA, msglen, cur) ||
107                         nla_put_u16(reply, BCM_NLATTR_LEN, msglen)) {
108                         kfree_skb(reply);
109                         ret = -ENOBUFS;
110                         break;
111                 }
112
113                 ret = cfg80211_vendor_cmd_reply(reply);
114                 if (ret) {
115                         WL_ERR(("testmode reply failed:%d\n", ret));
116                         break;
117                 }
118                 cur += msglen;
119         }
120
121 done:
122         vfree(buf);
123         DHD_OS_WAKE_UNLOCK(dhd);
124         return ret;
125 }
126
127 const struct wiphy_vendor_command dhd_cfgvendor_cmds [] = {
128         {
129                 {
130                         .vendor_id = OUI_BRCM,
131                         .subcmd = BRCM_VENDOR_SCMD_PRIV_STR
132                 },
133                 .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
134                 .doit = dhd_cfgvendor_priv_string_handler
135         },
136 };
137
138 int cfgvendor_attach(struct wiphy *wiphy)
139 {
140         wiphy->vendor_commands  = dhd_cfgvendor_cmds;
141         wiphy->n_vendor_commands = ARRAY_SIZE(dhd_cfgvendor_cmds);
142
143         return 0;
144 }
145
146 int cfgvendor_detach(struct wiphy *wiphy)
147 {
148         wiphy->vendor_commands  = NULL;
149         wiphy->n_vendor_commands = 0;
150
151         return 0;
152 }
153 #endif /* VENDOR_EXT_SUPPORT */