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