xref: /OK3568_Linux_fs/external/rkwifibt/drivers/infineon/dhd_cfg80211.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Linux cfg80211 driver - Dongle Host Driver (DHD) related
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Portions of this code are copyright (c) 2021 Cypress Semiconductor Corporation
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 1999-2017, Broadcom Corporation
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *      Unless you and Broadcom execute a separate written software license
9*4882a593Smuzhiyun  * agreement governing use of this software, this software is licensed to you
10*4882a593Smuzhiyun  * under the terms of the GNU General Public License version 2 (the "GPL"),
11*4882a593Smuzhiyun  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
12*4882a593Smuzhiyun  * following added to such license:
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  *      As a special exception, the copyright holders of this software give you
15*4882a593Smuzhiyun  * permission to link this software with independent modules, and to copy and
16*4882a593Smuzhiyun  * distribute the resulting executable under terms of your choice, provided that
17*4882a593Smuzhiyun  * you also meet, for each linked independent module, the terms and conditions of
18*4882a593Smuzhiyun  * the license of that module.  An independent module is a module which is not
19*4882a593Smuzhiyun  * derived from this software.  The special exception does not apply to any
20*4882a593Smuzhiyun  * modifications of the software.
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  *      Notwithstanding the above, under no circumstances may you combine this
23*4882a593Smuzhiyun  * software in any way with any other Broadcom software provided under a license
24*4882a593Smuzhiyun  * other than the GPL, without Broadcom's express prior written consent.
25*4882a593Smuzhiyun  *
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  * <<Broadcom-WL-IPTag/Open:>>
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  * $Id: dhd_cfg80211.c 696903 2017-04-28 19:48:01Z $
30*4882a593Smuzhiyun  */
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #include <linux/vmalloc.h>
33*4882a593Smuzhiyun #include <net/rtnetlink.h>
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #include <bcmutils.h>
36*4882a593Smuzhiyun #include <wldev_common.h>
37*4882a593Smuzhiyun #include <wl_cfg80211.h>
38*4882a593Smuzhiyun #include <dhd_cfg80211.h>
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #ifdef PKT_FILTER_SUPPORT
41*4882a593Smuzhiyun #include <dngl_stats.h>
42*4882a593Smuzhiyun #include <dhd.h>
43*4882a593Smuzhiyun #endif // endif
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #ifdef PKT_FILTER_SUPPORT
46*4882a593Smuzhiyun extern uint dhd_pkt_filter_enable;
47*4882a593Smuzhiyun extern uint dhd_master_mode;
48*4882a593Smuzhiyun extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
49*4882a593Smuzhiyun #endif // endif
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun static int dhd_dongle_up = FALSE;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun #include <dngl_stats.h>
54*4882a593Smuzhiyun #include <dhd.h>
55*4882a593Smuzhiyun #include <dhdioctl.h>
56*4882a593Smuzhiyun #include <wlioctl.h>
57*4882a593Smuzhiyun #include <brcm_nl80211.h>
58*4882a593Smuzhiyun #include <dhd_cfg80211.h>
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun static s32 wl_dongle_up(struct net_device *ndev);
61*4882a593Smuzhiyun static s32 wl_dongle_down(struct net_device *ndev);
62*4882a593Smuzhiyun #ifndef OEM_ANDROID
63*4882a593Smuzhiyun static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode);
64*4882a593Smuzhiyun #ifdef BCMSDIO /* glomming is a sdio specific feature */
65*4882a593Smuzhiyun static s32 wl_dongle_glom(struct net_device *ndev, s32 glom, u32 dongle_align);
66*4882a593Smuzhiyun #endif // endif
67*4882a593Smuzhiyun static s32 wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, s32 scan_unassoc_time);
68*4882a593Smuzhiyun static s32 wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol);
69*4882a593Smuzhiyun static s32 wl_pattern_atoh(s8 *src, s8 *dst);
70*4882a593Smuzhiyun static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode);
71*4882a593Smuzhiyun #endif /* OEM_ANDROID */
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun /**
74*4882a593Smuzhiyun  * Function implementations
75*4882a593Smuzhiyun  */
76*4882a593Smuzhiyun 
dhd_cfg80211_init(struct bcm_cfg80211 * cfg)77*4882a593Smuzhiyun s32 dhd_cfg80211_init(struct bcm_cfg80211 *cfg)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	dhd_dongle_up = FALSE;
80*4882a593Smuzhiyun 	return 0;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
dhd_cfg80211_deinit(struct bcm_cfg80211 * cfg)83*4882a593Smuzhiyun s32 dhd_cfg80211_deinit(struct bcm_cfg80211 *cfg)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	dhd_dongle_up = FALSE;
86*4882a593Smuzhiyun 	return 0;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun 
dhd_cfg80211_down(struct bcm_cfg80211 * cfg)89*4882a593Smuzhiyun s32 dhd_cfg80211_down(struct bcm_cfg80211 *cfg)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun 	struct net_device *ndev;
92*4882a593Smuzhiyun 	s32 err = 0;
93*4882a593Smuzhiyun 	dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	WL_TRACE(("In\n"));
96*4882a593Smuzhiyun 	if ((!dhd_dongle_up) || (!dhd->up)) {
97*4882a593Smuzhiyun 		WL_INFORM_MEM(("Dongle is already down\n"));
98*4882a593Smuzhiyun 		err = 0;
99*4882a593Smuzhiyun 		goto done;
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun 	ndev = bcmcfg_to_prmry_ndev(cfg);
102*4882a593Smuzhiyun 	wl_dongle_down(ndev);
103*4882a593Smuzhiyun done:
104*4882a593Smuzhiyun 	dhd_dongle_up = FALSE;
105*4882a593Smuzhiyun 	return err;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 * cfg,int val)108*4882a593Smuzhiyun s32 dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 *cfg, int val)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun 	dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
111*4882a593Smuzhiyun 	dhd->op_mode |= val;
112*4882a593Smuzhiyun 	WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode));
113*4882a593Smuzhiyun #ifdef ARP_OFFLOAD_SUPPORT
114*4882a593Smuzhiyun 	if (dhd->arp_version == 1) {
115*4882a593Smuzhiyun 		/* IF P2P is enabled, disable arpoe */
116*4882a593Smuzhiyun 		dhd_arp_offload_set(dhd, 0);
117*4882a593Smuzhiyun 		dhd_arp_offload_enable(dhd, false);
118*4882a593Smuzhiyun 	}
119*4882a593Smuzhiyun #endif /* ARP_OFFLOAD_SUPPORT */
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	return 0;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 * cfg)124*4882a593Smuzhiyun s32 dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 *cfg)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
127*4882a593Smuzhiyun 	dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE);
128*4882a593Smuzhiyun 	WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode));
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun #ifdef ARP_OFFLOAD_SUPPORT
131*4882a593Smuzhiyun 	if (dhd->arp_version == 1) {
132*4882a593Smuzhiyun 		/* IF P2P is disabled, enable arpoe back for STA mode. */
133*4882a593Smuzhiyun 		dhd_arp_offload_set(dhd, dhd_arp_mode);
134*4882a593Smuzhiyun 		dhd_arp_offload_enable(dhd, true);
135*4882a593Smuzhiyun 	}
136*4882a593Smuzhiyun #endif /* ARP_OFFLOAD_SUPPORT */
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun #ifdef WL_STATIC_IF
142*4882a593Smuzhiyun int32
wl_cfg80211_update_iflist_info(struct bcm_cfg80211 * cfg,struct net_device * ndev,int ifidx,uint8 * addr,int bssidx,char * name,int if_state)143*4882a593Smuzhiyun wl_cfg80211_update_iflist_info(struct bcm_cfg80211 *cfg, struct net_device *ndev,
144*4882a593Smuzhiyun 	int ifidx, uint8 *addr, int bssidx, char *name, int if_state)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	return dhd_update_iflist_info(cfg->pub, ndev, ifidx, addr, bssidx, name, if_state);
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun #endif /* WL_STATIC_IF */
149*4882a593Smuzhiyun 
wl_cfg80211_allocate_if(struct bcm_cfg80211 * cfg,int ifidx,const char * name,uint8 * mac,uint8 bssidx,const char * dngl_name)150*4882a593Smuzhiyun struct net_device* wl_cfg80211_allocate_if(struct bcm_cfg80211 *cfg, int ifidx, const char *name,
151*4882a593Smuzhiyun 	uint8 *mac, uint8 bssidx, const char *dngl_name)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	return dhd_allocate_if(cfg->pub, ifidx, name, mac, bssidx, FALSE, dngl_name);
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun 
wl_cfg80211_register_if(struct bcm_cfg80211 * cfg,int ifidx,struct net_device * ndev,bool rtnl_lock_reqd)156*4882a593Smuzhiyun int wl_cfg80211_register_if(struct bcm_cfg80211 *cfg,
157*4882a593Smuzhiyun 	int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun 	return dhd_register_if(cfg->pub, ifidx, rtnl_lock_reqd);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
wl_cfg80211_remove_if(struct bcm_cfg80211 * cfg,int ifidx,struct net_device * ndev,bool rtnl_lock_reqd)162*4882a593Smuzhiyun int wl_cfg80211_remove_if(struct bcm_cfg80211 *cfg,
163*4882a593Smuzhiyun 	int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun #ifdef DHD_PCIE_RUNTIMEPM
166*4882a593Smuzhiyun 	dhdpcie_runtime_bus_wake(cfg->pub, CAN_SLEEP(), __builtin_return_address(0));
167*4882a593Smuzhiyun #endif /* DHD_PCIE_RUNTIMEPM */
168*4882a593Smuzhiyun 	return dhd_remove_if(cfg->pub, ifidx, rtnl_lock_reqd);
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun 
wl_cfg80211_cleanup_if(struct net_device * net)171*4882a593Smuzhiyun void wl_cfg80211_cleanup_if(struct net_device *net)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(net);
174*4882a593Smuzhiyun #ifdef DHD_PCIE_RUNTIMEPM
175*4882a593Smuzhiyun 	dhdpcie_runtime_bus_wake(cfg->pub, CAN_SLEEP(), __builtin_return_address(0));
176*4882a593Smuzhiyun #else
177*4882a593Smuzhiyun 	BCM_REFERENCE(cfg);
178*4882a593Smuzhiyun #endif /* DHD_PCIE_RUNTIMEPM */
179*4882a593Smuzhiyun 	dhd_cleanup_if(net);
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
dhd_cfg80211_netdev_free(struct net_device * ndev)182*4882a593Smuzhiyun struct net_device * dhd_cfg80211_netdev_free(struct net_device *ndev)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	if (ndev) {
187*4882a593Smuzhiyun 		cfg = wl_get_cfg(ndev);
188*4882a593Smuzhiyun 		if (ndev->ieee80211_ptr) {
189*4882a593Smuzhiyun 			MFREE(cfg->osh, ndev->ieee80211_ptr, sizeof(struct wireless_dev));
190*4882a593Smuzhiyun 			ndev->ieee80211_ptr = NULL;
191*4882a593Smuzhiyun 		}
192*4882a593Smuzhiyun 		free_netdev(ndev);
193*4882a593Smuzhiyun 		return NULL;
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	return ndev;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun 
dhd_netdev_free(struct net_device * ndev)199*4882a593Smuzhiyun void dhd_netdev_free(struct net_device *ndev)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun #ifdef WL_CFG80211
202*4882a593Smuzhiyun 	ndev = dhd_cfg80211_netdev_free(ndev);
203*4882a593Smuzhiyun #endif // endif
204*4882a593Smuzhiyun 	if (ndev)
205*4882a593Smuzhiyun 		free_netdev(ndev);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun static s32
wl_dongle_up(struct net_device * ndev)209*4882a593Smuzhiyun wl_dongle_up(struct net_device *ndev)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	s32 err = 0;
212*4882a593Smuzhiyun 	u32 local_up = 0;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	err = wldev_ioctl_set(ndev, WLC_UP, &local_up, sizeof(local_up));
215*4882a593Smuzhiyun 	if (unlikely(err)) {
216*4882a593Smuzhiyun 		WL_ERR(("WLC_UP error (%d)\n", err));
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 	return err;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun static s32
wl_dongle_down(struct net_device * ndev)222*4882a593Smuzhiyun wl_dongle_down(struct net_device *ndev)
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun 	s32 err = 0;
225*4882a593Smuzhiyun 	u32 local_down = 0;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	err = wldev_ioctl_set(ndev, WLC_DOWN, &local_down, sizeof(local_down));
228*4882a593Smuzhiyun 	if (unlikely(err)) {
229*4882a593Smuzhiyun 		WL_ERR(("WLC_DOWN error (%d)\n", err));
230*4882a593Smuzhiyun 	}
231*4882a593Smuzhiyun 	return err;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun #ifndef OEM_ANDROID
wl_dongle_power(struct net_device * ndev,u32 power_mode)235*4882a593Smuzhiyun static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun 	s32 err = 0;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	WL_TRACE(("In\n"));
240*4882a593Smuzhiyun 	err = wldev_ioctl_set(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode));
241*4882a593Smuzhiyun 	if (unlikely(err)) {
242*4882a593Smuzhiyun 		WL_ERR(("WLC_SET_PM error (%d)\n", err));
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 	return err;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun #ifdef BCMSDIO
248*4882a593Smuzhiyun static s32
wl_dongle_glom(struct net_device * ndev,s32 glom,u32 dongle_align)249*4882a593Smuzhiyun wl_dongle_glom(struct net_device *ndev, s32 glom, u32 dongle_align)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun 	s32 err = 0;
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	/* Match Host and Dongle rx alignment */
254*4882a593Smuzhiyun 	err = wldev_iovar_setint(ndev, "bus:txglomalign", dongle_align);
255*4882a593Smuzhiyun 	if (unlikely(err)) {
256*4882a593Smuzhiyun 		WL_ERR(("txglomalign error (%d)\n", err));
257*4882a593Smuzhiyun 		goto dongle_glom_out;
258*4882a593Smuzhiyun 	}
259*4882a593Smuzhiyun 	/* disable glom option per default */
260*4882a593Smuzhiyun 	if (glom != DEFAULT_GLOM_VALUE) {
261*4882a593Smuzhiyun 		err = wldev_iovar_setint(ndev, "bus:txglom", glom);
262*4882a593Smuzhiyun 		if (unlikely(err)) {
263*4882a593Smuzhiyun 			WL_ERR(("txglom error (%d)\n", err));
264*4882a593Smuzhiyun 			goto dongle_glom_out;
265*4882a593Smuzhiyun 		}
266*4882a593Smuzhiyun 	}
267*4882a593Smuzhiyun dongle_glom_out:
268*4882a593Smuzhiyun 	return err;
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun #endif /* BCMSDIO */
272*4882a593Smuzhiyun #endif /* OEM_ANDROID */
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun s32
wl_dongle_roam(struct net_device * ndev,u32 roamvar,u32 bcn_timeout)275*4882a593Smuzhiyun wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	s32 err = 0;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	/* Setup timeout if Beacons are lost and roam is off to report link down */
280*4882a593Smuzhiyun 	if (roamvar) {
281*4882a593Smuzhiyun 		err = wldev_iovar_setint(ndev, "bcn_timeout", bcn_timeout);
282*4882a593Smuzhiyun 		if (unlikely(err)) {
283*4882a593Smuzhiyun 			WL_ERR(("bcn_timeout error (%d)\n", err));
284*4882a593Smuzhiyun 			goto dongle_rom_out;
285*4882a593Smuzhiyun 		}
286*4882a593Smuzhiyun 	}
287*4882a593Smuzhiyun 	/* Enable/Disable built-in roaming to allow supplicant to take care of roaming */
288*4882a593Smuzhiyun 	err = wldev_iovar_setint(ndev, "roam_off", roamvar);
289*4882a593Smuzhiyun 	if (unlikely(err)) {
290*4882a593Smuzhiyun 		WL_ERR(("roam_off error (%d)\n", err));
291*4882a593Smuzhiyun 		goto dongle_rom_out;
292*4882a593Smuzhiyun 	}
293*4882a593Smuzhiyun dongle_rom_out:
294*4882a593Smuzhiyun 	return err;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun #ifndef OEM_ANDROID
298*4882a593Smuzhiyun static s32
wl_dongle_scantime(struct net_device * ndev,s32 scan_assoc_time,s32 scan_unassoc_time)299*4882a593Smuzhiyun wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
300*4882a593Smuzhiyun 	s32 scan_unassoc_time)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	s32 err = 0;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	err = wldev_ioctl_set(ndev, WLC_SET_SCAN_CHANNEL_TIME, &scan_assoc_time,
305*4882a593Smuzhiyun 		sizeof(scan_assoc_time));
306*4882a593Smuzhiyun 	if (err) {
307*4882a593Smuzhiyun 		if (err == -EOPNOTSUPP) {
308*4882a593Smuzhiyun 			WL_INFORM(("Scan assoc time is not supported\n"));
309*4882a593Smuzhiyun 		} else {
310*4882a593Smuzhiyun 			WL_ERR(("Scan assoc time error (%d)\n", err));
311*4882a593Smuzhiyun 		}
312*4882a593Smuzhiyun 		goto dongle_scantime_out;
313*4882a593Smuzhiyun 	}
314*4882a593Smuzhiyun 	err = wldev_ioctl_set(ndev, WLC_SET_SCAN_UNASSOC_TIME, &scan_unassoc_time,
315*4882a593Smuzhiyun 		sizeof(scan_unassoc_time));
316*4882a593Smuzhiyun 	if (err) {
317*4882a593Smuzhiyun 		if (err == -EOPNOTSUPP) {
318*4882a593Smuzhiyun 			WL_INFORM(("Scan unassoc time is not supported\n"));
319*4882a593Smuzhiyun 		} else {
320*4882a593Smuzhiyun 			WL_ERR(("Scan unassoc time error (%d)\n", err));
321*4882a593Smuzhiyun 		}
322*4882a593Smuzhiyun 		goto dongle_scantime_out;
323*4882a593Smuzhiyun 	}
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun dongle_scantime_out:
326*4882a593Smuzhiyun 	return err;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun static s32
wl_dongle_offload(struct net_device * ndev,s32 arpoe,s32 arp_ol)330*4882a593Smuzhiyun wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun 	s8 iovbuf[WLC_IOCTL_SMLEN];
333*4882a593Smuzhiyun 	s32 err = 0;
334*4882a593Smuzhiyun 	s32 len;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	/* Set ARP offload */
337*4882a593Smuzhiyun 	len = bcm_mkiovar("arpoe", (char *)&arpoe, sizeof(arpoe), iovbuf, sizeof(iovbuf));
338*4882a593Smuzhiyun 	if (!len) {
339*4882a593Smuzhiyun 		WL_ERR(("%s: bcm_mkiovar failed:%d\n", __FUNCTION__, len));
340*4882a593Smuzhiyun 		return BCME_BADARG;
341*4882a593Smuzhiyun 	}
342*4882a593Smuzhiyun 	err = wldev_ioctl_set(ndev, WLC_SET_VAR, iovbuf, len);
343*4882a593Smuzhiyun 	if (err) {
344*4882a593Smuzhiyun 		if (err == -EOPNOTSUPP)
345*4882a593Smuzhiyun 			WL_INFORM(("arpoe is not supported\n"));
346*4882a593Smuzhiyun 		else
347*4882a593Smuzhiyun 			WL_ERR(("arpoe error (%d)\n", err));
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 		goto dongle_offload_out;
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 	len = bcm_mkiovar("arp_ol", (char *)&arp_ol, sizeof(arp_ol), iovbuf, sizeof(iovbuf));
352*4882a593Smuzhiyun 	if (!len) {
353*4882a593Smuzhiyun 		WL_ERR(("%s: bcm_mkiovar failed:%d\n", __FUNCTION__, len));
354*4882a593Smuzhiyun 		return BCME_BADARG;
355*4882a593Smuzhiyun 	}
356*4882a593Smuzhiyun 	err = wldev_ioctl_set(ndev, WLC_SET_VAR, iovbuf, len);
357*4882a593Smuzhiyun 	if (err) {
358*4882a593Smuzhiyun 		if (err == -EOPNOTSUPP)
359*4882a593Smuzhiyun 			WL_INFORM(("arp_ol is not supported\n"));
360*4882a593Smuzhiyun 		else
361*4882a593Smuzhiyun 			WL_ERR(("arp_ol error (%d)\n", err));
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 		goto dongle_offload_out;
364*4882a593Smuzhiyun 	}
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun dongle_offload_out:
367*4882a593Smuzhiyun 	return err;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun 
wl_pattern_atoh(s8 * src,s8 * dst)370*4882a593Smuzhiyun static s32 wl_pattern_atoh(s8 *src, s8 *dst)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun 	int i;
373*4882a593Smuzhiyun 	if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) {
374*4882a593Smuzhiyun 		WL_ERR(("Mask invalid format. Needs to start with 0x\n"));
375*4882a593Smuzhiyun 		return -1;
376*4882a593Smuzhiyun 	}
377*4882a593Smuzhiyun 	src = src + 2;		/* Skip past 0x */
378*4882a593Smuzhiyun 	if (strlen(src) % 2 != 0) {
379*4882a593Smuzhiyun 		WL_ERR(("Mask invalid format. Needs to be of even length\n"));
380*4882a593Smuzhiyun 		return -1;
381*4882a593Smuzhiyun 	}
382*4882a593Smuzhiyun 	for (i = 0; *src != '\0'; i++) {
383*4882a593Smuzhiyun 		char num[3];
384*4882a593Smuzhiyun 		strncpy(num, src, 2);
385*4882a593Smuzhiyun 		num[2] = '\0';
386*4882a593Smuzhiyun 		dst[i] = (u8) simple_strtoul(num, NULL, 16);
387*4882a593Smuzhiyun 		src += 2;
388*4882a593Smuzhiyun 	}
389*4882a593Smuzhiyun 	return i;
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun 
wl_dongle_filter(struct net_device * ndev,u32 filter_mode)392*4882a593Smuzhiyun static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun 	const s8 *str;
395*4882a593Smuzhiyun 	struct wl_pkt_filter pkt_filter;
396*4882a593Smuzhiyun 	struct wl_pkt_filter *pkt_filterp;
397*4882a593Smuzhiyun 	s32 buf_len;
398*4882a593Smuzhiyun 	s32 str_len;
399*4882a593Smuzhiyun 	u32 mask_size;
400*4882a593Smuzhiyun 	u32 pattern_size;
401*4882a593Smuzhiyun 	s8 buf[64] = {0};
402*4882a593Smuzhiyun 	s32 err = 0;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	/* add a default packet filter pattern */
405*4882a593Smuzhiyun 	str = "pkt_filter_add";
406*4882a593Smuzhiyun 	str_len = strlen(str);
407*4882a593Smuzhiyun 	strncpy(buf, str, sizeof(buf) - 1);
408*4882a593Smuzhiyun 	buf[ sizeof(buf) - 1 ] = '\0';
409*4882a593Smuzhiyun 	buf_len = str_len + 1;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	pkt_filterp = (struct wl_pkt_filter *)(buf + str_len + 1);
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	/* Parse packet filter id. */
414*4882a593Smuzhiyun 	pkt_filter.id = htod32(100);
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	/* Parse filter polarity. */
417*4882a593Smuzhiyun 	pkt_filter.negate_match = htod32(0);
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	/* Parse filter type. */
420*4882a593Smuzhiyun 	pkt_filter.type = htod32(0);
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	/* Parse pattern filter offset. */
423*4882a593Smuzhiyun 	pkt_filter.u.pattern.offset = htod32(0);
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	/* Parse pattern filter mask. */
426*4882a593Smuzhiyun 	mask_size = htod32(wl_pattern_atoh("0xff",
427*4882a593Smuzhiyun 		(char *)pkt_filterp->u.pattern.
428*4882a593Smuzhiyun 		    mask_and_pattern));
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	/* Parse pattern filter pattern. */
431*4882a593Smuzhiyun 	pattern_size = htod32(wl_pattern_atoh("0x00",
432*4882a593Smuzhiyun 		(char *)&pkt_filterp->u.pattern.mask_and_pattern[mask_size]));
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	if (mask_size != pattern_size) {
435*4882a593Smuzhiyun 		WL_ERR(("Mask and pattern not the same size\n"));
436*4882a593Smuzhiyun 		err = -EINVAL;
437*4882a593Smuzhiyun 		goto dongle_filter_out;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	pkt_filter.u.pattern.size_bytes = mask_size;
441*4882a593Smuzhiyun 	buf_len += WL_PKT_FILTER_FIXED_LEN;
442*4882a593Smuzhiyun 	buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	/* Keep-alive attributes are set in local
445*4882a593Smuzhiyun 	 * variable (keep_alive_pkt), and
446*4882a593Smuzhiyun 	 * then memcpy'ed into buffer (keep_alive_pktp) since there is no
447*4882a593Smuzhiyun 	 * guarantee that the buffer is properly aligned.
448*4882a593Smuzhiyun 	 */
449*4882a593Smuzhiyun 	memcpy((char *)pkt_filterp, &pkt_filter,
450*4882a593Smuzhiyun 		WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	err = wldev_ioctl_set(ndev, WLC_SET_VAR, buf, buf_len);
453*4882a593Smuzhiyun 	if (err) {
454*4882a593Smuzhiyun 		if (err == -EOPNOTSUPP) {
455*4882a593Smuzhiyun 			WL_INFORM(("filter not supported\n"));
456*4882a593Smuzhiyun 		} else {
457*4882a593Smuzhiyun 			WL_ERR(("filter (%d)\n", err));
458*4882a593Smuzhiyun 		}
459*4882a593Smuzhiyun 		goto dongle_filter_out;
460*4882a593Smuzhiyun 	}
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	/* set mode to allow pattern */
463*4882a593Smuzhiyun 	err = wldev_iovar_setint(ndev, "pkt_filter_mode", filter_mode);
464*4882a593Smuzhiyun 	if (err) {
465*4882a593Smuzhiyun 		if (err == -EOPNOTSUPP) {
466*4882a593Smuzhiyun 			WL_INFORM(("filter_mode not supported\n"));
467*4882a593Smuzhiyun 		} else {
468*4882a593Smuzhiyun 			WL_ERR(("filter_mode (%d)\n", err));
469*4882a593Smuzhiyun 		}
470*4882a593Smuzhiyun 		goto dongle_filter_out;
471*4882a593Smuzhiyun 	}
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun dongle_filter_out:
474*4882a593Smuzhiyun 	return err;
475*4882a593Smuzhiyun }
476*4882a593Smuzhiyun #endif /* OEM_ANDROID */
477*4882a593Smuzhiyun 
dhd_config_dongle(struct bcm_cfg80211 * cfg)478*4882a593Smuzhiyun s32 dhd_config_dongle(struct bcm_cfg80211 *cfg)
479*4882a593Smuzhiyun {
480*4882a593Smuzhiyun #ifndef DHD_SDALIGN
481*4882a593Smuzhiyun #define DHD_SDALIGN	32
482*4882a593Smuzhiyun #endif // endif
483*4882a593Smuzhiyun 	struct net_device *ndev;
484*4882a593Smuzhiyun 	s32 err = 0;
485*4882a593Smuzhiyun #if !defined(OEM_ANDROID) && defined(BCMSDIO)
486*4882a593Smuzhiyun 	s32 glom = CUSTOM_GLOM_SETTING;
487*4882a593Smuzhiyun #endif // endif
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	WL_TRACE(("In\n"));
490*4882a593Smuzhiyun 	if (dhd_dongle_up) {
491*4882a593Smuzhiyun 		WL_ERR(("Dongle is already up\n"));
492*4882a593Smuzhiyun 		return err;
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	ndev = bcmcfg_to_prmry_ndev(cfg);
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	err = wl_dongle_up(ndev);
498*4882a593Smuzhiyun 	if (unlikely(err)) {
499*4882a593Smuzhiyun 		WL_ERR(("wl_dongle_up failed\n"));
500*4882a593Smuzhiyun 		goto default_conf_out;
501*4882a593Smuzhiyun 	}
502*4882a593Smuzhiyun #ifndef OEM_ANDROID
503*4882a593Smuzhiyun 	err = wl_dongle_power(ndev, PM_FAST);
504*4882a593Smuzhiyun 	if (unlikely(err)) {
505*4882a593Smuzhiyun 		WL_ERR(("wl_dongle_power failed\n"));
506*4882a593Smuzhiyun 		goto default_conf_out;
507*4882a593Smuzhiyun 	}
508*4882a593Smuzhiyun #ifdef BCMSDIO
509*4882a593Smuzhiyun 	if (glom != DEFAULT_GLOM_VALUE) {
510*4882a593Smuzhiyun 		err = wl_dongle_glom(ndev, glom, DHD_SDALIGN);
511*4882a593Smuzhiyun 	} else {
512*4882a593Smuzhiyun 		err = wl_dongle_glom(ndev, DEFAULT_GLOM_VALUE, DHD_SDALIGN);
513*4882a593Smuzhiyun 	}
514*4882a593Smuzhiyun 	if (unlikely(err)) {
515*4882a593Smuzhiyun 		WL_ERR(("wl_dongle_glom failed\n"));
516*4882a593Smuzhiyun 		goto default_conf_out;
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun #endif /* BCMSDIO */
519*4882a593Smuzhiyun 	err = wl_dongle_roam(ndev, (cfg->roam_on ? 0 : 1), 3);
520*4882a593Smuzhiyun 	if (unlikely(err)) {
521*4882a593Smuzhiyun 		WL_ERR(("wl_dongle_roam failed\n"));
522*4882a593Smuzhiyun 		goto default_conf_out;
523*4882a593Smuzhiyun 	}
524*4882a593Smuzhiyun 	wl_dongle_scantime(ndev, 40, 80);
525*4882a593Smuzhiyun 	wl_dongle_offload(ndev, 1, 0xf);
526*4882a593Smuzhiyun 	wl_dongle_filter(ndev, 1);
527*4882a593Smuzhiyun #endif /* OEM_ANDROID */
528*4882a593Smuzhiyun 	dhd_dongle_up = true;
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun default_conf_out:
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	return err;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun }
535*4882a593Smuzhiyun 
dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 * cfg,struct wireless_dev * wdev,const struct bcm_nlmsg_hdr * nlioc,void * buf)536*4882a593Smuzhiyun int dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 *cfg, struct wireless_dev *wdev,
537*4882a593Smuzhiyun 	const struct bcm_nlmsg_hdr *nlioc, void *buf)
538*4882a593Smuzhiyun {
539*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
540*4882a593Smuzhiyun 	dhd_pub_t *dhd;
541*4882a593Smuzhiyun 	dhd_ioctl_t ioc = { 0, NULL, 0, 0, 0, 0, 0};
542*4882a593Smuzhiyun 	int ret = 0;
543*4882a593Smuzhiyun 	int8 index;
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	WL_TRACE(("entry: cmd = %d\n", nlioc->cmd));
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 	dhd = cfg->pub;
548*4882a593Smuzhiyun 	DHD_OS_WAKE_LOCK(dhd);
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	ndev = wdev_to_wlc_ndev(wdev, cfg);
551*4882a593Smuzhiyun 	index = dhd_net2idx(dhd->info, ndev);
552*4882a593Smuzhiyun 	if (index == DHD_BAD_IF) {
553*4882a593Smuzhiyun 		WL_ERR(("Bad ifidx from wdev:%p\n", wdev));
554*4882a593Smuzhiyun 		ret = BCME_ERROR;
555*4882a593Smuzhiyun 		goto done;
556*4882a593Smuzhiyun 	}
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	ioc.cmd = nlioc->cmd;
559*4882a593Smuzhiyun 	ioc.len = nlioc->len;
560*4882a593Smuzhiyun 	ioc.set = nlioc->set;
561*4882a593Smuzhiyun 	ioc.driver = nlioc->magic;
562*4882a593Smuzhiyun 	ioc.buf = buf;
563*4882a593Smuzhiyun 	ret = dhd_ioctl_process(dhd, index, &ioc, buf);
564*4882a593Smuzhiyun 	if (ret) {
565*4882a593Smuzhiyun 		WL_TRACE(("dhd_ioctl_process return err %d\n", ret));
566*4882a593Smuzhiyun 		ret = OSL_ERROR(ret);
567*4882a593Smuzhiyun 		goto done;
568*4882a593Smuzhiyun 	}
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun done:
571*4882a593Smuzhiyun 	DHD_OS_WAKE_UNLOCK(dhd);
572*4882a593Smuzhiyun 	return ret;
573*4882a593Smuzhiyun }
574