1 /*
2 * Linux cfg80211 driver - Dongle Host Driver (DHD) related
3 *
4 * Copyright (C) 1999-2017, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 *
25 * <<Broadcom-WL-IPTag/Open:>>
26 *
27 * $Id: dhd_cfg80211.c 699163 2017-05-12 05:18:23Z $
28 */
29
30 #include <linux/vmalloc.h>
31 #include <net/rtnetlink.h>
32
33 #include <bcmutils.h>
34 #include <wldev_common.h>
35 #include <wl_cfg80211.h>
36 #include <dhd_cfg80211.h>
37
38 #ifdef PKT_FILTER_SUPPORT
39 #include <dngl_stats.h>
40 #include <dhd.h>
41 #endif
42
43 #ifdef PKT_FILTER_SUPPORT
44 extern uint dhd_pkt_filter_enable;
45 extern uint dhd_master_mode;
46 extern void dhd_pktfilter_offload_enable(dhd_pub_t *dhd, char *arg, int enable, int master_mode);
47 #endif
48
49 static int dhd_dongle_up = FALSE;
50
51 #include <dngl_stats.h>
52 #include <dhd.h>
53 #include <dhdioctl.h>
54 #include <wlioctl.h>
55 #include <brcm_nl80211.h>
56 #include <dhd_cfg80211.h>
57
58 static s32 wl_dongle_up(struct net_device *ndev);
59 static s32 wl_dongle_down(struct net_device *ndev);
60
61 /**
62 * Function implementations
63 */
64
dhd_cfg80211_init(struct bcm_cfg80211 * cfg)65 s32 dhd_cfg80211_init(struct bcm_cfg80211 *cfg)
66 {
67 dhd_dongle_up = FALSE;
68 return 0;
69 }
70
dhd_cfg80211_deinit(struct bcm_cfg80211 * cfg)71 s32 dhd_cfg80211_deinit(struct bcm_cfg80211 *cfg)
72 {
73 dhd_dongle_up = FALSE;
74 return 0;
75 }
76
dhd_cfg80211_down(struct bcm_cfg80211 * cfg)77 s32 dhd_cfg80211_down(struct bcm_cfg80211 *cfg)
78 {
79 struct net_device *ndev;
80 s32 err = 0;
81
82 WL_TRACE(("In\n"));
83 if (!dhd_dongle_up) {
84 WL_ERR(("Dongle is already down\n"));
85 return err;
86 }
87
88 ndev = bcmcfg_to_prmry_ndev(cfg);
89 wl_dongle_down(ndev);
90 dhd_dongle_up = FALSE;
91 return 0;
92 }
93
dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 * cfg,int val)94 s32 dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 *cfg, int val)
95 {
96 dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
97 dhd->op_mode |= val;
98 WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode));
99 #ifdef ARP_OFFLOAD_SUPPORT
100 if (dhd->arp_version == 1) {
101 /* IF P2P is enabled, disable arpoe */
102 dhd_arp_offload_set(dhd, 0);
103 dhd_arp_offload_enable(dhd, false);
104 }
105 #endif /* ARP_OFFLOAD_SUPPORT */
106
107 return 0;
108 }
109
dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 * cfg)110 s32 dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 *cfg)
111 {
112 dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
113 dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE);
114 WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode));
115
116 #ifdef ARP_OFFLOAD_SUPPORT
117 if (dhd->arp_version == 1) {
118 /* IF P2P is disabled, enable arpoe back for STA mode. */
119 dhd_arp_offload_set(dhd, dhd_arp_mode);
120 dhd_arp_offload_enable(dhd, true);
121 }
122 #endif /* ARP_OFFLOAD_SUPPORT */
123
124 return 0;
125 }
126
wl_cfg80211_allocate_if(struct bcm_cfg80211 * cfg,int ifidx,const char * name,uint8 * mac,uint8 bssidx,const char * dngl_name)127 struct net_device* wl_cfg80211_allocate_if(struct bcm_cfg80211 *cfg, int ifidx, const char *name,
128 uint8 *mac, uint8 bssidx, const char *dngl_name)
129 {
130 return dhd_allocate_if(cfg->pub, ifidx, name, mac, bssidx, FALSE, dngl_name);
131 }
132
wl_cfg80211_register_if(struct bcm_cfg80211 * cfg,int ifidx,struct net_device * ndev,bool rtnl_lock_reqd)133 int wl_cfg80211_register_if(struct bcm_cfg80211 *cfg,
134 int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
135 {
136 return dhd_register_if(cfg->pub, ifidx, rtnl_lock_reqd);
137 }
138
wl_cfg80211_remove_if(struct bcm_cfg80211 * cfg,int ifidx,struct net_device * ndev,bool rtnl_lock_reqd)139 int wl_cfg80211_remove_if(struct bcm_cfg80211 *cfg,
140 int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
141 {
142 return dhd_remove_if(cfg->pub, ifidx, rtnl_lock_reqd);
143 }
144
dhd_cfg80211_netdev_free(struct net_device * ndev)145 struct net_device *dhd_cfg80211_netdev_free(struct net_device *ndev)
146 {
147 if (ndev) {
148 if (ndev->ieee80211_ptr) {
149 kfree(ndev->ieee80211_ptr);
150 ndev->ieee80211_ptr = NULL;
151 }
152 free_netdev(ndev);
153 return NULL;
154 }
155
156 return ndev;
157 }
158
dhd_netdev_free(struct net_device * ndev)159 void dhd_netdev_free(struct net_device *ndev)
160 {
161 #ifdef WL_CFG80211
162 ndev = dhd_cfg80211_netdev_free(ndev);
163 #endif
164 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
165 if (ndev)
166 free_netdev(ndev);
167 #endif
168 }
169
170 static s32
wl_dongle_up(struct net_device * ndev)171 wl_dongle_up(struct net_device *ndev)
172 {
173 s32 err = 0;
174 u32 local_up = 0;
175
176 err = wldev_ioctl_set(ndev, WLC_UP, &local_up, sizeof(local_up));
177 if (unlikely(err)) {
178 WL_ERR(("WLC_UP error (%d)\n", err));
179 }
180 return err;
181 }
182
183 static s32
wl_dongle_down(struct net_device * ndev)184 wl_dongle_down(struct net_device *ndev)
185 {
186 s32 err = 0;
187 u32 local_down = 0;
188
189 err = wldev_ioctl_set(ndev, WLC_DOWN, &local_down, sizeof(local_down));
190 if (unlikely(err)) {
191 WL_ERR(("WLC_DOWN error (%d)\n", err));
192 }
193 return err;
194 }
195
196
dhd_config_dongle(struct bcm_cfg80211 * cfg)197 s32 dhd_config_dongle(struct bcm_cfg80211 *cfg)
198 {
199 #ifndef DHD_SDALIGN
200 #define DHD_SDALIGN 32
201 #endif
202 struct net_device *ndev;
203 s32 err = 0;
204
205 WL_TRACE(("In\n"));
206 if (dhd_dongle_up) {
207 WL_ERR(("Dongle is already up\n"));
208 return err;
209 }
210
211 ndev = bcmcfg_to_prmry_ndev(cfg);
212
213 err = wl_dongle_up(ndev);
214 if (unlikely(err)) {
215 WL_ERR(("wl_dongle_up failed\n"));
216 goto default_conf_out;
217 }
218 dhd_dongle_up = true;
219
220 default_conf_out:
221
222 return err;
223 }
224
dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 * cfg,struct wireless_dev * wdev,const struct bcm_nlmsg_hdr * nlioc,void * buf)225 int dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 *cfg, struct wireless_dev *wdev,
226 const struct bcm_nlmsg_hdr *nlioc, void *buf)
227 {
228 struct net_device *ndev = NULL;
229 dhd_pub_t *dhd;
230 dhd_ioctl_t ioc = { 0, NULL, 0, 0, 0, 0, 0};
231 int ret = 0;
232 int8 index;
233
234 WL_TRACE(("entry: cmd = %d\n", nlioc->cmd));
235
236 dhd = cfg->pub;
237 DHD_OS_WAKE_LOCK(dhd);
238
239 /* send to dongle only if we are not waiting for reload already */
240 if (dhd->hang_was_sent) {
241 WL_ERR(("HANG was sent up earlier\n"));
242 DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhd, DHD_EVENT_TIMEOUT_MS);
243 DHD_OS_WAKE_UNLOCK(dhd);
244 return OSL_ERROR(BCME_DONGLE_DOWN);
245 }
246
247 ndev = wdev_to_wlc_ndev(wdev, cfg);
248 index = dhd_net2idx(dhd->info, ndev);
249 if (index == DHD_BAD_IF) {
250 WL_ERR(("Bad ifidx from wdev:%p\n", wdev));
251 ret = BCME_ERROR;
252 goto done;
253 }
254
255 ioc.cmd = nlioc->cmd;
256 ioc.len = nlioc->len;
257 ioc.set = nlioc->set;
258 ioc.driver = nlioc->magic;
259 ioc.buf = buf;
260 ret = dhd_ioctl_process(dhd, index, &ioc, buf);
261 if (ret) {
262 WL_TRACE(("dhd_ioctl_process return err %d\n", ret));
263 ret = OSL_ERROR(ret);
264 goto done;
265 }
266
267 done:
268 DHD_OS_WAKE_UNLOCK(dhd);
269 return ret;
270 }
271