Commit | Line | Data |
---|---|---|
cf2b4488 HP |
1 | /* |
2 | * Copyright (c) 2010 Broadcom Corporation | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
a1c16ed2 GKH |
17 | #include <linux/kernel.h> |
18 | #include <linux/if_arp.h> | |
cf2b4488 HP |
19 | |
20 | #include <bcmutils.h> | |
cf2b4488 | 21 | |
cf2b4488 HP |
22 | #include <asm/uaccess.h> |
23 | ||
24 | #include <dngl_stats.h> | |
25 | #include <dhd.h> | |
cf2b4488 | 26 | |
7716314b | 27 | #include <linux/kthread.h> |
cf2b4488 HP |
28 | #include <linux/netdevice.h> |
29 | #include <linux/sched.h> | |
30 | #include <linux/etherdevice.h> | |
31 | #include <linux/wireless.h> | |
32 | #include <linux/ieee80211.h> | |
33 | #include <net/cfg80211.h> | |
34 | ||
35 | #include <net/rtnetlink.h> | |
36 | #include <linux/mmc/sdio_func.h> | |
37 | #include <linux/firmware.h> | |
38 | #include <wl_cfg80211.h> | |
39 | ||
6b5a5a3e GG |
40 | void sdioh_sdio_set_host_pm_flags(int flag); |
41 | ||
5f782dee JC |
42 | static struct sdio_func *cfg80211_sdio_func; |
43 | static struct wl_dev *wl_cfg80211_dev; | |
a44d4236 | 44 | static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255}; |
cf2b4488 | 45 | |
1ce4784e | 46 | u32 wl_dbg_level = WL_DBG_ERR; |
cf2b4488 | 47 | |
a4ac0d84 HP |
48 | #define WL_4329_FW_FILE "brcm/bcm4329-fullmac-4.bin" |
49 | #define WL_4329_NVRAM_FILE "brcm/bcm4329-fullmac-4.txt" | |
cf2b4488 HP |
50 | |
51 | /* | |
52 | ** cfg80211_ops api/callback list | |
53 | */ | |
3e26416e | 54 | static s32 wl_cfg80211_change_iface(struct wiphy *wiphy, |
cf2b4488 | 55 | struct net_device *ndev, |
66cbd3ab | 56 | enum nl80211_iftype type, u32 *flags, |
cf2b4488 | 57 | struct vif_params *params); |
3e26416e | 58 | static s32 __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, |
cf2b4488 HP |
59 | struct cfg80211_scan_request *request, |
60 | struct cfg80211_ssid *this_ssid); | |
3e26416e | 61 | static s32 wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, |
cf2b4488 | 62 | struct cfg80211_scan_request *request); |
3e26416e GKH |
63 | static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed); |
64 | static s32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, | |
cf2b4488 | 65 | struct cfg80211_ibss_params *params); |
3e26416e | 66 | static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, |
cf2b4488 | 67 | struct net_device *dev); |
3e26416e | 68 | static s32 wl_cfg80211_get_station(struct wiphy *wiphy, |
3fd79f7c | 69 | struct net_device *dev, u8 *mac, |
cf2b4488 | 70 | struct station_info *sinfo); |
3e26416e | 71 | static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, |
cf2b4488 | 72 | struct net_device *dev, bool enabled, |
3e26416e GKH |
73 | s32 timeout); |
74 | static s32 wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, | |
cf2b4488 | 75 | struct net_device *dev, |
3fd79f7c | 76 | const u8 *addr, |
cf2b4488 HP |
77 | const struct cfg80211_bitrate_mask |
78 | *mask); | |
79 | static int wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, | |
80 | struct cfg80211_connect_params *sme); | |
3e26416e | 81 | static s32 wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, |
7d4df48e | 82 | u16 reason_code); |
3e26416e | 83 | static s32 wl_cfg80211_set_tx_power(struct wiphy *wiphy, |
cf2b4488 | 84 | enum nl80211_tx_power_setting type, |
3e26416e GKH |
85 | s32 dbm); |
86 | static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm); | |
87 | static s32 wl_cfg80211_config_default_key(struct wiphy *wiphy, | |
8a6257fb AS |
88 | struct net_device *dev, u8 key_idx, |
89 | bool unicast, bool multicast); | |
3e26416e | 90 | static s32 wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, |
34a488c1 | 91 | u8 key_idx, bool pairwise, const u8 *mac_addr, |
cf2b4488 | 92 | struct key_params *params); |
3e26416e | 93 | static s32 wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, |
34a488c1 | 94 | u8 key_idx, bool pairwise, const u8 *mac_addr); |
3e26416e | 95 | static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, |
34a488c1 | 96 | u8 key_idx, bool pairwise, const u8 *mac_addr, |
cf2b4488 HP |
97 | void *cookie, void (*callback) (void *cookie, |
98 | struct | |
99 | key_params * | |
100 | params)); | |
3e26416e | 101 | static s32 wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, |
cf2b4488 | 102 | struct net_device *dev, |
3fd79f7c | 103 | u8 key_idx); |
3e26416e GKH |
104 | static s32 wl_cfg80211_resume(struct wiphy *wiphy); |
105 | static s32 wl_cfg80211_suspend(struct wiphy *wiphy); | |
106 | static s32 wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, | |
cf2b4488 | 107 | struct cfg80211_pmksa *pmksa); |
3e26416e | 108 | static s32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, |
cf2b4488 | 109 | struct cfg80211_pmksa *pmksa); |
3e26416e | 110 | static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, |
cf2b4488 HP |
111 | struct net_device *dev); |
112 | /* | |
113 | ** event & event Q handlers for cfg80211 interfaces | |
114 | */ | |
3e26416e | 115 | static s32 wl_create_event_handler(struct wl_priv *wl); |
cf2b4488 | 116 | static void wl_destroy_event_handler(struct wl_priv *wl); |
3e26416e | 117 | static s32 wl_event_handler(void *data); |
cf2b4488 HP |
118 | static void wl_init_eq(struct wl_priv *wl); |
119 | static void wl_flush_eq(struct wl_priv *wl); | |
120 | static void wl_lock_eq(struct wl_priv *wl); | |
121 | static void wl_unlock_eq(struct wl_priv *wl); | |
122 | static void wl_init_eq_lock(struct wl_priv *wl); | |
123 | static void wl_init_eloop_handler(struct wl_event_loop *el); | |
124 | static struct wl_event_q *wl_deq_event(struct wl_priv *wl); | |
3e26416e | 125 | static s32 wl_enq_event(struct wl_priv *wl, u32 type, |
cf2b4488 HP |
126 | const wl_event_msg_t *msg, void *data); |
127 | static void wl_put_event(struct wl_event_q *e); | |
128 | static void wl_wakeup_event(struct wl_priv *wl); | |
3e26416e | 129 | static s32 wl_notify_connect_status(struct wl_priv *wl, |
cf2b4488 HP |
130 | struct net_device *ndev, |
131 | const wl_event_msg_t *e, void *data); | |
3e26416e | 132 | static s32 wl_notify_roaming_status(struct wl_priv *wl, |
cf2b4488 HP |
133 | struct net_device *ndev, |
134 | const wl_event_msg_t *e, void *data); | |
3e26416e | 135 | static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, |
cf2b4488 | 136 | const wl_event_msg_t *e, void *data); |
3e26416e | 137 | static s32 wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, |
b3164c71 | 138 | const wl_event_msg_t *e, void *data, |
139 | bool completed); | |
3e26416e | 140 | static s32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, |
cf2b4488 | 141 | const wl_event_msg_t *e, void *data); |
3e26416e | 142 | static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, |
cf2b4488 HP |
143 | const wl_event_msg_t *e, void *data); |
144 | ||
145 | /* | |
146 | ** register/deregister sdio function | |
147 | */ | |
93ad12cf | 148 | struct sdio_func *wl_cfg80211_get_sdio_func(void); |
cf2b4488 HP |
149 | static void wl_clear_sdio_func(void); |
150 | ||
151 | /* | |
152 | ** ioctl utilites | |
153 | */ | |
3e26416e GKH |
154 | static s32 wl_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf, |
155 | s32 buf_len); | |
156 | static __used s32 wl_dev_bufvar_set(struct net_device *dev, s8 *name, | |
157 | s8 *buf, s32 len); | |
158 | static s32 wl_dev_intvar_set(struct net_device *dev, s8 *name, s32 val); | |
159 | static s32 wl_dev_intvar_get(struct net_device *dev, s8 *name, | |
160 | s32 *retval); | |
161 | static s32 wl_dev_ioctl(struct net_device *dev, u32 cmd, void *arg, | |
66cbd3ab | 162 | u32 len); |
cf2b4488 HP |
163 | |
164 | /* | |
165 | ** cfg80211 set_wiphy_params utilities | |
166 | */ | |
3e26416e GKH |
167 | static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold); |
168 | static s32 wl_set_rts(struct net_device *dev, u32 frag_threshold); | |
169 | static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l); | |
cf2b4488 HP |
170 | |
171 | /* | |
172 | ** wl profile utilities | |
173 | */ | |
3e26416e GKH |
174 | static s32 wl_update_prof(struct wl_priv *wl, const wl_event_msg_t *e, |
175 | void *data, s32 item); | |
176 | static void *wl_read_prof(struct wl_priv *wl, s32 item); | |
cf2b4488 HP |
177 | static void wl_init_prof(struct wl_profile *prof); |
178 | ||
179 | /* | |
180 | ** cfg80211 connect utilites | |
181 | */ | |
3e26416e | 182 | static s32 wl_set_wpa_version(struct net_device *dev, |
75494966 | 183 | struct cfg80211_connect_params *sme); |
3e26416e | 184 | static s32 wl_set_auth_type(struct net_device *dev, |
75494966 | 185 | struct cfg80211_connect_params *sme); |
3e26416e | 186 | static s32 wl_set_set_cipher(struct net_device *dev, |
75494966 | 187 | struct cfg80211_connect_params *sme); |
3e26416e | 188 | static s32 wl_set_key_mgmt(struct net_device *dev, |
75494966 | 189 | struct cfg80211_connect_params *sme); |
3e26416e | 190 | static s32 wl_set_set_sharedkey(struct net_device *dev, |
75494966 | 191 | struct cfg80211_connect_params *sme); |
3e26416e | 192 | static s32 wl_get_assoc_ies(struct wl_priv *wl); |
e494632e | 193 | static void wl_clear_assoc_ies(struct wl_priv *wl); |
fb693a71 | 194 | static void wl_ch_to_chanspec(int ch, |
195 | struct wl_join_params *join_params, size_t *join_params_size); | |
cf2b4488 HP |
196 | |
197 | /* | |
198 | ** information element utilities | |
199 | */ | |
5dc56c9f | 200 | static __used s32 wl_add_ie(struct wl_priv *wl, u8 t, u8 l, u8 *v); |
3e26416e | 201 | static s32 wl_mode_to_nl80211_iftype(s32 mode); |
3e26416e | 202 | static struct wireless_dev *wl_alloc_wdev(s32 sizeof_iface, |
75494966 | 203 | struct device *dev); |
cf2b4488 | 204 | static void wl_free_wdev(struct wl_priv *wl); |
3e26416e GKH |
205 | static s32 wl_inform_bss(struct wl_priv *wl); |
206 | static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi); | |
207 | static s32 wl_update_bss_info(struct wl_priv *wl); | |
3e26416e | 208 | static s32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, |
75494966 SS |
209 | u8 key_idx, const u8 *mac_addr, |
210 | struct key_params *params); | |
cf2b4488 HP |
211 | |
212 | /* | |
213 | ** key indianess swap utilities | |
214 | */ | |
215 | static void swap_key_from_BE(struct wl_wsec_key *key); | |
216 | static void swap_key_to_BE(struct wl_wsec_key *key); | |
217 | ||
218 | /* | |
219 | ** wl_priv memory init/deinit utilities | |
220 | */ | |
3e26416e | 221 | static s32 wl_init_priv_mem(struct wl_priv *wl); |
cf2b4488 HP |
222 | static void wl_deinit_priv_mem(struct wl_priv *wl); |
223 | ||
66cbd3ab | 224 | static void wl_delay(u32 ms); |
cf2b4488 HP |
225 | |
226 | /* | |
227 | ** store/restore cfg80211 instance data | |
228 | */ | |
229 | static void wl_set_drvdata(struct wl_dev *dev, void *data); | |
230 | static void *wl_get_drvdata(struct wl_dev *dev); | |
231 | ||
232 | /* | |
233 | ** ibss mode utilities | |
234 | */ | |
235 | static bool wl_is_ibssmode(struct wl_priv *wl); | |
cf2b4488 HP |
236 | |
237 | /* | |
238 | ** dongle up/down , default configuration utilities | |
239 | */ | |
240 | static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e); | |
241 | static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e); | |
b3164c71 | 242 | static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e); |
cf2b4488 | 243 | static void wl_link_down(struct wl_priv *wl); |
3e26416e GKH |
244 | static s32 wl_dongle_mode(struct net_device *ndev, s32 iftype); |
245 | static s32 __wl_cfg80211_up(struct wl_priv *wl); | |
246 | static s32 __wl_cfg80211_down(struct wl_priv *wl); | |
247 | static s32 wl_dongle_probecap(struct wl_priv *wl); | |
cf2b4488 HP |
248 | static void wl_init_conf(struct wl_conf *conf); |
249 | ||
250 | /* | |
251 | ** dongle configuration utilities | |
252 | */ | |
253 | #ifndef EMBEDDED_PLATFORM | |
3e26416e GKH |
254 | static s32 wl_dongle_mode(struct net_device *ndev, s32 iftype); |
255 | static s32 wl_dongle_country(struct net_device *ndev, u8 ccode); | |
256 | static s32 wl_dongle_up(struct net_device *ndev, u32 up); | |
257 | static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode); | |
258 | static s32 wl_dongle_glom(struct net_device *ndev, u32 glom, | |
66cbd3ab | 259 | u32 dongle_align); |
3e26416e GKH |
260 | static s32 wl_dongle_offload(struct net_device *ndev, s32 arpoe, |
261 | s32 arp_ol); | |
262 | static s32 wl_pattern_atoh(s8 *src, s8 *dst); | |
263 | static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode); | |
264 | static s32 wl_update_wiphybands(struct wl_priv *wl); | |
cf2b4488 | 265 | #endif /* !EMBEDDED_PLATFORM */ |
e494632e SS |
266 | |
267 | static s32 wl_dongle_eventmsg(struct net_device *ndev); | |
e4dd6325 SS |
268 | static s32 wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, |
269 | s32 scan_unassoc_time, s32 scan_passive_time); | |
3e26416e | 270 | static s32 wl_config_dongle(struct wl_priv *wl, bool need_lock); |
a1e962b6 SS |
271 | static s32 wl_dongle_roam(struct net_device *ndev, u32 roamvar, |
272 | u32 bcn_timeout); | |
cf2b4488 HP |
273 | |
274 | /* | |
275 | ** iscan handler | |
276 | */ | |
3deea904 | 277 | static void wl_iscan_timer(unsigned long data); |
cf2b4488 | 278 | static void wl_term_iscan(struct wl_priv *wl); |
3e26416e GKH |
279 | static s32 wl_init_iscan(struct wl_priv *wl); |
280 | static s32 wl_iscan_thread(void *data); | |
281 | static s32 wl_dev_iovar_setbuf(struct net_device *dev, s8 *iovar, | |
282 | void *param, s32 paramlen, void *bufptr, | |
283 | s32 buflen); | |
284 | static s32 wl_dev_iovar_getbuf(struct net_device *dev, s8 *iovar, | |
285 | void *param, s32 paramlen, void *bufptr, | |
286 | s32 buflen); | |
287 | static s32 wl_run_iscan(struct wl_iscan_ctrl *iscan, struct wlc_ssid *ssid, | |
7d4df48e | 288 | u16 action); |
3e26416e GKH |
289 | static s32 wl_do_iscan(struct wl_priv *wl); |
290 | static s32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan); | |
291 | static s32 wl_invoke_iscan(struct wl_priv *wl); | |
292 | static s32 wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, | |
cf2b4488 HP |
293 | struct wl_scan_results **bss_list); |
294 | static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted); | |
295 | static void wl_init_iscan_eloop(struct wl_iscan_eloop *el); | |
3e26416e GKH |
296 | static s32 wl_iscan_done(struct wl_priv *wl); |
297 | static s32 wl_iscan_pending(struct wl_priv *wl); | |
298 | static s32 wl_iscan_inprogress(struct wl_priv *wl); | |
299 | static s32 wl_iscan_aborted(struct wl_priv *wl); | |
cf2b4488 HP |
300 | |
301 | /* | |
302 | ** fw/nvram downloading handler | |
303 | */ | |
304 | static void wl_init_fw(struct wl_fw_ctrl *fw); | |
305 | ||
306 | /* | |
307 | * find most significant bit set | |
308 | */ | |
66cbd3ab | 309 | static __used u32 wl_find_msb(u16 bit16); |
cf2b4488 HP |
310 | |
311 | /* | |
312 | * update pmklist to dongle | |
313 | */ | |
3e26416e GKH |
314 | static __used s32 wl_update_pmklist(struct net_device *dev, |
315 | struct wl_pmk_list *pmk_list, s32 err); | |
cf2b4488 | 316 | |
fb693a71 | 317 | static void wl_set_mpc(struct net_device *ndev, int mpc); |
318 | ||
cd389a34 | 319 | /* |
320 | * debufs support | |
321 | */ | |
322 | static int wl_debugfs_add_netdev_params(struct wl_priv *wl); | |
323 | static void wl_debugfs_remove_netdev(struct wl_priv *wl); | |
324 | ||
cf2b4488 HP |
325 | #define WL_PRIV_GET() \ |
326 | ({ \ | |
327 | struct wl_iface *ci; \ | |
328 | if (unlikely(!(wl_cfg80211_dev && \ | |
329 | (ci = wl_get_drvdata(wl_cfg80211_dev))))) { \ | |
f4528696 | 330 | WL_ERR("wl_cfg80211_dev is unavailable\n"); \ |
cf2b4488 HP |
331 | BUG(); \ |
332 | } \ | |
333 | ci_to_wl(ci); \ | |
334 | }) | |
335 | ||
336 | #define CHECK_SYS_UP() \ | |
337 | do { \ | |
338 | struct wl_priv *wl = wiphy_to_wl(wiphy); \ | |
339 | if (unlikely(!test_bit(WL_STATUS_READY, &wl->status))) { \ | |
f4528696 JP |
340 | WL_INFO("device is not ready : status (%d)\n", \ |
341 | (int)wl->status); \ | |
cf2b4488 HP |
342 | return -EIO; \ |
343 | } \ | |
344 | } while (0) | |
345 | ||
346 | extern int dhd_wait_pend8021x(struct net_device *dev); | |
cf2b4488 HP |
347 | #define CHAN2G(_channel, _freq, _flags) { \ |
348 | .band = IEEE80211_BAND_2GHZ, \ | |
349 | .center_freq = (_freq), \ | |
350 | .hw_value = (_channel), \ | |
351 | .flags = (_flags), \ | |
352 | .max_antenna_gain = 0, \ | |
353 | .max_power = 30, \ | |
354 | } | |
355 | ||
356 | #define CHAN5G(_channel, _flags) { \ | |
357 | .band = IEEE80211_BAND_5GHZ, \ | |
358 | .center_freq = 5000 + (5 * (_channel)), \ | |
359 | .hw_value = (_channel), \ | |
360 | .flags = (_flags), \ | |
361 | .max_antenna_gain = 0, \ | |
362 | .max_power = 30, \ | |
363 | } | |
364 | ||
365 | #define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) | |
366 | #define RATETAB_ENT(_rateid, _flags) \ | |
367 | { \ | |
368 | .bitrate = RATE_TO_BASE100KBPS(_rateid), \ | |
369 | .hw_value = (_rateid), \ | |
370 | .flags = (_flags), \ | |
371 | } | |
372 | ||
373 | static struct ieee80211_rate __wl_rates[] = { | |
374 | RATETAB_ENT(WLC_RATE_1M, 0), | |
375 | RATETAB_ENT(WLC_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), | |
376 | RATETAB_ENT(WLC_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), | |
377 | RATETAB_ENT(WLC_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), | |
378 | RATETAB_ENT(WLC_RATE_6M, 0), | |
379 | RATETAB_ENT(WLC_RATE_9M, 0), | |
380 | RATETAB_ENT(WLC_RATE_12M, 0), | |
381 | RATETAB_ENT(WLC_RATE_18M, 0), | |
382 | RATETAB_ENT(WLC_RATE_24M, 0), | |
383 | RATETAB_ENT(WLC_RATE_36M, 0), | |
384 | RATETAB_ENT(WLC_RATE_48M, 0), | |
385 | RATETAB_ENT(WLC_RATE_54M, 0), | |
386 | }; | |
387 | ||
388 | #define wl_a_rates (__wl_rates + 4) | |
389 | #define wl_a_rates_size 8 | |
390 | #define wl_g_rates (__wl_rates + 0) | |
391 | #define wl_g_rates_size 12 | |
392 | ||
393 | static struct ieee80211_channel __wl_2ghz_channels[] = { | |
394 | CHAN2G(1, 2412, 0), | |
395 | CHAN2G(2, 2417, 0), | |
396 | CHAN2G(3, 2422, 0), | |
397 | CHAN2G(4, 2427, 0), | |
398 | CHAN2G(5, 2432, 0), | |
399 | CHAN2G(6, 2437, 0), | |
400 | CHAN2G(7, 2442, 0), | |
401 | CHAN2G(8, 2447, 0), | |
402 | CHAN2G(9, 2452, 0), | |
403 | CHAN2G(10, 2457, 0), | |
404 | CHAN2G(11, 2462, 0), | |
405 | CHAN2G(12, 2467, 0), | |
406 | CHAN2G(13, 2472, 0), | |
407 | CHAN2G(14, 2484, 0), | |
408 | }; | |
409 | ||
410 | static struct ieee80211_channel __wl_5ghz_a_channels[] = { | |
411 | CHAN5G(34, 0), CHAN5G(36, 0), | |
412 | CHAN5G(38, 0), CHAN5G(40, 0), | |
413 | CHAN5G(42, 0), CHAN5G(44, 0), | |
414 | CHAN5G(46, 0), CHAN5G(48, 0), | |
415 | CHAN5G(52, 0), CHAN5G(56, 0), | |
416 | CHAN5G(60, 0), CHAN5G(64, 0), | |
417 | CHAN5G(100, 0), CHAN5G(104, 0), | |
418 | CHAN5G(108, 0), CHAN5G(112, 0), | |
419 | CHAN5G(116, 0), CHAN5G(120, 0), | |
420 | CHAN5G(124, 0), CHAN5G(128, 0), | |
421 | CHAN5G(132, 0), CHAN5G(136, 0), | |
422 | CHAN5G(140, 0), CHAN5G(149, 0), | |
423 | CHAN5G(153, 0), CHAN5G(157, 0), | |
424 | CHAN5G(161, 0), CHAN5G(165, 0), | |
425 | CHAN5G(184, 0), CHAN5G(188, 0), | |
426 | CHAN5G(192, 0), CHAN5G(196, 0), | |
427 | CHAN5G(200, 0), CHAN5G(204, 0), | |
428 | CHAN5G(208, 0), CHAN5G(212, 0), | |
429 | CHAN5G(216, 0), | |
430 | }; | |
431 | ||
432 | static struct ieee80211_channel __wl_5ghz_n_channels[] = { | |
433 | CHAN5G(32, 0), CHAN5G(34, 0), | |
434 | CHAN5G(36, 0), CHAN5G(38, 0), | |
435 | CHAN5G(40, 0), CHAN5G(42, 0), | |
436 | CHAN5G(44, 0), CHAN5G(46, 0), | |
437 | CHAN5G(48, 0), CHAN5G(50, 0), | |
438 | CHAN5G(52, 0), CHAN5G(54, 0), | |
439 | CHAN5G(56, 0), CHAN5G(58, 0), | |
440 | CHAN5G(60, 0), CHAN5G(62, 0), | |
441 | CHAN5G(64, 0), CHAN5G(66, 0), | |
442 | CHAN5G(68, 0), CHAN5G(70, 0), | |
443 | CHAN5G(72, 0), CHAN5G(74, 0), | |
444 | CHAN5G(76, 0), CHAN5G(78, 0), | |
445 | CHAN5G(80, 0), CHAN5G(82, 0), | |
446 | CHAN5G(84, 0), CHAN5G(86, 0), | |
447 | CHAN5G(88, 0), CHAN5G(90, 0), | |
448 | CHAN5G(92, 0), CHAN5G(94, 0), | |
449 | CHAN5G(96, 0), CHAN5G(98, 0), | |
450 | CHAN5G(100, 0), CHAN5G(102, 0), | |
451 | CHAN5G(104, 0), CHAN5G(106, 0), | |
452 | CHAN5G(108, 0), CHAN5G(110, 0), | |
453 | CHAN5G(112, 0), CHAN5G(114, 0), | |
454 | CHAN5G(116, 0), CHAN5G(118, 0), | |
455 | CHAN5G(120, 0), CHAN5G(122, 0), | |
456 | CHAN5G(124, 0), CHAN5G(126, 0), | |
457 | CHAN5G(128, 0), CHAN5G(130, 0), | |
458 | CHAN5G(132, 0), CHAN5G(134, 0), | |
459 | CHAN5G(136, 0), CHAN5G(138, 0), | |
460 | CHAN5G(140, 0), CHAN5G(142, 0), | |
461 | CHAN5G(144, 0), CHAN5G(145, 0), | |
462 | CHAN5G(146, 0), CHAN5G(147, 0), | |
463 | CHAN5G(148, 0), CHAN5G(149, 0), | |
464 | CHAN5G(150, 0), CHAN5G(151, 0), | |
465 | CHAN5G(152, 0), CHAN5G(153, 0), | |
466 | CHAN5G(154, 0), CHAN5G(155, 0), | |
467 | CHAN5G(156, 0), CHAN5G(157, 0), | |
468 | CHAN5G(158, 0), CHAN5G(159, 0), | |
469 | CHAN5G(160, 0), CHAN5G(161, 0), | |
470 | CHAN5G(162, 0), CHAN5G(163, 0), | |
471 | CHAN5G(164, 0), CHAN5G(165, 0), | |
472 | CHAN5G(166, 0), CHAN5G(168, 0), | |
473 | CHAN5G(170, 0), CHAN5G(172, 0), | |
474 | CHAN5G(174, 0), CHAN5G(176, 0), | |
475 | CHAN5G(178, 0), CHAN5G(180, 0), | |
476 | CHAN5G(182, 0), CHAN5G(184, 0), | |
477 | CHAN5G(186, 0), CHAN5G(188, 0), | |
478 | CHAN5G(190, 0), CHAN5G(192, 0), | |
479 | CHAN5G(194, 0), CHAN5G(196, 0), | |
480 | CHAN5G(198, 0), CHAN5G(200, 0), | |
481 | CHAN5G(202, 0), CHAN5G(204, 0), | |
482 | CHAN5G(206, 0), CHAN5G(208, 0), | |
483 | CHAN5G(210, 0), CHAN5G(212, 0), | |
484 | CHAN5G(214, 0), CHAN5G(216, 0), | |
485 | CHAN5G(218, 0), CHAN5G(220, 0), | |
486 | CHAN5G(222, 0), CHAN5G(224, 0), | |
487 | CHAN5G(226, 0), CHAN5G(228, 0), | |
488 | }; | |
489 | ||
490 | static struct ieee80211_supported_band __wl_band_2ghz = { | |
491 | .band = IEEE80211_BAND_2GHZ, | |
492 | .channels = __wl_2ghz_channels, | |
493 | .n_channels = ARRAY_SIZE(__wl_2ghz_channels), | |
494 | .bitrates = wl_g_rates, | |
495 | .n_bitrates = wl_g_rates_size, | |
496 | }; | |
497 | ||
498 | static struct ieee80211_supported_band __wl_band_5ghz_a = { | |
499 | .band = IEEE80211_BAND_5GHZ, | |
500 | .channels = __wl_5ghz_a_channels, | |
501 | .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), | |
502 | .bitrates = wl_a_rates, | |
503 | .n_bitrates = wl_a_rates_size, | |
504 | }; | |
505 | ||
506 | static struct ieee80211_supported_band __wl_band_5ghz_n = { | |
507 | .band = IEEE80211_BAND_5GHZ, | |
508 | .channels = __wl_5ghz_n_channels, | |
509 | .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels), | |
510 | .bitrates = wl_a_rates, | |
511 | .n_bitrates = wl_a_rates_size, | |
512 | }; | |
513 | ||
66cbd3ab | 514 | static const u32 __wl_cipher_suites[] = { |
cf2b4488 HP |
515 | WLAN_CIPHER_SUITE_WEP40, |
516 | WLAN_CIPHER_SUITE_WEP104, | |
517 | WLAN_CIPHER_SUITE_TKIP, | |
518 | WLAN_CIPHER_SUITE_CCMP, | |
519 | WLAN_CIPHER_SUITE_AES_CMAC, | |
520 | }; | |
521 | ||
522 | static void swap_key_from_BE(struct wl_wsec_key *key) | |
523 | { | |
29750b90 SF |
524 | key->index = cpu_to_le32(key->index); |
525 | key->len = cpu_to_le32(key->len); | |
526 | key->algo = cpu_to_le32(key->algo); | |
527 | key->flags = cpu_to_le32(key->flags); | |
528 | key->rxiv.hi = cpu_to_le32(key->rxiv.hi); | |
529 | key->rxiv.lo = cpu_to_le16(key->rxiv.lo); | |
530 | key->iv_initialized = cpu_to_le32(key->iv_initialized); | |
cf2b4488 HP |
531 | } |
532 | ||
533 | static void swap_key_to_BE(struct wl_wsec_key *key) | |
534 | { | |
29750b90 SF |
535 | key->index = le32_to_cpu(key->index); |
536 | key->len = le32_to_cpu(key->len); | |
537 | key->algo = le32_to_cpu(key->algo); | |
538 | key->flags = le32_to_cpu(key->flags); | |
539 | key->rxiv.hi = le32_to_cpu(key->rxiv.hi); | |
540 | key->rxiv.lo = le16_to_cpu(key->rxiv.lo); | |
541 | key->iv_initialized = le32_to_cpu(key->iv_initialized); | |
cf2b4488 HP |
542 | } |
543 | ||
3e26416e | 544 | static s32 |
66cbd3ab | 545 | wl_dev_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len) |
cf2b4488 HP |
546 | { |
547 | struct ifreq ifr; | |
548 | struct wl_ioctl ioc; | |
549 | mm_segment_t fs; | |
3e26416e | 550 | s32 err = 0; |
cf2b4488 HP |
551 | |
552 | memset(&ioc, 0, sizeof(ioc)); | |
553 | ioc.cmd = cmd; | |
554 | ioc.buf = arg; | |
555 | ioc.len = len; | |
556 | strcpy(ifr.ifr_name, dev->name); | |
557 | ifr.ifr_data = (caddr_t)&ioc; | |
558 | ||
559 | fs = get_fs(); | |
560 | set_fs(get_ds()); | |
561 | err = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE); | |
562 | set_fs(fs); | |
563 | ||
564 | return err; | |
565 | } | |
566 | ||
3e26416e | 567 | static s32 |
cf2b4488 | 568 | wl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, |
66cbd3ab | 569 | enum nl80211_iftype type, u32 *flags, |
cf2b4488 HP |
570 | struct vif_params *params) |
571 | { | |
572 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
573 | struct wireless_dev *wdev; | |
3e26416e | 574 | s32 infra = 0; |
3e26416e | 575 | s32 err = 0; |
cf2b4488 | 576 | |
1ce4784e | 577 | WL_TRACE("Enter\n"); |
cf2b4488 | 578 | CHECK_SYS_UP(); |
69274f02 | 579 | |
cf2b4488 HP |
580 | switch (type) { |
581 | case NL80211_IFTYPE_MONITOR: | |
582 | case NL80211_IFTYPE_WDS: | |
f4528696 JP |
583 | WL_ERR("type (%d) : currently we do not support this type\n", |
584 | type); | |
cf2b4488 HP |
585 | return -EOPNOTSUPP; |
586 | case NL80211_IFTYPE_ADHOC: | |
587 | wl->conf->mode = WL_MODE_IBSS; | |
69274f02 | 588 | infra = 0; |
cf2b4488 HP |
589 | break; |
590 | case NL80211_IFTYPE_STATION: | |
591 | wl->conf->mode = WL_MODE_BSS; | |
592 | infra = 1; | |
593 | break; | |
594 | default: | |
69274f02 SS |
595 | err = -EINVAL; |
596 | goto done; | |
cf2b4488 | 597 | } |
69274f02 | 598 | |
29750b90 | 599 | infra = cpu_to_le32(infra); |
76c06459 JC |
600 | err = wl_dev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra)); |
601 | if (unlikely(err)) { | |
f4528696 | 602 | WL_ERR("WLC_SET_INFRA error (%d)\n", err); |
69274f02 SS |
603 | err = -EAGAIN; |
604 | } else { | |
605 | wdev = ndev->ieee80211_ptr; | |
606 | wdev->iftype = type; | |
cf2b4488 | 607 | } |
76c06459 | 608 | |
69274f02 SS |
609 | WL_INFO("IF Type = %s\n", |
610 | (wl->conf->mode == WL_MODE_IBSS) ? "Adhoc" : "Infra"); | |
611 | ||
612 | done: | |
1ce4784e SS |
613 | WL_TRACE("Exit\n"); |
614 | ||
69274f02 | 615 | return err; |
cf2b4488 HP |
616 | } |
617 | ||
618 | static void wl_iscan_prep(struct wl_scan_params *params, struct wlc_ssid *ssid) | |
619 | { | |
a44d4236 | 620 | memcpy(params->bssid, ether_bcast, ETH_ALEN); |
cf2b4488 HP |
621 | params->bss_type = DOT11_BSSTYPE_ANY; |
622 | params->scan_type = 0; | |
623 | params->nprobes = -1; | |
624 | params->active_time = -1; | |
625 | params->passive_time = -1; | |
626 | params->home_time = -1; | |
627 | params->channel_num = 0; | |
628 | ||
29750b90 SF |
629 | params->nprobes = cpu_to_le32(params->nprobes); |
630 | params->active_time = cpu_to_le32(params->active_time); | |
631 | params->passive_time = cpu_to_le32(params->passive_time); | |
632 | params->home_time = cpu_to_le32(params->home_time); | |
cf2b4488 HP |
633 | if (ssid && ssid->SSID_len) |
634 | memcpy(¶ms->ssid, ssid, sizeof(wlc_ssid_t)); | |
635 | ||
636 | } | |
637 | ||
3e26416e | 638 | static s32 |
562c8850 | 639 | wl_dev_iovar_setbuf(struct net_device *dev, s8 * iovar, void *param, |
3e26416e | 640 | s32 paramlen, void *bufptr, s32 buflen) |
cf2b4488 | 641 | { |
3e26416e | 642 | s32 iolen; |
cf2b4488 HP |
643 | |
644 | iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); | |
d7ddd169 | 645 | BUG_ON(!iolen); |
cf2b4488 HP |
646 | |
647 | return wl_dev_ioctl(dev, WLC_SET_VAR, bufptr, iolen); | |
648 | } | |
649 | ||
3e26416e | 650 | static s32 |
562c8850 | 651 | wl_dev_iovar_getbuf(struct net_device *dev, s8 * iovar, void *param, |
3e26416e | 652 | s32 paramlen, void *bufptr, s32 buflen) |
cf2b4488 | 653 | { |
3e26416e | 654 | s32 iolen; |
cf2b4488 HP |
655 | |
656 | iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); | |
d7ddd169 | 657 | BUG_ON(!iolen); |
cf2b4488 HP |
658 | |
659 | return wl_dev_ioctl(dev, WLC_GET_VAR, bufptr, buflen); | |
660 | } | |
661 | ||
3e26416e | 662 | static s32 |
7d4df48e | 663 | wl_run_iscan(struct wl_iscan_ctrl *iscan, struct wlc_ssid *ssid, u16 action) |
cf2b4488 | 664 | { |
3e26416e | 665 | s32 params_size = |
ce0f1b8c | 666 | (WL_SCAN_PARAMS_FIXED_SIZE + offsetof(wl_iscan_params_t, params)); |
cf2b4488 | 667 | struct wl_iscan_params *params; |
3e26416e | 668 | s32 err = 0; |
cf2b4488 HP |
669 | |
670 | if (ssid && ssid->SSID_len) | |
671 | params_size += sizeof(struct wlc_ssid); | |
3b785a8c | 672 | params = kzalloc(params_size, GFP_KERNEL); |
cf2b4488 HP |
673 | if (unlikely(!params)) |
674 | return -ENOMEM; | |
d7ddd169 | 675 | BUG_ON(params_size >= WLC_IOCTL_SMLEN); |
cf2b4488 HP |
676 | |
677 | wl_iscan_prep(¶ms->params, ssid); | |
678 | ||
29750b90 SF |
679 | params->version = cpu_to_le32(ISCAN_REQ_VERSION); |
680 | params->action = cpu_to_le16(action); | |
681 | params->scan_duration = cpu_to_le16(0); | |
cf2b4488 | 682 | |
ce0f1b8c | 683 | /* params_size += offsetof(wl_iscan_params_t, params); */ |
76c06459 JC |
684 | err = wl_dev_iovar_setbuf(iscan->dev, "iscan", params, params_size, |
685 | iscan->ioctl_buf, WLC_IOCTL_SMLEN); | |
686 | if (unlikely(err)) { | |
cf2b4488 | 687 | if (err == -EBUSY) { |
f4528696 | 688 | WL_INFO("system busy : iscan canceled\n"); |
cf2b4488 | 689 | } else { |
f4528696 | 690 | WL_ERR("error (%d)\n", err); |
cf2b4488 HP |
691 | } |
692 | } | |
693 | kfree(params); | |
694 | return err; | |
695 | } | |
696 | ||
3e26416e | 697 | static s32 wl_do_iscan(struct wl_priv *wl) |
cf2b4488 HP |
698 | { |
699 | struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); | |
fb693a71 | 700 | struct net_device *ndev = wl_to_ndev(wl); |
cf2b4488 | 701 | struct wlc_ssid ssid; |
c5ca038f | 702 | s32 passive_scan; |
3e26416e | 703 | s32 err = 0; |
cf2b4488 HP |
704 | |
705 | /* Broadcast scan by default */ | |
706 | memset(&ssid, 0, sizeof(ssid)); | |
707 | ||
708 | iscan->state = WL_ISCAN_STATE_SCANING; | |
709 | ||
c5ca038f | 710 | passive_scan = wl->active_scan ? 0 : 1; |
711 | err = wl_dev_ioctl(wl_to_ndev(wl), WLC_SET_PASSIVE_SCAN, | |
712 | &passive_scan, sizeof(passive_scan)); | |
713 | if (unlikely(err)) { | |
1ce4784e | 714 | WL_ERR("error (%d)\n", err); |
c5ca038f | 715 | return err; |
cf2b4488 | 716 | } |
fb693a71 | 717 | wl_set_mpc(ndev, 0); |
0f0881b0 | 718 | wl->iscan_kickstart = true; |
cf2b4488 HP |
719 | wl_run_iscan(iscan, &ssid, WL_SCAN_ACTION_START); |
720 | mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); | |
721 | iscan->timer_on = 1; | |
722 | ||
723 | return err; | |
724 | } | |
725 | ||
3e26416e | 726 | static s32 |
cf2b4488 HP |
727 | __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, |
728 | struct cfg80211_scan_request *request, | |
729 | struct cfg80211_ssid *this_ssid) | |
730 | { | |
731 | struct wl_priv *wl = ndev_to_wl(ndev); | |
732 | struct cfg80211_ssid *ssids; | |
733 | struct wl_scan_req *sr = wl_to_sr(wl); | |
c5ca038f | 734 | s32 passive_scan; |
cf2b4488 HP |
735 | bool iscan_req; |
736 | bool spec_scan; | |
3e26416e | 737 | s32 err = 0; |
cf2b4488 HP |
738 | |
739 | if (unlikely(test_bit(WL_STATUS_SCANNING, &wl->status))) { | |
f4528696 | 740 | WL_ERR("Scanning already : status (%d)\n", (int)wl->status); |
cf2b4488 HP |
741 | return -EAGAIN; |
742 | } | |
743 | if (unlikely(test_bit(WL_STATUS_SCAN_ABORTING, &wl->status))) { | |
f4528696 JP |
744 | WL_ERR("Scanning being aborted : status (%d)\n", |
745 | (int)wl->status); | |
cf2b4488 HP |
746 | return -EAGAIN; |
747 | } | |
9446af06 SS |
748 | if (test_bit(WL_STATUS_CONNECTING, &wl->status)) { |
749 | WL_ERR("Connecting : status (%d)\n", | |
750 | (int)wl->status); | |
751 | return -EAGAIN; | |
752 | } | |
cf2b4488 | 753 | |
0965ae88 GKH |
754 | iscan_req = false; |
755 | spec_scan = false; | |
9446af06 SS |
756 | if (request) { |
757 | /* scan bss */ | |
cf2b4488 | 758 | ssids = request->ssids; |
9446af06 | 759 | if (wl->iscan_on && (!ssids || !ssids->ssid_len)) |
0f0881b0 | 760 | iscan_req = true; |
9446af06 SS |
761 | } else { |
762 | /* scan in ibss */ | |
cf2b4488 HP |
763 | /* we don't do iscan in ibss */ |
764 | ssids = this_ssid; | |
cf2b4488 | 765 | } |
9446af06 | 766 | |
cf2b4488 HP |
767 | wl->scan_request = request; |
768 | set_bit(WL_STATUS_SCANNING, &wl->status); | |
769 | if (iscan_req) { | |
76c06459 | 770 | err = wl_do_iscan(wl); |
fb693a71 | 771 | if (likely(!err)) |
cf2b4488 HP |
772 | return err; |
773 | else | |
774 | goto scan_out; | |
775 | } else { | |
1ce4784e | 776 | WL_SCAN("ssid \"%s\", ssid_len (%d)\n", |
f4528696 | 777 | ssids->ssid, ssids->ssid_len); |
cf2b4488 | 778 | memset(&sr->ssid, 0, sizeof(sr->ssid)); |
93ad12cf | 779 | sr->ssid.SSID_len = |
b61640d1 | 780 | min_t(u8, sizeof(sr->ssid.SSID), ssids->ssid_len); |
93ad12cf | 781 | if (sr->ssid.SSID_len) { |
782 | memcpy(sr->ssid.SSID, ssids->ssid, sr->ssid.SSID_len); | |
29750b90 | 783 | sr->ssid.SSID_len = cpu_to_le32(sr->ssid.SSID_len); |
0f0881b0 | 784 | spec_scan = true; |
cf2b4488 | 785 | } else { |
1ce4784e | 786 | WL_SCAN("Broadcast scan\n"); |
cf2b4488 | 787 | } |
1ce4784e | 788 | |
c5ca038f | 789 | passive_scan = wl->active_scan ? 0 : 1; |
790 | err = wl_dev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, | |
791 | &passive_scan, sizeof(passive_scan)); | |
792 | if (unlikely(err)) { | |
f4528696 | 793 | WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err); |
c5ca038f | 794 | goto scan_out; |
cf2b4488 | 795 | } |
fb693a71 | 796 | wl_set_mpc(ndev, 0); |
76c06459 JC |
797 | err = wl_dev_ioctl(ndev, WLC_SCAN, &sr->ssid, |
798 | sizeof(sr->ssid)); | |
799 | if (err) { | |
cf2b4488 | 800 | if (err == -EBUSY) { |
f4528696 JP |
801 | WL_INFO("system busy : scan for \"%s\" canceled\n", |
802 | sr->ssid.SSID); | |
cf2b4488 | 803 | } else { |
f4528696 | 804 | WL_ERR("WLC_SCAN error (%d)\n", err); |
cf2b4488 | 805 | } |
fb693a71 | 806 | wl_set_mpc(ndev, 1); |
cf2b4488 HP |
807 | goto scan_out; |
808 | } | |
809 | } | |
810 | ||
811 | return 0; | |
812 | ||
813 | scan_out: | |
814 | clear_bit(WL_STATUS_SCANNING, &wl->status); | |
815 | wl->scan_request = NULL; | |
816 | return err; | |
817 | } | |
818 | ||
3e26416e | 819 | static s32 |
cf2b4488 HP |
820 | wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, |
821 | struct cfg80211_scan_request *request) | |
822 | { | |
3e26416e | 823 | s32 err = 0; |
cf2b4488 | 824 | |
1ce4784e SS |
825 | WL_TRACE("Enter\n"); |
826 | ||
cf2b4488 | 827 | CHECK_SYS_UP(); |
1ce4784e | 828 | |
76c06459 | 829 | err = __wl_cfg80211_scan(wiphy, ndev, request, NULL); |
1ce4784e SS |
830 | if (unlikely(err)) |
831 | WL_ERR("scan error (%d)\n", err); | |
cf2b4488 | 832 | |
1ce4784e | 833 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
834 | return err; |
835 | } | |
836 | ||
3e26416e | 837 | static s32 wl_dev_intvar_set(struct net_device *dev, s8 *name, s32 val) |
cf2b4488 | 838 | { |
562c8850 | 839 | s8 buf[WLC_IOCTL_SMLEN]; |
66cbd3ab | 840 | u32 len; |
3e26416e | 841 | s32 err = 0; |
cf2b4488 | 842 | |
29750b90 | 843 | val = cpu_to_le32(val); |
cf2b4488 | 844 | len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf)); |
d7ddd169 | 845 | BUG_ON(!len); |
cf2b4488 | 846 | |
76c06459 | 847 | err = wl_dev_ioctl(dev, WLC_SET_VAR, buf, len); |
1ce4784e | 848 | if (unlikely(err)) |
f4528696 | 849 | WL_ERR("error (%d)\n", err); |
cf2b4488 HP |
850 | |
851 | return err; | |
852 | } | |
853 | ||
3e26416e GKH |
854 | static s32 |
855 | wl_dev_intvar_get(struct net_device *dev, s8 *name, s32 *retval) | |
cf2b4488 HP |
856 | { |
857 | union { | |
562c8850 | 858 | s8 buf[WLC_IOCTL_SMLEN]; |
3e26416e | 859 | s32 val; |
cf2b4488 | 860 | } var; |
66cbd3ab GKH |
861 | u32 len; |
862 | u32 data_null; | |
3e26416e | 863 | s32 err = 0; |
cf2b4488 HP |
864 | |
865 | len = | |
866 | bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), | |
867 | sizeof(var.buf)); | |
d7ddd169 | 868 | BUG_ON(!len); |
76c06459 | 869 | err = wl_dev_ioctl(dev, WLC_GET_VAR, &var, len); |
1ce4784e | 870 | if (unlikely(err)) |
f4528696 | 871 | WL_ERR("error (%d)\n", err); |
1ce4784e | 872 | |
29750b90 | 873 | *retval = le32_to_cpu(var.val); |
cf2b4488 HP |
874 | |
875 | return err; | |
876 | } | |
877 | ||
3e26416e | 878 | static s32 wl_set_rts(struct net_device *dev, u32 rts_threshold) |
cf2b4488 | 879 | { |
3e26416e | 880 | s32 err = 0; |
cf2b4488 | 881 | |
76c06459 | 882 | err = wl_dev_intvar_set(dev, "rtsthresh", rts_threshold); |
1ce4784e | 883 | if (unlikely(err)) |
f4528696 | 884 | WL_ERR("Error (%d)\n", err); |
1ce4784e | 885 | |
cf2b4488 HP |
886 | return err; |
887 | } | |
888 | ||
3e26416e | 889 | static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold) |
cf2b4488 | 890 | { |
3e26416e | 891 | s32 err = 0; |
cf2b4488 | 892 | |
76c06459 | 893 | err = wl_dev_intvar_set(dev, "fragthresh", frag_threshold); |
1ce4784e | 894 | if (unlikely(err)) |
f4528696 | 895 | WL_ERR("Error (%d)\n", err); |
1ce4784e | 896 | |
cf2b4488 HP |
897 | return err; |
898 | } | |
899 | ||
3e26416e | 900 | static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l) |
cf2b4488 | 901 | { |
3e26416e | 902 | s32 err = 0; |
66cbd3ab | 903 | u32 cmd = (l ? WLC_SET_LRL : WLC_SET_SRL); |
cf2b4488 | 904 | |
29750b90 | 905 | retry = cpu_to_le32(retry); |
76c06459 JC |
906 | err = wl_dev_ioctl(dev, cmd, &retry, sizeof(retry)); |
907 | if (unlikely(err)) { | |
f4528696 | 908 | WL_ERR("cmd (%d) , error (%d)\n", cmd, err); |
cf2b4488 HP |
909 | return err; |
910 | } | |
911 | return err; | |
912 | } | |
913 | ||
3e26416e | 914 | static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) |
cf2b4488 HP |
915 | { |
916 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
917 | struct net_device *ndev = wl_to_ndev(wl); | |
3e26416e | 918 | s32 err = 0; |
cf2b4488 | 919 | |
1ce4784e | 920 | WL_TRACE("Enter\n"); |
cf2b4488 | 921 | CHECK_SYS_UP(); |
1ce4784e | 922 | |
cf2b4488 HP |
923 | if (changed & WIPHY_PARAM_RTS_THRESHOLD && |
924 | (wl->conf->rts_threshold != wiphy->rts_threshold)) { | |
925 | wl->conf->rts_threshold = wiphy->rts_threshold; | |
76c06459 JC |
926 | err = wl_set_rts(ndev, wl->conf->rts_threshold); |
927 | if (!err) | |
1ce4784e | 928 | goto done; |
cf2b4488 HP |
929 | } |
930 | if (changed & WIPHY_PARAM_FRAG_THRESHOLD && | |
931 | (wl->conf->frag_threshold != wiphy->frag_threshold)) { | |
932 | wl->conf->frag_threshold = wiphy->frag_threshold; | |
76c06459 JC |
933 | err = wl_set_frag(ndev, wl->conf->frag_threshold); |
934 | if (!err) | |
1ce4784e | 935 | goto done; |
cf2b4488 HP |
936 | } |
937 | if (changed & WIPHY_PARAM_RETRY_LONG | |
938 | && (wl->conf->retry_long != wiphy->retry_long)) { | |
939 | wl->conf->retry_long = wiphy->retry_long; | |
0f0881b0 | 940 | err = wl_set_retry(ndev, wl->conf->retry_long, true); |
76c06459 | 941 | if (!err) |
1ce4784e | 942 | goto done; |
cf2b4488 HP |
943 | } |
944 | if (changed & WIPHY_PARAM_RETRY_SHORT | |
945 | && (wl->conf->retry_short != wiphy->retry_short)) { | |
946 | wl->conf->retry_short = wiphy->retry_short; | |
0965ae88 | 947 | err = wl_set_retry(ndev, wl->conf->retry_short, false); |
1ce4784e SS |
948 | if (!err) |
949 | goto done; | |
cf2b4488 HP |
950 | } |
951 | ||
1ce4784e SS |
952 | done: |
953 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
954 | return err; |
955 | } | |
956 | ||
3e26416e | 957 | static s32 |
cf2b4488 HP |
958 | wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, |
959 | struct cfg80211_ibss_params *params) | |
960 | { | |
961 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
cf2b4488 | 962 | struct wl_join_params join_params; |
69274f02 | 963 | size_t join_params_size = 0; |
3e26416e | 964 | s32 err = 0; |
69274f02 SS |
965 | s32 wsec = 0; |
966 | s32 bcnprd; | |
cf2b4488 | 967 | |
1ce4784e | 968 | WL_TRACE("Enter\n"); |
cf2b4488 | 969 | CHECK_SYS_UP(); |
69274f02 SS |
970 | |
971 | if (params->ssid) | |
1ce4784e | 972 | WL_CONN("SSID: %s\n", params->ssid); |
69274f02 | 973 | else { |
1ce4784e | 974 | WL_CONN("SSID: NULL, Not supported\n"); |
cf2b4488 HP |
975 | return -EOPNOTSUPP; |
976 | } | |
69274f02 SS |
977 | |
978 | if (params->bssid) | |
1ce4784e | 979 | WL_CONN("BSSID: %02X %02X %02X %02X %02X %02X\n", |
69274f02 SS |
980 | params->bssid[0], params->bssid[1], params->bssid[2], |
981 | params->bssid[3], params->bssid[4], params->bssid[5]); | |
982 | else | |
1ce4784e | 983 | WL_CONN("No BSSID specified\n"); |
69274f02 SS |
984 | |
985 | if (params->channel) | |
1ce4784e | 986 | WL_CONN("channel: %d\n", params->channel->center_freq); |
69274f02 | 987 | else |
1ce4784e | 988 | WL_CONN("no channel specified\n"); |
69274f02 SS |
989 | |
990 | if (params->channel_fixed) | |
1ce4784e | 991 | WL_CONN("fixed channel required\n"); |
69274f02 | 992 | else |
1ce4784e | 993 | WL_CONN("no fixed channel required\n"); |
69274f02 SS |
994 | |
995 | if (params->ie && params->ie_len) | |
1ce4784e | 996 | WL_CONN("ie len: %d\n", params->ie_len); |
69274f02 | 997 | else |
1ce4784e | 998 | WL_CONN("no ie specified\n"); |
69274f02 SS |
999 | |
1000 | if (params->beacon_interval) | |
1ce4784e | 1001 | WL_CONN("beacon interval: %d\n", params->beacon_interval); |
69274f02 | 1002 | else |
1ce4784e | 1003 | WL_CONN("no beacon interval specified\n"); |
69274f02 SS |
1004 | |
1005 | if (params->basic_rates) | |
1ce4784e | 1006 | WL_CONN("basic rates: %08X\n", params->basic_rates); |
69274f02 | 1007 | else |
1ce4784e | 1008 | WL_CONN("no basic rates specified\n"); |
69274f02 SS |
1009 | |
1010 | if (params->privacy) | |
1ce4784e | 1011 | WL_CONN("privacy required\n"); |
69274f02 | 1012 | else |
1ce4784e | 1013 | WL_CONN("no privacy required\n"); |
69274f02 SS |
1014 | |
1015 | /* Configure Privacy for starter */ | |
1016 | if (params->privacy) | |
1017 | wsec |= WEP_ENABLED; | |
1018 | ||
1019 | err = wl_dev_intvar_set(dev, "wsec", wsec); | |
1020 | if (unlikely(err)) { | |
1021 | WL_ERR("wsec failed (%d)\n", err); | |
1022 | goto done; | |
1023 | } | |
1024 | ||
1025 | /* Configure Beacon Interval for starter */ | |
1026 | if (params->beacon_interval) | |
1027 | bcnprd = cpu_to_le32(params->beacon_interval); | |
1028 | else | |
1029 | bcnprd = cpu_to_le32(100); | |
1030 | ||
1031 | err = wl_dev_ioctl(dev, WLC_SET_BCNPRD, &bcnprd, sizeof(bcnprd)); | |
1032 | if (unlikely(err)) { | |
1033 | WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err); | |
1034 | goto done; | |
cf2b4488 | 1035 | } |
69274f02 SS |
1036 | |
1037 | /* Configure required join parameter */ | |
1038 | memset(&join_params, 0, sizeof(wl_join_params_t)); | |
1039 | ||
1040 | /* SSID */ | |
1041 | join_params.ssid.SSID_len = | |
1042 | (params->ssid_len > 32) ? 32 : params->ssid_len; | |
1043 | memcpy(join_params.ssid.SSID, params->ssid, join_params.ssid.SSID_len); | |
1044 | join_params.ssid.SSID_len = cpu_to_le32(join_params.ssid.SSID_len); | |
1045 | join_params_size = sizeof(join_params.ssid); | |
1046 | wl_update_prof(wl, NULL, &join_params.ssid, WL_PROF_SSID); | |
1047 | ||
1048 | /* BSSID */ | |
1049 | if (params->bssid) { | |
1050 | memcpy(join_params.params.bssid, params->bssid, ETH_ALEN); | |
1051 | join_params_size = | |
1052 | sizeof(join_params.ssid) + WL_ASSOC_PARAMS_FIXED_SIZE; | |
cf2b4488 | 1053 | } else { |
69274f02 | 1054 | memcpy(join_params.params.bssid, ether_bcast, ETH_ALEN); |
cf2b4488 | 1055 | } |
69274f02 SS |
1056 | wl_update_prof(wl, NULL, &join_params.params.bssid, WL_PROF_BSSID); |
1057 | ||
1058 | /* Channel */ | |
1059 | if (params->channel) { | |
1060 | u32 target_channel; | |
1061 | ||
1062 | wl->channel = | |
1063 | ieee80211_frequency_to_channel( | |
1064 | params->channel->center_freq); | |
1065 | if (params->channel_fixed) { | |
1066 | /* adding chanspec */ | |
1067 | wl_ch_to_chanspec(wl->channel, | |
1068 | &join_params, &join_params_size); | |
1069 | } | |
cf2b4488 | 1070 | |
69274f02 SS |
1071 | /* set channel for starter */ |
1072 | target_channel = cpu_to_le32(wl->channel); | |
1073 | err = wl_dev_ioctl(dev, WLC_SET_CHANNEL, | |
1074 | &target_channel, sizeof(target_channel)); | |
1075 | if (unlikely(err)) { | |
1076 | WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err); | |
1077 | goto done; | |
1078 | } | |
1079 | } else | |
1080 | wl->channel = 0; | |
1081 | ||
1082 | wl->ibss_starter = false; | |
1083 | ||
1084 | ||
1085 | err = wl_dev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size); | |
76c06459 | 1086 | if (unlikely(err)) { |
69274f02 SS |
1087 | WL_ERR("WLC_SET_SSID failed (%d)\n", err); |
1088 | goto done; | |
cf2b4488 | 1089 | } |
69274f02 SS |
1090 | |
1091 | set_bit(WL_STATUS_CONNECTING, &wl->status); | |
1092 | ||
1093 | done: | |
1ce4784e | 1094 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
1095 | return err; |
1096 | } | |
1097 | ||
3e26416e | 1098 | static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) |
cf2b4488 HP |
1099 | { |
1100 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
3e26416e | 1101 | s32 err = 0; |
cf2b4488 | 1102 | |
1ce4784e | 1103 | WL_TRACE("Enter\n"); |
cf2b4488 | 1104 | CHECK_SYS_UP(); |
1ce4784e | 1105 | |
cf2b4488 HP |
1106 | wl_link_down(wl); |
1107 | ||
1ce4784e SS |
1108 | WL_TRACE("Exit\n"); |
1109 | ||
cf2b4488 HP |
1110 | return err; |
1111 | } | |
1112 | ||
3e26416e | 1113 | static s32 |
cf2b4488 HP |
1114 | wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme) |
1115 | { | |
1116 | struct wl_priv *wl = ndev_to_wl(dev); | |
1117 | struct wl_security *sec; | |
3e26416e GKH |
1118 | s32 val = 0; |
1119 | s32 err = 0; | |
cf2b4488 HP |
1120 | |
1121 | if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) | |
1122 | val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; | |
1123 | else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) | |
1124 | val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; | |
1125 | else | |
1126 | val = WPA_AUTH_DISABLED; | |
1ce4784e | 1127 | WL_CONN("setting wpa_auth to 0x%0x\n", val); |
76c06459 JC |
1128 | err = wl_dev_intvar_set(dev, "wpa_auth", val); |
1129 | if (unlikely(err)) { | |
f4528696 | 1130 | WL_ERR("set wpa_auth failed (%d)\n", err); |
cf2b4488 HP |
1131 | return err; |
1132 | } | |
1133 | sec = wl_read_prof(wl, WL_PROF_SEC); | |
1134 | sec->wpa_versions = sme->crypto.wpa_versions; | |
1135 | return err; | |
1136 | } | |
1137 | ||
3e26416e | 1138 | static s32 |
cf2b4488 HP |
1139 | wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) |
1140 | { | |
1141 | struct wl_priv *wl = ndev_to_wl(dev); | |
1142 | struct wl_security *sec; | |
3e26416e GKH |
1143 | s32 val = 0; |
1144 | s32 err = 0; | |
cf2b4488 HP |
1145 | |
1146 | switch (sme->auth_type) { | |
1147 | case NL80211_AUTHTYPE_OPEN_SYSTEM: | |
1148 | val = 0; | |
1ce4784e | 1149 | WL_CONN("open system\n"); |
cf2b4488 HP |
1150 | break; |
1151 | case NL80211_AUTHTYPE_SHARED_KEY: | |
1152 | val = 1; | |
1ce4784e | 1153 | WL_CONN("shared key\n"); |
cf2b4488 HP |
1154 | break; |
1155 | case NL80211_AUTHTYPE_AUTOMATIC: | |
1156 | val = 2; | |
1ce4784e | 1157 | WL_CONN("automatic\n"); |
cf2b4488 HP |
1158 | break; |
1159 | case NL80211_AUTHTYPE_NETWORK_EAP: | |
1ce4784e | 1160 | WL_CONN("network eap\n"); |
cf2b4488 HP |
1161 | default: |
1162 | val = 2; | |
f4528696 | 1163 | WL_ERR("invalid auth type (%d)\n", sme->auth_type); |
cf2b4488 HP |
1164 | break; |
1165 | } | |
1166 | ||
76c06459 JC |
1167 | err = wl_dev_intvar_set(dev, "auth", val); |
1168 | if (unlikely(err)) { | |
f4528696 | 1169 | WL_ERR("set auth failed (%d)\n", err); |
cf2b4488 HP |
1170 | return err; |
1171 | } | |
1172 | sec = wl_read_prof(wl, WL_PROF_SEC); | |
1173 | sec->auth_type = sme->auth_type; | |
1174 | return err; | |
1175 | } | |
1176 | ||
3e26416e | 1177 | static s32 |
cf2b4488 HP |
1178 | wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) |
1179 | { | |
1180 | struct wl_priv *wl = ndev_to_wl(dev); | |
1181 | struct wl_security *sec; | |
3e26416e GKH |
1182 | s32 pval = 0; |
1183 | s32 gval = 0; | |
1184 | s32 err = 0; | |
cf2b4488 HP |
1185 | |
1186 | if (sme->crypto.n_ciphers_pairwise) { | |
1187 | switch (sme->crypto.ciphers_pairwise[0]) { | |
1188 | case WLAN_CIPHER_SUITE_WEP40: | |
1189 | case WLAN_CIPHER_SUITE_WEP104: | |
1190 | pval = WEP_ENABLED; | |
1191 | break; | |
1192 | case WLAN_CIPHER_SUITE_TKIP: | |
1193 | pval = TKIP_ENABLED; | |
1194 | break; | |
1195 | case WLAN_CIPHER_SUITE_CCMP: | |
1196 | pval = AES_ENABLED; | |
1197 | break; | |
1198 | case WLAN_CIPHER_SUITE_AES_CMAC: | |
1199 | pval = AES_ENABLED; | |
1200 | break; | |
1201 | default: | |
f4528696 JP |
1202 | WL_ERR("invalid cipher pairwise (%d)\n", |
1203 | sme->crypto.ciphers_pairwise[0]); | |
cf2b4488 HP |
1204 | return -EINVAL; |
1205 | } | |
1206 | } | |
1207 | if (sme->crypto.cipher_group) { | |
1208 | switch (sme->crypto.cipher_group) { | |
1209 | case WLAN_CIPHER_SUITE_WEP40: | |
1210 | case WLAN_CIPHER_SUITE_WEP104: | |
1211 | gval = WEP_ENABLED; | |
1212 | break; | |
1213 | case WLAN_CIPHER_SUITE_TKIP: | |
1214 | gval = TKIP_ENABLED; | |
1215 | break; | |
1216 | case WLAN_CIPHER_SUITE_CCMP: | |
1217 | gval = AES_ENABLED; | |
1218 | break; | |
1219 | case WLAN_CIPHER_SUITE_AES_CMAC: | |
1220 | gval = AES_ENABLED; | |
1221 | break; | |
1222 | default: | |
f4528696 JP |
1223 | WL_ERR("invalid cipher group (%d)\n", |
1224 | sme->crypto.cipher_group); | |
cf2b4488 HP |
1225 | return -EINVAL; |
1226 | } | |
1227 | } | |
1228 | ||
1ce4784e | 1229 | WL_CONN("pval (%d) gval (%d)\n", pval, gval); |
76c06459 JC |
1230 | err = wl_dev_intvar_set(dev, "wsec", pval | gval); |
1231 | if (unlikely(err)) { | |
f4528696 | 1232 | WL_ERR("error (%d)\n", err); |
cf2b4488 HP |
1233 | return err; |
1234 | } | |
1235 | ||
1236 | sec = wl_read_prof(wl, WL_PROF_SEC); | |
1237 | sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; | |
1238 | sec->cipher_group = sme->crypto.cipher_group; | |
1239 | ||
1240 | return err; | |
1241 | } | |
1242 | ||
3e26416e | 1243 | static s32 |
cf2b4488 HP |
1244 | wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) |
1245 | { | |
1246 | struct wl_priv *wl = ndev_to_wl(dev); | |
1247 | struct wl_security *sec; | |
3e26416e GKH |
1248 | s32 val = 0; |
1249 | s32 err = 0; | |
cf2b4488 HP |
1250 | |
1251 | if (sme->crypto.n_akm_suites) { | |
76c06459 JC |
1252 | err = wl_dev_intvar_get(dev, "wpa_auth", &val); |
1253 | if (unlikely(err)) { | |
f4528696 | 1254 | WL_ERR("could not get wpa_auth (%d)\n", err); |
cf2b4488 HP |
1255 | return err; |
1256 | } | |
1257 | if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { | |
1258 | switch (sme->crypto.akm_suites[0]) { | |
1259 | case WLAN_AKM_SUITE_8021X: | |
1260 | val = WPA_AUTH_UNSPECIFIED; | |
1261 | break; | |
1262 | case WLAN_AKM_SUITE_PSK: | |
1263 | val = WPA_AUTH_PSK; | |
1264 | break; | |
1265 | default: | |
f4528696 JP |
1266 | WL_ERR("invalid cipher group (%d)\n", |
1267 | sme->crypto.cipher_group); | |
cf2b4488 HP |
1268 | return -EINVAL; |
1269 | } | |
1270 | } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { | |
1271 | switch (sme->crypto.akm_suites[0]) { | |
1272 | case WLAN_AKM_SUITE_8021X: | |
1273 | val = WPA2_AUTH_UNSPECIFIED; | |
1274 | break; | |
1275 | case WLAN_AKM_SUITE_PSK: | |
1276 | val = WPA2_AUTH_PSK; | |
1277 | break; | |
1278 | default: | |
f4528696 JP |
1279 | WL_ERR("invalid cipher group (%d)\n", |
1280 | sme->crypto.cipher_group); | |
cf2b4488 HP |
1281 | return -EINVAL; |
1282 | } | |
1283 | } | |
1284 | ||
1ce4784e | 1285 | WL_CONN("setting wpa_auth to %d\n", val); |
76c06459 JC |
1286 | err = wl_dev_intvar_set(dev, "wpa_auth", val); |
1287 | if (unlikely(err)) { | |
f4528696 | 1288 | WL_ERR("could not set wpa_auth (%d)\n", err); |
cf2b4488 HP |
1289 | return err; |
1290 | } | |
1291 | } | |
1292 | sec = wl_read_prof(wl, WL_PROF_SEC); | |
1293 | sec->wpa_auth = sme->crypto.akm_suites[0]; | |
1294 | ||
1295 | return err; | |
1296 | } | |
1297 | ||
3e26416e | 1298 | static s32 |
cf2b4488 HP |
1299 | wl_set_set_sharedkey(struct net_device *dev, |
1300 | struct cfg80211_connect_params *sme) | |
1301 | { | |
1302 | struct wl_priv *wl = ndev_to_wl(dev); | |
1303 | struct wl_security *sec; | |
1304 | struct wl_wsec_key key; | |
3e26416e GKH |
1305 | s32 val; |
1306 | s32 err = 0; | |
cf2b4488 | 1307 | |
1ce4784e | 1308 | WL_CONN("key len (%d)\n", sme->key_len); |
cf2b4488 HP |
1309 | if (sme->key_len) { |
1310 | sec = wl_read_prof(wl, WL_PROF_SEC); | |
1ce4784e | 1311 | WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n", |
f4528696 | 1312 | sec->wpa_versions, sec->cipher_pairwise); |
cf2b4488 HP |
1313 | if (! |
1314 | (sec->wpa_versions & (NL80211_WPA_VERSION_1 | | |
1315 | NL80211_WPA_VERSION_2)) | |
1316 | && (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | | |
1317 | WLAN_CIPHER_SUITE_WEP104))) { | |
1318 | memset(&key, 0, sizeof(key)); | |
66cbd3ab GKH |
1319 | key.len = (u32) sme->key_len; |
1320 | key.index = (u32) sme->key_idx; | |
cf2b4488 | 1321 | if (unlikely(key.len > sizeof(key.data))) { |
f4528696 | 1322 | WL_ERR("Too long key length (%u)\n", key.len); |
cf2b4488 HP |
1323 | return -EINVAL; |
1324 | } | |
1325 | memcpy(key.data, sme->key, key.len); | |
1326 | key.flags = WL_PRIMARY_KEY; | |
1327 | switch (sec->cipher_pairwise) { | |
1328 | case WLAN_CIPHER_SUITE_WEP40: | |
1329 | key.algo = CRYPTO_ALGO_WEP1; | |
1330 | break; | |
1331 | case WLAN_CIPHER_SUITE_WEP104: | |
1332 | key.algo = CRYPTO_ALGO_WEP128; | |
1333 | break; | |
1334 | default: | |
f4528696 JP |
1335 | WL_ERR("Invalid algorithm (%d)\n", |
1336 | sme->crypto.ciphers_pairwise[0]); | |
cf2b4488 HP |
1337 | return -EINVAL; |
1338 | } | |
1339 | /* Set the new key/index */ | |
1ce4784e | 1340 | WL_CONN("key length (%d) key index (%d) algo (%d)\n", |
f4528696 | 1341 | key.len, key.index, key.algo); |
1ce4784e | 1342 | WL_CONN("key \"%s\"\n", key.data); |
cf2b4488 | 1343 | swap_key_from_BE(&key); |
76c06459 JC |
1344 | err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, |
1345 | sizeof(key)); | |
1346 | if (unlikely(err)) { | |
f4528696 | 1347 | WL_ERR("WLC_SET_KEY error (%d)\n", err); |
cf2b4488 HP |
1348 | return err; |
1349 | } | |
1350 | if (sec->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) { | |
1ce4784e | 1351 | WL_CONN("set auth_type to shared key\n"); |
cf2b4488 | 1352 | val = 1; /* shared key */ |
76c06459 JC |
1353 | err = wl_dev_intvar_set(dev, "auth", val); |
1354 | if (unlikely(err)) { | |
f4528696 | 1355 | WL_ERR("set auth failed (%d)\n", err); |
cf2b4488 HP |
1356 | return err; |
1357 | } | |
1358 | } | |
1359 | } | |
1360 | } | |
1361 | return err; | |
1362 | } | |
1363 | ||
3e26416e | 1364 | static s32 |
cf2b4488 HP |
1365 | wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, |
1366 | struct cfg80211_connect_params *sme) | |
1367 | { | |
1368 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
1369 | struct ieee80211_channel *chan = sme->channel; | |
fb693a71 | 1370 | struct wl_join_params join_params; |
1371 | size_t join_params_size; | |
1372 | ||
3e26416e | 1373 | s32 err = 0; |
cf2b4488 | 1374 | |
1ce4784e | 1375 | WL_TRACE("Enter\n"); |
cf2b4488 | 1376 | CHECK_SYS_UP(); |
1ce4784e | 1377 | |
cf2b4488 | 1378 | if (unlikely(!sme->ssid)) { |
f4528696 | 1379 | WL_ERR("Invalid ssid\n"); |
cf2b4488 HP |
1380 | return -EOPNOTSUPP; |
1381 | } | |
1ce4784e | 1382 | |
cf2b4488 | 1383 | if (chan) { |
e494632e SS |
1384 | wl->channel = |
1385 | ieee80211_frequency_to_channel(chan->center_freq); | |
1ce4784e | 1386 | WL_CONN("channel (%d), center_req (%d)\n", |
e494632e SS |
1387 | wl->channel, chan->center_freq); |
1388 | } else | |
1389 | wl->channel = 0; | |
1ce4784e SS |
1390 | |
1391 | WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len); | |
1392 | ||
76c06459 JC |
1393 | err = wl_set_wpa_version(dev, sme); |
1394 | if (unlikely(err)) | |
cf2b4488 HP |
1395 | return err; |
1396 | ||
76c06459 JC |
1397 | err = wl_set_auth_type(dev, sme); |
1398 | if (unlikely(err)) | |
cf2b4488 HP |
1399 | return err; |
1400 | ||
76c06459 JC |
1401 | err = wl_set_set_cipher(dev, sme); |
1402 | if (unlikely(err)) | |
cf2b4488 HP |
1403 | return err; |
1404 | ||
76c06459 JC |
1405 | err = wl_set_key_mgmt(dev, sme); |
1406 | if (unlikely(err)) | |
cf2b4488 HP |
1407 | return err; |
1408 | ||
76c06459 JC |
1409 | err = wl_set_set_sharedkey(dev, sme); |
1410 | if (unlikely(err)) | |
cf2b4488 HP |
1411 | return err; |
1412 | ||
1413 | wl_update_prof(wl, NULL, sme->bssid, WL_PROF_BSSID); | |
1414 | /* | |
1415 | ** Join with specific BSSID and cached SSID | |
1416 | ** If SSID is zero join based on BSSID only | |
1417 | */ | |
fb693a71 | 1418 | memset(&join_params, 0, sizeof(join_params)); |
1419 | join_params_size = sizeof(join_params.ssid); | |
1420 | ||
1421 | join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), sme->ssid_len); | |
1422 | memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len); | |
29750b90 | 1423 | join_params.ssid.SSID_len = cpu_to_le32(join_params.ssid.SSID_len); |
fb693a71 | 1424 | wl_update_prof(wl, NULL, &join_params.ssid, WL_PROF_SSID); |
fb693a71 | 1425 | |
e494632e SS |
1426 | if (sme->bssid) |
1427 | memcpy(join_params.params.bssid, sme->bssid, ETH_ALEN); | |
1428 | else | |
1429 | memcpy(join_params.params.bssid, ether_bcast, ETH_ALEN); | |
fb693a71 | 1430 | |
1431 | if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { | |
1ce4784e | 1432 | WL_CONN("ssid \"%s\", len (%d)\n", |
f4528696 | 1433 | join_params.ssid.SSID, join_params.ssid.SSID_len); |
fb693a71 | 1434 | } |
e494632e SS |
1435 | |
1436 | wl_ch_to_chanspec(wl->channel, &join_params, &join_params_size); | |
fb693a71 | 1437 | err = wl_dev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size); |
76c06459 | 1438 | if (unlikely(err)) { |
f4528696 | 1439 | WL_ERR("error (%d)\n", err); |
cf2b4488 HP |
1440 | return err; |
1441 | } | |
1442 | set_bit(WL_STATUS_CONNECTING, &wl->status); | |
1443 | ||
1ce4784e | 1444 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
1445 | return err; |
1446 | } | |
1447 | ||
3e26416e | 1448 | static s32 |
cf2b4488 | 1449 | wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, |
7d4df48e | 1450 | u16 reason_code) |
cf2b4488 HP |
1451 | { |
1452 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
1453 | scb_val_t scbval; | |
3e26416e | 1454 | s32 err = 0; |
cf2b4488 | 1455 | |
1ce4784e | 1456 | WL_TRACE("Enter. Reason code = %d\n", reason_code); |
cf2b4488 | 1457 | CHECK_SYS_UP(); |
e494632e SS |
1458 | |
1459 | clear_bit(WL_STATUS_CONNECTED, &wl->status); | |
1460 | ||
1461 | scbval.val = reason_code; | |
1462 | memcpy(&scbval.ea, wl_read_prof(wl, WL_PROF_BSSID), ETH_ALEN); | |
1463 | scbval.val = cpu_to_le32(scbval.val); | |
1464 | err = wl_dev_ioctl(dev, WLC_DISASSOC, &scbval, | |
1465 | sizeof(scb_val_t)); | |
1466 | if (unlikely(err)) | |
1467 | WL_ERR("error (%d)\n", err); | |
1468 | ||
1469 | wl->link_up = false; | |
cf2b4488 | 1470 | |
1ce4784e | 1471 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
1472 | return err; |
1473 | } | |
1474 | ||
3e26416e | 1475 | static s32 |
cf2b4488 | 1476 | wl_cfg80211_set_tx_power(struct wiphy *wiphy, |
3e26416e | 1477 | enum nl80211_tx_power_setting type, s32 dbm) |
cf2b4488 HP |
1478 | { |
1479 | ||
1480 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
1481 | struct net_device *ndev = wl_to_ndev(wl); | |
7d4df48e | 1482 | u16 txpwrmw; |
3e26416e GKH |
1483 | s32 err = 0; |
1484 | s32 disable = 0; | |
cf2b4488 | 1485 | |
1ce4784e | 1486 | WL_TRACE("Enter\n"); |
cf2b4488 | 1487 | CHECK_SYS_UP(); |
1ce4784e | 1488 | |
cf2b4488 HP |
1489 | switch (type) { |
1490 | case NL80211_TX_POWER_AUTOMATIC: | |
1491 | break; | |
1492 | case NL80211_TX_POWER_LIMITED: | |
1493 | if (dbm < 0) { | |
f4528696 | 1494 | WL_ERR("TX_POWER_LIMITED - dbm is negative\n"); |
1ce4784e SS |
1495 | err = -EINVAL; |
1496 | goto done; | |
cf2b4488 HP |
1497 | } |
1498 | break; | |
1499 | case NL80211_TX_POWER_FIXED: | |
1500 | if (dbm < 0) { | |
f4528696 | 1501 | WL_ERR("TX_POWER_FIXED - dbm is negative\n"); |
1ce4784e SS |
1502 | err = -EINVAL; |
1503 | goto done; | |
cf2b4488 HP |
1504 | } |
1505 | break; | |
1506 | } | |
1507 | /* Make sure radio is off or on as far as software is concerned */ | |
1508 | disable = WL_RADIO_SW_DISABLE << 16; | |
29750b90 | 1509 | disable = cpu_to_le32(disable); |
76c06459 | 1510 | err = wl_dev_ioctl(ndev, WLC_SET_RADIO, &disable, sizeof(disable)); |
1ce4784e | 1511 | if (unlikely(err)) |
f4528696 | 1512 | WL_ERR("WLC_SET_RADIO error (%d)\n", err); |
cf2b4488 HP |
1513 | |
1514 | if (dbm > 0xffff) | |
1515 | txpwrmw = 0xffff; | |
1516 | else | |
7d4df48e | 1517 | txpwrmw = (u16) dbm; |
76c06459 | 1518 | err = wl_dev_intvar_set(ndev, "qtxpower", |
3e26416e | 1519 | (s32) (bcm_mw_to_qdbm(txpwrmw))); |
1ce4784e | 1520 | if (unlikely(err)) |
f4528696 | 1521 | WL_ERR("qtxpower error (%d)\n", err); |
cf2b4488 HP |
1522 | wl->conf->tx_power = dbm; |
1523 | ||
1ce4784e SS |
1524 | done: |
1525 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
1526 | return err; |
1527 | } | |
1528 | ||
3e26416e | 1529 | static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) |
cf2b4488 HP |
1530 | { |
1531 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
1532 | struct net_device *ndev = wl_to_ndev(wl); | |
3e26416e | 1533 | s32 txpwrdbm; |
3fd79f7c | 1534 | u8 result; |
3e26416e | 1535 | s32 err = 0; |
cf2b4488 | 1536 | |
1ce4784e | 1537 | WL_TRACE("Enter\n"); |
cf2b4488 | 1538 | CHECK_SYS_UP(); |
1ce4784e | 1539 | |
76c06459 JC |
1540 | err = wl_dev_intvar_get(ndev, "qtxpower", &txpwrdbm); |
1541 | if (unlikely(err)) { | |
f4528696 | 1542 | WL_ERR("error (%d)\n", err); |
1ce4784e | 1543 | goto done; |
cf2b4488 | 1544 | } |
1ce4784e | 1545 | |
3fd79f7c | 1546 | result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); |
3e26416e | 1547 | *dbm = (s32) bcm_qdbm_to_mw(result); |
cf2b4488 | 1548 | |
1ce4784e SS |
1549 | done: |
1550 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
1551 | return err; |
1552 | } | |
1553 | ||
3e26416e | 1554 | static s32 |
cf2b4488 | 1555 | wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, |
8a6257fb | 1556 | u8 key_idx, bool unicast, bool multicast) |
cf2b4488 | 1557 | { |
66cbd3ab | 1558 | u32 index; |
3e26416e GKH |
1559 | s32 wsec; |
1560 | s32 err = 0; | |
cf2b4488 | 1561 | |
1ce4784e SS |
1562 | WL_TRACE("Enter\n"); |
1563 | WL_CONN("key index (%d)\n", key_idx); | |
cf2b4488 HP |
1564 | CHECK_SYS_UP(); |
1565 | ||
76c06459 JC |
1566 | err = wl_dev_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec)); |
1567 | if (unlikely(err)) { | |
f4528696 | 1568 | WL_ERR("WLC_GET_WSEC error (%d)\n", err); |
1ce4784e | 1569 | goto done; |
cf2b4488 | 1570 | } |
1ce4784e | 1571 | |
29750b90 | 1572 | wsec = le32_to_cpu(wsec); |
cf2b4488 HP |
1573 | if (wsec & WEP_ENABLED) { |
1574 | /* Just select a new current key */ | |
66cbd3ab | 1575 | index = (u32) key_idx; |
29750b90 | 1576 | index = cpu_to_le32(index); |
76c06459 JC |
1577 | err = wl_dev_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, |
1578 | sizeof(index)); | |
1ce4784e | 1579 | if (unlikely(err)) |
f4528696 | 1580 | WL_ERR("error (%d)\n", err); |
cf2b4488 | 1581 | } |
1ce4784e SS |
1582 | done: |
1583 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
1584 | return err; |
1585 | } | |
1586 | ||
3e26416e | 1587 | static s32 |
cf2b4488 | 1588 | wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, |
3fd79f7c | 1589 | u8 key_idx, const u8 *mac_addr, struct key_params *params) |
cf2b4488 HP |
1590 | { |
1591 | struct wl_wsec_key key; | |
3e26416e | 1592 | s32 err = 0; |
cf2b4488 HP |
1593 | |
1594 | memset(&key, 0, sizeof(key)); | |
66cbd3ab | 1595 | key.index = (u32) key_idx; |
cf2b4488 HP |
1596 | /* Instead of bcast for ea address for default wep keys, |
1597 | driver needs it to be Null */ | |
3ca5ada5 | 1598 | if (!is_multicast_ether_addr(mac_addr)) |
b8d63078 | 1599 | memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN); |
66cbd3ab | 1600 | key.len = (u32) params->key_len; |
cf2b4488 HP |
1601 | /* check for key index change */ |
1602 | if (key.len == 0) { | |
1603 | /* key delete */ | |
1604 | swap_key_from_BE(&key); | |
76c06459 JC |
1605 | err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); |
1606 | if (unlikely(err)) { | |
f4528696 | 1607 | WL_ERR("key delete error (%d)\n", err); |
cf2b4488 HP |
1608 | return err; |
1609 | } | |
1610 | } else { | |
1611 | if (key.len > sizeof(key.data)) { | |
f4528696 | 1612 | WL_ERR("Invalid key length (%d)\n", key.len); |
cf2b4488 HP |
1613 | return -EINVAL; |
1614 | } | |
1615 | ||
1ce4784e | 1616 | WL_CONN("Setting the key index %d\n", key.index); |
cf2b4488 HP |
1617 | memcpy(key.data, params->key, key.len); |
1618 | ||
1619 | if (params->cipher == WLAN_CIPHER_SUITE_TKIP) { | |
3fd79f7c | 1620 | u8 keybuf[8]; |
cf2b4488 HP |
1621 | memcpy(keybuf, &key.data[24], sizeof(keybuf)); |
1622 | memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); | |
1623 | memcpy(&key.data[16], keybuf, sizeof(keybuf)); | |
1624 | } | |
1625 | ||
1626 | /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ | |
1627 | if (params->seq && params->seq_len == 6) { | |
1628 | /* rx iv */ | |
3fd79f7c GKH |
1629 | u8 *ivptr; |
1630 | ivptr = (u8 *) params->seq; | |
cf2b4488 HP |
1631 | key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | |
1632 | (ivptr[3] << 8) | ivptr[2]; | |
1633 | key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; | |
0f0881b0 | 1634 | key.iv_initialized = true; |
cf2b4488 HP |
1635 | } |
1636 | ||
1637 | switch (params->cipher) { | |
1638 | case WLAN_CIPHER_SUITE_WEP40: | |
1639 | key.algo = CRYPTO_ALGO_WEP1; | |
1ce4784e | 1640 | WL_CONN("WLAN_CIPHER_SUITE_WEP40\n"); |
cf2b4488 HP |
1641 | break; |
1642 | case WLAN_CIPHER_SUITE_WEP104: | |
1643 | key.algo = CRYPTO_ALGO_WEP128; | |
1ce4784e | 1644 | WL_CONN("WLAN_CIPHER_SUITE_WEP104\n"); |
cf2b4488 HP |
1645 | break; |
1646 | case WLAN_CIPHER_SUITE_TKIP: | |
1647 | key.algo = CRYPTO_ALGO_TKIP; | |
1ce4784e | 1648 | WL_CONN("WLAN_CIPHER_SUITE_TKIP\n"); |
cf2b4488 HP |
1649 | break; |
1650 | case WLAN_CIPHER_SUITE_AES_CMAC: | |
1651 | key.algo = CRYPTO_ALGO_AES_CCM; | |
1ce4784e | 1652 | WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n"); |
cf2b4488 HP |
1653 | break; |
1654 | case WLAN_CIPHER_SUITE_CCMP: | |
1655 | key.algo = CRYPTO_ALGO_AES_CCM; | |
1ce4784e | 1656 | WL_CONN("WLAN_CIPHER_SUITE_CCMP\n"); |
cf2b4488 HP |
1657 | break; |
1658 | default: | |
f4528696 | 1659 | WL_ERR("Invalid cipher (0x%x)\n", params->cipher); |
cf2b4488 HP |
1660 | return -EINVAL; |
1661 | } | |
1662 | swap_key_from_BE(&key); | |
1663 | ||
1664 | dhd_wait_pend8021x(dev); | |
76c06459 JC |
1665 | err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); |
1666 | if (unlikely(err)) { | |
f4528696 | 1667 | WL_ERR("WLC_SET_KEY error (%d)\n", err); |
cf2b4488 HP |
1668 | return err; |
1669 | } | |
1670 | } | |
1671 | return err; | |
1672 | } | |
1673 | ||
3e26416e | 1674 | static s32 |
cf2b4488 | 1675 | wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, |
34a488c1 | 1676 | u8 key_idx, bool pairwise, const u8 *mac_addr, |
cf2b4488 HP |
1677 | struct key_params *params) |
1678 | { | |
1679 | struct wl_wsec_key key; | |
3e26416e GKH |
1680 | s32 val; |
1681 | s32 wsec; | |
1682 | s32 err = 0; | |
22d5d59b | 1683 | u8 keybuf[8]; |
cf2b4488 | 1684 | |
1ce4784e SS |
1685 | WL_TRACE("Enter\n"); |
1686 | WL_CONN("key index (%d)\n", key_idx); | |
cf2b4488 HP |
1687 | CHECK_SYS_UP(); |
1688 | ||
1ce4784e SS |
1689 | if (mac_addr) { |
1690 | WL_TRACE("Exit"); | |
cf2b4488 | 1691 | return wl_add_keyext(wiphy, dev, key_idx, mac_addr, params); |
1ce4784e | 1692 | } |
cf2b4488 HP |
1693 | memset(&key, 0, sizeof(key)); |
1694 | ||
66cbd3ab GKH |
1695 | key.len = (u32) params->key_len; |
1696 | key.index = (u32) key_idx; | |
cf2b4488 HP |
1697 | |
1698 | if (unlikely(key.len > sizeof(key.data))) { | |
f4528696 | 1699 | WL_ERR("Too long key length (%u)\n", key.len); |
1ce4784e SS |
1700 | err = -EINVAL; |
1701 | goto done; | |
cf2b4488 HP |
1702 | } |
1703 | memcpy(key.data, params->key, key.len); | |
1704 | ||
1705 | key.flags = WL_PRIMARY_KEY; | |
1706 | switch (params->cipher) { | |
1707 | case WLAN_CIPHER_SUITE_WEP40: | |
1708 | key.algo = CRYPTO_ALGO_WEP1; | |
1ce4784e | 1709 | WL_CONN("WLAN_CIPHER_SUITE_WEP40\n"); |
cf2b4488 HP |
1710 | break; |
1711 | case WLAN_CIPHER_SUITE_WEP104: | |
1712 | key.algo = CRYPTO_ALGO_WEP128; | |
1ce4784e | 1713 | WL_CONN("WLAN_CIPHER_SUITE_WEP104\n"); |
cf2b4488 HP |
1714 | break; |
1715 | case WLAN_CIPHER_SUITE_TKIP: | |
22d5d59b SS |
1716 | memcpy(keybuf, &key.data[24], sizeof(keybuf)); |
1717 | memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); | |
1718 | memcpy(&key.data[16], keybuf, sizeof(keybuf)); | |
cf2b4488 | 1719 | key.algo = CRYPTO_ALGO_TKIP; |
1ce4784e | 1720 | WL_CONN("WLAN_CIPHER_SUITE_TKIP\n"); |
cf2b4488 HP |
1721 | break; |
1722 | case WLAN_CIPHER_SUITE_AES_CMAC: | |
1723 | key.algo = CRYPTO_ALGO_AES_CCM; | |
1ce4784e | 1724 | WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n"); |
cf2b4488 HP |
1725 | break; |
1726 | case WLAN_CIPHER_SUITE_CCMP: | |
1727 | key.algo = CRYPTO_ALGO_AES_CCM; | |
1ce4784e | 1728 | WL_CONN("WLAN_CIPHER_SUITE_CCMP\n"); |
cf2b4488 HP |
1729 | break; |
1730 | default: | |
f4528696 | 1731 | WL_ERR("Invalid cipher (0x%x)\n", params->cipher); |
1ce4784e SS |
1732 | err = -EINVAL; |
1733 | goto done; | |
cf2b4488 HP |
1734 | } |
1735 | ||
1736 | /* Set the new key/index */ | |
1737 | swap_key_from_BE(&key); | |
76c06459 JC |
1738 | err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); |
1739 | if (unlikely(err)) { | |
f4528696 | 1740 | WL_ERR("WLC_SET_KEY error (%d)\n", err); |
1ce4784e | 1741 | goto done; |
cf2b4488 HP |
1742 | } |
1743 | ||
1744 | val = WEP_ENABLED; | |
76c06459 JC |
1745 | err = wl_dev_intvar_get(dev, "wsec", &wsec); |
1746 | if (unlikely(err)) { | |
f4528696 | 1747 | WL_ERR("get wsec error (%d)\n", err); |
1ce4784e | 1748 | goto done; |
cf2b4488 HP |
1749 | } |
1750 | wsec &= ~(WEP_ENABLED); | |
1751 | wsec |= val; | |
76c06459 JC |
1752 | err = wl_dev_intvar_set(dev, "wsec", wsec); |
1753 | if (unlikely(err)) { | |
f4528696 | 1754 | WL_ERR("set wsec error (%d)\n", err); |
1ce4784e | 1755 | goto done; |
cf2b4488 HP |
1756 | } |
1757 | ||
1758 | val = 1; /* assume shared key. otherwise 0 */ | |
29750b90 | 1759 | val = cpu_to_le32(val); |
76c06459 | 1760 | err = wl_dev_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val)); |
1ce4784e | 1761 | if (unlikely(err)) |
f4528696 | 1762 | WL_ERR("WLC_SET_AUTH error (%d)\n", err); |
1ce4784e SS |
1763 | done: |
1764 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
1765 | return err; |
1766 | } | |
1767 | ||
3e26416e | 1768 | static s32 |
cf2b4488 | 1769 | wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, |
34a488c1 | 1770 | u8 key_idx, bool pairwise, const u8 *mac_addr) |
cf2b4488 HP |
1771 | { |
1772 | struct wl_wsec_key key; | |
3e26416e GKH |
1773 | s32 err = 0; |
1774 | s32 val; | |
1775 | s32 wsec; | |
cf2b4488 | 1776 | |
1ce4784e | 1777 | WL_TRACE("Enter\n"); |
cf2b4488 HP |
1778 | CHECK_SYS_UP(); |
1779 | memset(&key, 0, sizeof(key)); | |
1780 | ||
66cbd3ab | 1781 | key.index = (u32) key_idx; |
cf2b4488 HP |
1782 | key.flags = WL_PRIMARY_KEY; |
1783 | key.algo = CRYPTO_ALGO_OFF; | |
1784 | ||
1ce4784e | 1785 | WL_CONN("key index (%d)\n", key_idx); |
cf2b4488 HP |
1786 | /* Set the new key/index */ |
1787 | swap_key_from_BE(&key); | |
76c06459 JC |
1788 | err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); |
1789 | if (unlikely(err)) { | |
cf2b4488 | 1790 | if (err == -EINVAL) { |
1ce4784e | 1791 | if (key.index >= DOT11_MAX_DEFAULT_KEYS) |
cf2b4488 | 1792 | /* we ignore this key index in this case */ |
1ce4784e SS |
1793 | WL_ERR("invalid key index (%d)\n", key_idx); |
1794 | } else | |
f4528696 | 1795 | WL_ERR("WLC_SET_KEY error (%d)\n", err); |
1ce4784e SS |
1796 | |
1797 | /* Ignore this error, may happen during DISASSOC */ | |
1798 | err = -EAGAIN; | |
1799 | goto done; | |
cf2b4488 HP |
1800 | } |
1801 | ||
1802 | val = 0; | |
76c06459 JC |
1803 | err = wl_dev_intvar_get(dev, "wsec", &wsec); |
1804 | if (unlikely(err)) { | |
f4528696 | 1805 | WL_ERR("get wsec error (%d)\n", err); |
1ce4784e SS |
1806 | /* Ignore this error, may happen during DISASSOC */ |
1807 | err = -EAGAIN; | |
1808 | goto done; | |
cf2b4488 HP |
1809 | } |
1810 | wsec &= ~(WEP_ENABLED); | |
1811 | wsec |= val; | |
76c06459 JC |
1812 | err = wl_dev_intvar_set(dev, "wsec", wsec); |
1813 | if (unlikely(err)) { | |
f4528696 | 1814 | WL_ERR("set wsec error (%d)\n", err); |
1ce4784e SS |
1815 | /* Ignore this error, may happen during DISASSOC */ |
1816 | err = -EAGAIN; | |
1817 | goto done; | |
cf2b4488 HP |
1818 | } |
1819 | ||
1820 | val = 0; /* assume open key. otherwise 1 */ | |
29750b90 | 1821 | val = cpu_to_le32(val); |
76c06459 JC |
1822 | err = wl_dev_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val)); |
1823 | if (unlikely(err)) { | |
f4528696 | 1824 | WL_ERR("WLC_SET_AUTH error (%d)\n", err); |
1ce4784e SS |
1825 | /* Ignore this error, may happen during DISASSOC */ |
1826 | err = -EAGAIN; | |
cf2b4488 | 1827 | } |
1ce4784e SS |
1828 | done: |
1829 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
1830 | return err; |
1831 | } | |
1832 | ||
3e26416e | 1833 | static s32 |
cf2b4488 | 1834 | wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, |
34a488c1 | 1835 | u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, |
cf2b4488 HP |
1836 | void (*callback) (void *cookie, struct key_params * params)) |
1837 | { | |
1838 | struct key_params params; | |
1839 | struct wl_wsec_key key; | |
1840 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
1841 | struct wl_security *sec; | |
3e26416e GKH |
1842 | s32 wsec; |
1843 | s32 err = 0; | |
cf2b4488 | 1844 | |
1ce4784e SS |
1845 | WL_TRACE("Enter\n"); |
1846 | WL_CONN("key index (%d)\n", key_idx); | |
cf2b4488 HP |
1847 | CHECK_SYS_UP(); |
1848 | ||
1849 | memset(&key, 0, sizeof(key)); | |
1850 | key.index = key_idx; | |
1851 | swap_key_to_BE(&key); | |
1852 | memset(¶ms, 0, sizeof(params)); | |
f4728c38 | 1853 | params.key_len = (u8) min_t(u8, WLAN_MAX_KEY_LEN, key.len); |
cf2b4488 HP |
1854 | memcpy(params.key, key.data, params.key_len); |
1855 | ||
76c06459 JC |
1856 | err = wl_dev_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec)); |
1857 | if (unlikely(err)) { | |
f4528696 | 1858 | WL_ERR("WLC_GET_WSEC error (%d)\n", err); |
1ce4784e SS |
1859 | /* Ignore this error, may happen during DISASSOC */ |
1860 | err = -EAGAIN; | |
1861 | goto done; | |
cf2b4488 | 1862 | } |
29750b90 | 1863 | wsec = le32_to_cpu(wsec); |
cf2b4488 HP |
1864 | switch (wsec) { |
1865 | case WEP_ENABLED: | |
1866 | sec = wl_read_prof(wl, WL_PROF_SEC); | |
1867 | if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { | |
1868 | params.cipher = WLAN_CIPHER_SUITE_WEP40; | |
1ce4784e | 1869 | WL_CONN("WLAN_CIPHER_SUITE_WEP40\n"); |
cf2b4488 HP |
1870 | } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { |
1871 | params.cipher = WLAN_CIPHER_SUITE_WEP104; | |
1ce4784e | 1872 | WL_CONN("WLAN_CIPHER_SUITE_WEP104\n"); |
cf2b4488 HP |
1873 | } |
1874 | break; | |
1875 | case TKIP_ENABLED: | |
1876 | params.cipher = WLAN_CIPHER_SUITE_TKIP; | |
1ce4784e | 1877 | WL_CONN("WLAN_CIPHER_SUITE_TKIP\n"); |
cf2b4488 HP |
1878 | break; |
1879 | case AES_ENABLED: | |
1880 | params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; | |
1ce4784e | 1881 | WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n"); |
cf2b4488 HP |
1882 | break; |
1883 | default: | |
f4528696 | 1884 | WL_ERR("Invalid algo (0x%x)\n", wsec); |
1ce4784e SS |
1885 | err = -EINVAL; |
1886 | goto done; | |
cf2b4488 | 1887 | } |
cf2b4488 | 1888 | callback(cookie, ¶ms); |
1ce4784e SS |
1889 | |
1890 | done: | |
1891 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
1892 | return err; |
1893 | } | |
1894 | ||
3e26416e | 1895 | static s32 |
cf2b4488 | 1896 | wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, |
3fd79f7c | 1897 | struct net_device *dev, u8 key_idx) |
cf2b4488 | 1898 | { |
f4528696 | 1899 | WL_INFO("Not supported\n"); |
1ce4784e | 1900 | |
cf2b4488 HP |
1901 | CHECK_SYS_UP(); |
1902 | return -EOPNOTSUPP; | |
1903 | } | |
1904 | ||
3e26416e | 1905 | static s32 |
cf2b4488 | 1906 | wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, |
3fd79f7c | 1907 | u8 *mac, struct station_info *sinfo) |
cf2b4488 HP |
1908 | { |
1909 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
1910 | scb_val_t scb_val; | |
1911 | int rssi; | |
3e26416e GKH |
1912 | s32 rate; |
1913 | s32 err = 0; | |
1ce4784e | 1914 | u8 *bssid = wl_read_prof(wl, WL_PROF_BSSID); |
cf2b4488 | 1915 | |
1ce4784e | 1916 | WL_TRACE("Enter\n"); |
cf2b4488 | 1917 | CHECK_SYS_UP(); |
1ce4784e | 1918 | |
cf2b4488 | 1919 | if (unlikely |
1ce4784e SS |
1920 | (memcmp(mac, bssid, ETH_ALEN))) { |
1921 | WL_ERR("Wrong Mac address cfg_mac-%X:%X:%X:%X:%X:%X" | |
1922 | "wl_bssid-%X:%X:%X:%X:%X:%X\n", | |
1923 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], | |
1924 | bssid[0], bssid[1], bssid[2], bssid[3], | |
1925 | bssid[4], bssid[5]); | |
1926 | err = -ENOENT; | |
1927 | goto done; | |
cf2b4488 HP |
1928 | } |
1929 | ||
1930 | /* Report the current tx rate */ | |
76c06459 JC |
1931 | err = wl_dev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate)); |
1932 | if (err) { | |
f4528696 | 1933 | WL_ERR("Could not get rate (%d)\n", err); |
cf2b4488 | 1934 | } else { |
29750b90 | 1935 | rate = le32_to_cpu(rate); |
cf2b4488 HP |
1936 | sinfo->filled |= STATION_INFO_TX_BITRATE; |
1937 | sinfo->txrate.legacy = rate * 5; | |
1ce4784e | 1938 | WL_CONN("Rate %d Mbps\n", rate / 2); |
cf2b4488 HP |
1939 | } |
1940 | ||
1941 | if (test_bit(WL_STATUS_CONNECTED, &wl->status)) { | |
1942 | scb_val.val = 0; | |
76c06459 JC |
1943 | err = wl_dev_ioctl(dev, WLC_GET_RSSI, &scb_val, |
1944 | sizeof(scb_val_t)); | |
1945 | if (unlikely(err)) { | |
f4528696 | 1946 | WL_ERR("Could not get rssi (%d)\n", err); |
cf2b4488 | 1947 | } |
29750b90 | 1948 | rssi = le32_to_cpu(scb_val.val); |
cf2b4488 HP |
1949 | sinfo->filled |= STATION_INFO_SIGNAL; |
1950 | sinfo->signal = rssi; | |
1ce4784e | 1951 | WL_CONN("RSSI %d dBm\n", rssi); |
cf2b4488 HP |
1952 | } |
1953 | ||
1ce4784e SS |
1954 | done: |
1955 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
1956 | return err; |
1957 | } | |
1958 | ||
3e26416e | 1959 | static s32 |
cf2b4488 | 1960 | wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, |
3e26416e | 1961 | bool enabled, s32 timeout) |
cf2b4488 | 1962 | { |
3e26416e GKH |
1963 | s32 pm; |
1964 | s32 err = 0; | |
cf2b4488 | 1965 | |
1ce4784e | 1966 | WL_TRACE("Enter\n"); |
cf2b4488 | 1967 | CHECK_SYS_UP(); |
1ce4784e | 1968 | |
cf2b4488 | 1969 | pm = enabled ? PM_FAST : PM_OFF; |
29750b90 | 1970 | pm = cpu_to_le32(pm); |
1ce4784e SS |
1971 | WL_INFO("power save %s\n", (pm ? "enabled" : "disabled")); |
1972 | ||
76c06459 JC |
1973 | err = wl_dev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)); |
1974 | if (unlikely(err)) { | |
cf2b4488 | 1975 | if (err == -ENODEV) |
1ce4784e | 1976 | WL_ERR("net_device is not ready yet\n"); |
cf2b4488 | 1977 | else |
f4528696 | 1978 | WL_ERR("error (%d)\n", err); |
cf2b4488 | 1979 | } |
1ce4784e | 1980 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
1981 | return err; |
1982 | } | |
1983 | ||
66cbd3ab | 1984 | static __used u32 wl_find_msb(u16 bit16) |
cf2b4488 | 1985 | { |
66cbd3ab | 1986 | u32 ret = 0; |
cf2b4488 HP |
1987 | |
1988 | if (bit16 & 0xff00) { | |
1989 | ret += 8; | |
1990 | bit16 >>= 8; | |
1991 | } | |
1992 | ||
1993 | if (bit16 & 0xf0) { | |
1994 | ret += 4; | |
1995 | bit16 >>= 4; | |
1996 | } | |
1997 | ||
1998 | if (bit16 & 0xc) { | |
1999 | ret += 2; | |
2000 | bit16 >>= 2; | |
2001 | } | |
2002 | ||
2003 | if (bit16 & 2) | |
2004 | ret += bit16 & 2; | |
2005 | else if (bit16) | |
2006 | ret += bit16; | |
2007 | ||
2008 | return ret; | |
2009 | } | |
2010 | ||
3e26416e | 2011 | static s32 |
cf2b4488 | 2012 | wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, |
3fd79f7c | 2013 | const u8 *addr, |
cf2b4488 HP |
2014 | const struct cfg80211_bitrate_mask *mask) |
2015 | { | |
2016 | struct wl_rateset rateset; | |
3e26416e GKH |
2017 | s32 rate; |
2018 | s32 val; | |
2019 | s32 err_bg; | |
2020 | s32 err_a; | |
66cbd3ab | 2021 | u32 legacy; |
3e26416e | 2022 | s32 err = 0; |
cf2b4488 | 2023 | |
1ce4784e | 2024 | WL_TRACE("Enter\n"); |
cf2b4488 | 2025 | CHECK_SYS_UP(); |
1ce4784e | 2026 | |
cf2b4488 HP |
2027 | /* addr param is always NULL. ignore it */ |
2028 | /* Get current rateset */ | |
76c06459 JC |
2029 | err = wl_dev_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, |
2030 | sizeof(rateset)); | |
2031 | if (unlikely(err)) { | |
f4528696 | 2032 | WL_ERR("could not get current rateset (%d)\n", err); |
1ce4784e | 2033 | goto done; |
cf2b4488 HP |
2034 | } |
2035 | ||
29750b90 | 2036 | rateset.count = le32_to_cpu(rateset.count); |
cf2b4488 | 2037 | |
76c06459 JC |
2038 | legacy = wl_find_msb(mask->control[IEEE80211_BAND_2GHZ].legacy); |
2039 | if (!legacy) | |
cf2b4488 HP |
2040 | legacy = wl_find_msb(mask->control[IEEE80211_BAND_5GHZ].legacy); |
2041 | ||
2042 | val = wl_g_rates[legacy - 1].bitrate * 100000; | |
2043 | ||
1ce4784e | 2044 | if (val < rateset.count) |
cf2b4488 HP |
2045 | /* Select rate by rateset index */ |
2046 | rate = rateset.rates[val] & 0x7f; | |
1ce4784e | 2047 | else |
cf2b4488 HP |
2048 | /* Specified rate in bps */ |
2049 | rate = val / 500000; | |
cf2b4488 | 2050 | |
1ce4784e | 2051 | WL_CONN("rate %d mbps\n", rate / 2); |
cf2b4488 HP |
2052 | |
2053 | /* | |
2054 | * | |
2055 | * Set rate override, | |
2056 | * Since the is a/b/g-blind, both a/bg_rate are enforced. | |
2057 | */ | |
2058 | err_bg = wl_dev_intvar_set(dev, "bg_rate", rate); | |
2059 | err_a = wl_dev_intvar_set(dev, "a_rate", rate); | |
2060 | if (unlikely(err_bg && err_a)) { | |
f4528696 | 2061 | WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a); |
1ce4784e | 2062 | err = err_bg | err_a; |
cf2b4488 HP |
2063 | } |
2064 | ||
1ce4784e SS |
2065 | done: |
2066 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
2067 | return err; |
2068 | } | |
2069 | ||
3e26416e | 2070 | static s32 wl_cfg80211_resume(struct wiphy *wiphy) |
cf2b4488 | 2071 | { |
e6e8f894 SS |
2072 | struct wl_priv *wl = wiphy_to_wl(wiphy); |
2073 | struct net_device *ndev = wl_to_ndev(wl); | |
cf2b4488 | 2074 | |
e6e8f894 SS |
2075 | /* |
2076 | * Check for WL_STATUS_READY before any function call which | |
2077 | * could result is bus access. Don't block the resume for | |
2078 | * any driver error conditions | |
2079 | */ | |
1ce4784e | 2080 | WL_TRACE("Enter\n"); |
cf2b4488 | 2081 | |
e6e8f894 SS |
2082 | #if defined(CONFIG_PM_SLEEP) |
2083 | atomic_set(&dhd_mmc_suspend, false); | |
2084 | #endif /* defined(CONFIG_PM_SLEEP) */ | |
2085 | ||
2086 | if (test_bit(WL_STATUS_READY, &wl->status)) { | |
2087 | /* Turn on Watchdog timer */ | |
2088 | wl_os_wd_timer(ndev, dhd_watchdog_ms); | |
2089 | wl_invoke_iscan(wiphy_to_wl(wiphy)); | |
2090 | } | |
2091 | ||
1ce4784e | 2092 | WL_TRACE("Exit\n"); |
e6e8f894 | 2093 | return 0; |
cf2b4488 HP |
2094 | } |
2095 | ||
3e26416e | 2096 | static s32 wl_cfg80211_suspend(struct wiphy *wiphy) |
cf2b4488 HP |
2097 | { |
2098 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
fb693a71 | 2099 | struct net_device *ndev = wl_to_ndev(wl); |
e6e8f894 | 2100 | |
1ce4784e | 2101 | WL_TRACE("Enter\n"); |
e6e8f894 SS |
2102 | |
2103 | /* | |
2104 | * Check for WL_STATUS_READY before any function call which | |
2105 | * could result is bus access. Don't block the suspend for | |
2106 | * any driver error conditions | |
2107 | */ | |
2108 | ||
2109 | /* | |
2110 | * While going to suspend if associated with AP disassociate | |
2111 | * from AP to save power while system is in suspended state | |
2112 | */ | |
2113 | if (test_bit(WL_STATUS_CONNECTED, &wl->status) && | |
2114 | test_bit(WL_STATUS_READY, &wl->status)) { | |
2115 | WL_INFO("Disassociating from AP" | |
2116 | " while entering suspend state\n"); | |
2117 | wl_link_down(wl); | |
2118 | ||
2119 | /* | |
2120 | * Make sure WPA_Supplicant receives all the event | |
2121 | * generated due to DISASSOC call to the fw to keep | |
2122 | * the state fw and WPA_Supplicant state consistent | |
2123 | */ | |
2124 | rtnl_unlock(); | |
2125 | wl_delay(500); | |
2126 | rtnl_lock(); | |
2127 | } | |
cf2b4488 | 2128 | |
cf2b4488 | 2129 | set_bit(WL_STATUS_SCAN_ABORTING, &wl->status); |
e6e8f894 SS |
2130 | if (test_bit(WL_STATUS_READY, &wl->status)) |
2131 | wl_term_iscan(wl); | |
2132 | ||
cf2b4488 | 2133 | if (wl->scan_request) { |
e6e8f894 SS |
2134 | /* Indidate scan abort to cfg80211 layer */ |
2135 | WL_INFO("Terminating scan in progress\n"); | |
2136 | cfg80211_scan_done(wl->scan_request, true); | |
cf2b4488 HP |
2137 | wl->scan_request = NULL; |
2138 | } | |
2139 | clear_bit(WL_STATUS_SCANNING, &wl->status); | |
2140 | clear_bit(WL_STATUS_SCAN_ABORTING, &wl->status); | |
e6e8f894 SS |
2141 | clear_bit(WL_STATUS_CONNECTING, &wl->status); |
2142 | clear_bit(WL_STATUS_CONNECTED, &wl->status); | |
6b5a5a3e | 2143 | |
e6e8f894 | 2144 | /* Inform SDIO stack not to switch off power to the chip */ |
6b5a5a3e | 2145 | sdioh_sdio_set_host_pm_flags(MMC_PM_KEEP_POWER); |
cf2b4488 | 2146 | |
e6e8f894 SS |
2147 | /* Turn off watchdog timer */ |
2148 | if (test_bit(WL_STATUS_READY, &wl->status)) { | |
2149 | WL_INFO("Terminate watchdog timer and enable MPC\n"); | |
2150 | wl_set_mpc(ndev, 1); | |
2151 | wl_os_wd_timer(ndev, 0); | |
2152 | } | |
2153 | ||
2154 | #if defined(CONFIG_PM_SLEEP) | |
2155 | atomic_set(&dhd_mmc_suspend, true); | |
2156 | #endif /* defined(CONFIG_PM_SLEEP) */ | |
2157 | ||
1ce4784e | 2158 | WL_TRACE("Exit\n"); |
e6e8f894 SS |
2159 | |
2160 | return 0; | |
cf2b4488 HP |
2161 | } |
2162 | ||
3e26416e | 2163 | static __used s32 |
cf2b4488 | 2164 | wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, |
3e26416e | 2165 | s32 err) |
cf2b4488 | 2166 | { |
cf2b4488 HP |
2167 | int i, j; |
2168 | ||
1ce4784e | 2169 | WL_CONN("No of elements %d\n", pmk_list->pmkids.npmkid); |
cf2b4488 | 2170 | for (i = 0; i < pmk_list->pmkids.npmkid; i++) { |
1ce4784e | 2171 | WL_CONN("PMKID[%d]: %pM =\n", i, |
35af8764 | 2172 | &pmk_list->pmkids.pmkid[i].BSSID); |
1ce4784e SS |
2173 | for (j = 0; j < WLAN_PMKID_LEN; j++) |
2174 | WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]); | |
cf2b4488 | 2175 | } |
1ce4784e SS |
2176 | |
2177 | if (likely(!err)) | |
2178 | wl_dev_bufvar_set(dev, "pmkid_info", (char *)pmk_list, | |
cf2b4488 | 2179 | sizeof(*pmk_list)); |
cf2b4488 HP |
2180 | |
2181 | return err; | |
2182 | } | |
2183 | ||
3e26416e | 2184 | static s32 |
cf2b4488 HP |
2185 | wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, |
2186 | struct cfg80211_pmksa *pmksa) | |
2187 | { | |
2188 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
3e26416e | 2189 | s32 err = 0; |
cf2b4488 HP |
2190 | int i; |
2191 | ||
1ce4784e | 2192 | WL_TRACE("Enter\n"); |
cf2b4488 | 2193 | CHECK_SYS_UP(); |
1ce4784e | 2194 | |
cf2b4488 HP |
2195 | for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) |
2196 | if (!memcmp(pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, | |
b8d63078 | 2197 | ETH_ALEN)) |
cf2b4488 HP |
2198 | break; |
2199 | if (i < WL_NUM_PMKIDS_MAX) { | |
2200 | memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, pmksa->bssid, | |
b8d63078 | 2201 | ETH_ALEN); |
cf2b4488 | 2202 | memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, pmksa->pmkid, |
35af8764 | 2203 | WLAN_PMKID_LEN); |
cf2b4488 HP |
2204 | if (i == wl->pmk_list->pmkids.npmkid) |
2205 | wl->pmk_list->pmkids.npmkid++; | |
1ce4784e | 2206 | } else |
cf2b4488 | 2207 | err = -EINVAL; |
1ce4784e SS |
2208 | |
2209 | WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", | |
f4528696 | 2210 | &wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid].BSSID); |
1ce4784e SS |
2211 | for (i = 0; i < WLAN_PMKID_LEN; i++) |
2212 | WL_CONN("%02x\n", | |
f4528696 JP |
2213 | wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid]. |
2214 | PMKID[i]); | |
cf2b4488 HP |
2215 | |
2216 | err = wl_update_pmklist(dev, wl->pmk_list, err); | |
2217 | ||
1ce4784e | 2218 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
2219 | return err; |
2220 | } | |
2221 | ||
3e26416e | 2222 | static s32 |
cf2b4488 HP |
2223 | wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, |
2224 | struct cfg80211_pmksa *pmksa) | |
2225 | { | |
2226 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
cf2b4488 | 2227 | struct _pmkid_list pmkid; |
3e26416e | 2228 | s32 err = 0; |
cf2b4488 HP |
2229 | int i; |
2230 | ||
1ce4784e | 2231 | WL_TRACE("Enter\n"); |
cf2b4488 | 2232 | CHECK_SYS_UP(); |
b8d63078 | 2233 | memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN); |
35af8764 | 2234 | memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); |
cf2b4488 | 2235 | |
1ce4784e | 2236 | WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", |
f4528696 | 2237 | &pmkid.pmkid[0].BSSID); |
1ce4784e SS |
2238 | for (i = 0; i < WLAN_PMKID_LEN; i++) |
2239 | WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]); | |
cf2b4488 HP |
2240 | |
2241 | for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) | |
2242 | if (!memcmp | |
2243 | (pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, | |
b8d63078 | 2244 | ETH_ALEN)) |
cf2b4488 HP |
2245 | break; |
2246 | ||
2247 | if ((wl->pmk_list->pmkids.npmkid > 0) | |
2248 | && (i < wl->pmk_list->pmkids.npmkid)) { | |
2249 | memset(&wl->pmk_list->pmkids.pmkid[i], 0, sizeof(pmkid_t)); | |
2250 | for (; i < (wl->pmk_list->pmkids.npmkid - 1); i++) { | |
2251 | memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, | |
2252 | &wl->pmk_list->pmkids.pmkid[i + 1].BSSID, | |
b8d63078 | 2253 | ETH_ALEN); |
cf2b4488 HP |
2254 | memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, |
2255 | &wl->pmk_list->pmkids.pmkid[i + 1].PMKID, | |
35af8764 | 2256 | WLAN_PMKID_LEN); |
cf2b4488 HP |
2257 | } |
2258 | wl->pmk_list->pmkids.npmkid--; | |
1ce4784e | 2259 | } else |
cf2b4488 | 2260 | err = -EINVAL; |
cf2b4488 HP |
2261 | |
2262 | err = wl_update_pmklist(dev, wl->pmk_list, err); | |
2263 | ||
1ce4784e | 2264 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
2265 | return err; |
2266 | ||
2267 | } | |
2268 | ||
3e26416e | 2269 | static s32 |
cf2b4488 HP |
2270 | wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) |
2271 | { | |
2272 | struct wl_priv *wl = wiphy_to_wl(wiphy); | |
3e26416e | 2273 | s32 err = 0; |
cf2b4488 | 2274 | |
1ce4784e | 2275 | WL_TRACE("Enter\n"); |
cf2b4488 | 2276 | CHECK_SYS_UP(); |
1ce4784e | 2277 | |
cf2b4488 HP |
2278 | memset(wl->pmk_list, 0, sizeof(*wl->pmk_list)); |
2279 | err = wl_update_pmklist(dev, wl->pmk_list, err); | |
1ce4784e SS |
2280 | |
2281 | WL_TRACE("Exit\n"); | |
cf2b4488 HP |
2282 | return err; |
2283 | ||
2284 | } | |
2285 | ||
2286 | static struct cfg80211_ops wl_cfg80211_ops = { | |
2287 | .change_virtual_intf = wl_cfg80211_change_iface, | |
2288 | .scan = wl_cfg80211_scan, | |
2289 | .set_wiphy_params = wl_cfg80211_set_wiphy_params, | |
2290 | .join_ibss = wl_cfg80211_join_ibss, | |
2291 | .leave_ibss = wl_cfg80211_leave_ibss, | |
2292 | .get_station = wl_cfg80211_get_station, | |
2293 | .set_tx_power = wl_cfg80211_set_tx_power, | |
2294 | .get_tx_power = wl_cfg80211_get_tx_power, | |
2295 | .add_key = wl_cfg80211_add_key, | |
2296 | .del_key = wl_cfg80211_del_key, | |
2297 | .get_key = wl_cfg80211_get_key, | |
2298 | .set_default_key = wl_cfg80211_config_default_key, | |
2299 | .set_default_mgmt_key = wl_cfg80211_config_default_mgmt_key, | |
2300 | .set_power_mgmt = wl_cfg80211_set_power_mgmt, | |
2301 | .set_bitrate_mask = wl_cfg80211_set_bitrate_mask, | |
2302 | .connect = wl_cfg80211_connect, | |
2303 | .disconnect = wl_cfg80211_disconnect, | |
2304 | .suspend = wl_cfg80211_suspend, | |
2305 | .resume = wl_cfg80211_resume, | |
2306 | .set_pmksa = wl_cfg80211_set_pmksa, | |
2307 | .del_pmksa = wl_cfg80211_del_pmksa, | |
2308 | .flush_pmksa = wl_cfg80211_flush_pmksa | |
2309 | }; | |
2310 | ||
3e26416e | 2311 | static s32 wl_mode_to_nl80211_iftype(s32 mode) |
cf2b4488 | 2312 | { |
3e26416e | 2313 | s32 err = 0; |
cf2b4488 HP |
2314 | |
2315 | switch (mode) { | |
2316 | case WL_MODE_BSS: | |
2317 | return NL80211_IFTYPE_STATION; | |
2318 | case WL_MODE_IBSS: | |
2319 | return NL80211_IFTYPE_ADHOC; | |
2320 | default: | |
2321 | return NL80211_IFTYPE_UNSPECIFIED; | |
2322 | } | |
2323 | ||
2324 | return err; | |
2325 | } | |
2326 | ||
3e26416e | 2327 | static struct wireless_dev *wl_alloc_wdev(s32 sizeof_iface, |
cf2b4488 HP |
2328 | struct device *dev) |
2329 | { | |
2330 | struct wireless_dev *wdev; | |
3e26416e | 2331 | s32 err = 0; |
cf2b4488 HP |
2332 | |
2333 | wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); | |
2334 | if (unlikely(!wdev)) { | |
f4528696 | 2335 | WL_ERR("Could not allocate wireless device\n"); |
cf2b4488 HP |
2336 | return ERR_PTR(-ENOMEM); |
2337 | } | |
2338 | wdev->wiphy = | |
2339 | wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_priv) + sizeof_iface); | |
2340 | if (unlikely(!wdev->wiphy)) { | |
f4528696 | 2341 | WL_ERR("Couldn not allocate wiphy device\n"); |
cf2b4488 HP |
2342 | err = -ENOMEM; |
2343 | goto wiphy_new_out; | |
2344 | } | |
2345 | set_wiphy_dev(wdev->wiphy, dev); | |
2346 | wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; | |
2347 | wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; | |
2348 | wdev->wiphy->interface_modes = | |
2349 | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); | |
2350 | wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; | |
2351 | wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set | |
2352 | * it as 11a by default. | |
2353 | * This will be updated with | |
2354 | * 11n phy tables in | |
2355 | * "ifconfig up" | |
2356 | * if phy has 11n capability | |
2357 | */ | |
2358 | wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | |
2359 | wdev->wiphy->cipher_suites = __wl_cipher_suites; | |
2360 | wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); | |
2361 | #ifndef WL_POWERSAVE_DISABLED | |
2362 | wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power | |
2363 | * save mode | |
2364 | * by default | |
2365 | */ | |
2366 | #else | |
2367 | wdev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; | |
2368 | #endif /* !WL_POWERSAVE_DISABLED */ | |
76c06459 JC |
2369 | err = wiphy_register(wdev->wiphy); |
2370 | if (unlikely(err < 0)) { | |
f4528696 | 2371 | WL_ERR("Couldn not register wiphy device (%d)\n", err); |
cf2b4488 HP |
2372 | goto wiphy_register_out; |
2373 | } | |
2374 | return wdev; | |
2375 | ||
2376 | wiphy_register_out: | |
2377 | wiphy_free(wdev->wiphy); | |
2378 | ||
2379 | wiphy_new_out: | |
2380 | kfree(wdev); | |
2381 | ||
2382 | return ERR_PTR(err); | |
2383 | } | |
2384 | ||
2385 | static void wl_free_wdev(struct wl_priv *wl) | |
2386 | { | |
2387 | struct wireless_dev *wdev = wl_to_wdev(wl); | |
2388 | ||
2389 | if (unlikely(!wdev)) { | |
f4528696 | 2390 | WL_ERR("wdev is invalid\n"); |
cf2b4488 HP |
2391 | return; |
2392 | } | |
2393 | wiphy_unregister(wdev->wiphy); | |
2394 | wiphy_free(wdev->wiphy); | |
2395 | kfree(wdev); | |
2396 | wl_to_wdev(wl) = NULL; | |
2397 | } | |
2398 | ||
3e26416e | 2399 | static s32 wl_inform_bss(struct wl_priv *wl) |
cf2b4488 HP |
2400 | { |
2401 | struct wl_scan_results *bss_list; | |
2402 | struct wl_bss_info *bi = NULL; /* must be initialized */ | |
3e26416e | 2403 | s32 err = 0; |
cf2b4488 HP |
2404 | int i; |
2405 | ||
2406 | bss_list = wl->bss_list; | |
2407 | if (unlikely(bss_list->version != WL_BSS_INFO_VERSION)) { | |
f4528696 JP |
2408 | WL_ERR("Version %d != WL_BSS_INFO_VERSION\n", |
2409 | bss_list->version); | |
cf2b4488 HP |
2410 | return -EOPNOTSUPP; |
2411 | } | |
1ce4784e | 2412 | WL_SCAN("scanned AP count (%d)\n", bss_list->count); |
cf2b4488 HP |
2413 | bi = next_bss(bss_list, bi); |
2414 | for_each_bss(bss_list, bi, i) { | |
76c06459 JC |
2415 | err = wl_inform_single_bss(wl, bi); |
2416 | if (unlikely(err)) | |
cf2b4488 HP |
2417 | break; |
2418 | } | |
2419 | return err; | |
2420 | } | |
2421 | ||
75494966 | 2422 | |
3e26416e | 2423 | static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) |
cf2b4488 HP |
2424 | { |
2425 | struct wiphy *wiphy = wl_to_wiphy(wl); | |
75494966 SS |
2426 | struct ieee80211_channel *notify_channel; |
2427 | struct cfg80211_bss *bss; | |
cf2b4488 | 2428 | struct ieee80211_supported_band *band; |
3e26416e | 2429 | s32 err = 0; |
75494966 SS |
2430 | u16 channel; |
2431 | u32 freq; | |
2432 | u64 notify_timestamp; | |
2433 | u16 notify_capability; | |
2434 | u16 notify_interval; | |
2435 | u8 *notify_ie; | |
2436 | size_t notify_ielen; | |
2437 | s32 notify_signal; | |
cf2b4488 | 2438 | |
29750b90 | 2439 | if (unlikely(le32_to_cpu(bi->length) > WL_BSS_INFO_MAX)) { |
75494966 SS |
2440 | WL_ERR("Bss info is larger than buffer. Discarding\n"); |
2441 | return 0; | |
cf2b4488 | 2442 | } |
fb693a71 | 2443 | |
75494966 SS |
2444 | channel = bi->ctl_ch ? bi->ctl_ch : |
2445 | CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec)); | |
2446 | ||
2447 | if (channel <= CH_MAX_2G_CHANNEL) | |
cf2b4488 HP |
2448 | band = wiphy->bands[IEEE80211_BAND_2GHZ]; |
2449 | else | |
2450 | band = wiphy->bands[IEEE80211_BAND_5GHZ]; | |
75494966 SS |
2451 | |
2452 | freq = ieee80211_channel_to_frequency(channel, band->band); | |
2453 | notify_channel = ieee80211_get_channel(wiphy, freq); | |
2454 | ||
2455 | notify_timestamp = jiffies_to_msecs(jiffies)*1000; /* uSec */ | |
2456 | notify_capability = le16_to_cpu(bi->capability); | |
2457 | notify_interval = le16_to_cpu(bi->beacon_period); | |
2458 | notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); | |
2459 | notify_ielen = le16_to_cpu(bi->ie_length); | |
2460 | notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; | |
2461 | ||
1ce4784e | 2462 | WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", |
75494966 SS |
2463 | bi->BSSID[0], bi->BSSID[1], bi->BSSID[2], |
2464 | bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]); | |
1ce4784e SS |
2465 | WL_CONN("Channel: %d(%d)\n", channel, freq); |
2466 | WL_CONN("Capability: %X\n", notify_capability); | |
2467 | WL_CONN("Beacon interval: %d\n", notify_interval); | |
2468 | WL_CONN("Signal: %d\n", notify_signal); | |
2469 | WL_CONN("notify_timestamp: %#018llx\n", notify_timestamp); | |
75494966 SS |
2470 | |
2471 | bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID, | |
2472 | notify_timestamp, notify_capability, notify_interval, notify_ie, | |
2473 | notify_ielen, notify_signal, GFP_KERNEL); | |
2474 | ||
2475 | if (unlikely(!bss)) { | |
f4528696 | 2476 | WL_ERR("cfg80211_inform_bss_frame error\n"); |
cf2b4488 HP |
2477 | return -EINVAL; |
2478 | } | |
cf2b4488 HP |
2479 | |
2480 | return err; | |
2481 | } | |
2482 | ||
69274f02 SS |
2483 | static s32 |
2484 | wl_inform_ibss(struct wl_priv *wl, struct net_device *dev, const u8 *bssid) | |
2485 | { | |
2486 | struct wiphy *wiphy = wl_to_wiphy(wl); | |
2487 | struct ieee80211_channel *notify_channel; | |
2488 | struct wl_bss_info *bi = NULL; | |
2489 | struct ieee80211_supported_band *band; | |
2490 | u8 *buf = NULL; | |
2491 | s32 err = 0; | |
2492 | u16 channel; | |
2493 | u32 freq; | |
2494 | u64 notify_timestamp; | |
2495 | u16 notify_capability; | |
2496 | u16 notify_interval; | |
2497 | u8 *notify_ie; | |
2498 | size_t notify_ielen; | |
2499 | s32 notify_signal; | |
2500 | ||
1ce4784e SS |
2501 | WL_TRACE("Enter\n"); |
2502 | ||
69274f02 SS |
2503 | buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); |
2504 | if (buf == NULL) { | |
2505 | WL_ERR("kzalloc() failed\n"); | |
2506 | err = -ENOMEM; | |
2507 | goto CleanUp; | |
2508 | } | |
2509 | ||
2510 | *(u32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); | |
2511 | ||
2512 | err = wl_dev_ioctl(dev, WLC_GET_BSS_INFO, buf, WL_BSS_INFO_MAX); | |
2513 | if (unlikely(err)) { | |
2514 | WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err); | |
2515 | goto CleanUp; | |
2516 | } | |
2517 | ||
2518 | bi = (wl_bss_info_t *)(buf + 4); | |
2519 | ||
2520 | channel = bi->ctl_ch ? bi->ctl_ch : | |
2521 | CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec)); | |
2522 | ||
2523 | if (channel <= CH_MAX_2G_CHANNEL) | |
2524 | band = wiphy->bands[IEEE80211_BAND_2GHZ]; | |
2525 | else | |
2526 | band = wiphy->bands[IEEE80211_BAND_5GHZ]; | |
2527 | ||
2528 | freq = ieee80211_channel_to_frequency(channel, band->band); | |
2529 | notify_channel = ieee80211_get_channel(wiphy, freq); | |
2530 | ||
2531 | notify_timestamp = jiffies_to_msecs(jiffies)*1000; /* uSec */ | |
2532 | notify_capability = le16_to_cpu(bi->capability); | |
2533 | notify_interval = le16_to_cpu(bi->beacon_period); | |
2534 | notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); | |
2535 | notify_ielen = le16_to_cpu(bi->ie_length); | |
2536 | notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; | |
2537 | ||
1ce4784e SS |
2538 | WL_CONN("channel: %d(%d)\n", channel, freq); |
2539 | WL_CONN("capability: %X\n", notify_capability); | |
2540 | WL_CONN("beacon interval: %d\n", notify_interval); | |
2541 | WL_CONN("signal: %d\n", notify_signal); | |
2542 | WL_CONN("notify_timestamp: %#018llx\n", notify_timestamp); | |
69274f02 SS |
2543 | |
2544 | cfg80211_inform_bss(wiphy, notify_channel, bssid, | |
2545 | notify_timestamp, notify_capability, notify_interval, | |
2546 | notify_ie, notify_ielen, notify_signal, GFP_KERNEL); | |
2547 | ||
2548 | CleanUp: | |
2549 | ||
2550 | kfree(buf); | |
2551 | ||
1ce4784e SS |
2552 | WL_TRACE("Exit\n"); |
2553 | ||
69274f02 SS |
2554 | return err; |
2555 | } | |
2556 | ||
cf2b4488 HP |
2557 | static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e) |
2558 | { | |
628f10ba | 2559 | u32 event = be32_to_cpu(e->event_type); |
e494632e | 2560 | u32 status = be32_to_cpu(e->status); |
cf2b4488 | 2561 | |
e494632e | 2562 | if (event == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) { |
1ce4784e | 2563 | WL_CONN("Processing set ssid\n"); |
e494632e SS |
2564 | wl->link_up = true; |
2565 | return true; | |
cf2b4488 HP |
2566 | } |
2567 | ||
0965ae88 | 2568 | return false; |
cf2b4488 HP |
2569 | } |
2570 | ||
2571 | static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e) | |
2572 | { | |
628f10ba SF |
2573 | u32 event = be32_to_cpu(e->event_type); |
2574 | u16 flags = be16_to_cpu(e->flags); | |
cf2b4488 | 2575 | |
e494632e | 2576 | if (event == WLC_E_LINK && (!(flags & WLC_EVENT_MSG_LINK))) { |
1ce4784e | 2577 | WL_CONN("Processing link down\n"); |
0f0881b0 | 2578 | return true; |
cf2b4488 | 2579 | } |
0965ae88 | 2580 | return false; |
cf2b4488 HP |
2581 | } |
2582 | ||
b3164c71 | 2583 | static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e) |
2584 | { | |
628f10ba SF |
2585 | u32 event = be32_to_cpu(e->event_type); |
2586 | u32 status = be32_to_cpu(e->status); | |
e494632e | 2587 | u16 flags = be16_to_cpu(e->flags); |
b3164c71 | 2588 | |
e494632e | 2589 | if (event == WLC_E_LINK && status == WLC_E_STATUS_NO_NETWORKS) { |
1ce4784e | 2590 | WL_CONN("Processing Link %s & no network found\n", |
e494632e SS |
2591 | flags & WLC_EVENT_MSG_LINK ? "up" : "down"); |
2592 | return true; | |
2593 | } | |
2594 | ||
2595 | if (event == WLC_E_SET_SSID && status != WLC_E_STATUS_SUCCESS) { | |
1ce4784e | 2596 | WL_CONN("Processing connecting & no network found\n"); |
e494632e | 2597 | return true; |
b3164c71 | 2598 | } |
2599 | ||
0965ae88 | 2600 | return false; |
b3164c71 | 2601 | } |
2602 | ||
3e26416e | 2603 | static s32 |
cf2b4488 HP |
2604 | wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, |
2605 | const wl_event_msg_t *e, void *data) | |
2606 | { | |
3e26416e | 2607 | s32 err = 0; |
cf2b4488 HP |
2608 | |
2609 | if (wl_is_linkup(wl, e)) { | |
1ce4784e | 2610 | WL_CONN("Linkup\n"); |
cf2b4488 | 2611 | if (wl_is_ibssmode(wl)) { |
e494632e SS |
2612 | wl_update_prof(wl, NULL, (void *)e->addr, |
2613 | WL_PROF_BSSID); | |
69274f02 | 2614 | wl_inform_ibss(wl, ndev, e->addr); |
e494632e SS |
2615 | cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL); |
2616 | clear_bit(WL_STATUS_CONNECTING, &wl->status); | |
2617 | set_bit(WL_STATUS_CONNECTED, &wl->status); | |
2618 | } else | |
0f0881b0 | 2619 | wl_bss_connect_done(wl, ndev, e, data, true); |
cf2b4488 | 2620 | } else if (wl_is_linkdown(wl, e)) { |
1ce4784e | 2621 | WL_CONN("Linkdown\n"); |
e494632e SS |
2622 | if (wl_is_ibssmode(wl)) { |
2623 | if (test_and_clear_bit(WL_STATUS_CONNECTED, | |
2624 | &wl->status)) | |
2625 | wl_link_down(wl); | |
2626 | } else { | |
2627 | if (test_and_clear_bit(WL_STATUS_CONNECTED, | |
2628 | &wl->status)) { | |
2629 | cfg80211_disconnected(ndev, 0, NULL, 0, | |
2630 | GFP_KERNEL); | |
2631 | wl_link_down(wl); | |
2632 | } | |
2633 | } | |
cf2b4488 | 2634 | wl_init_prof(wl->profile); |
b3164c71 | 2635 | } else if (wl_is_nonetwork(wl, e)) { |
e494632e SS |
2636 | if (wl_is_ibssmode(wl)) |
2637 | clear_bit(WL_STATUS_CONNECTING, &wl->status); | |
2638 | else | |
2639 | wl_bss_connect_done(wl, ndev, e, data, false); | |
cf2b4488 HP |
2640 | } |
2641 | ||
2642 | return err; | |
2643 | } | |
2644 | ||
3e26416e | 2645 | static s32 |
cf2b4488 HP |
2646 | wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, |
2647 | const wl_event_msg_t *e, void *data) | |
2648 | { | |
3e26416e | 2649 | s32 err = 0; |
e494632e SS |
2650 | u32 event = be32_to_cpu(e->event_type); |
2651 | u32 status = be32_to_cpu(e->status); | |
cf2b4488 | 2652 | |
e494632e SS |
2653 | if (event == WLC_E_ROAM && status == WLC_E_STATUS_SUCCESS) { |
2654 | if (test_bit(WL_STATUS_CONNECTED, &wl->status)) | |
2655 | wl_bss_roaming_done(wl, ndev, e, data); | |
2656 | else | |
2657 | wl_bss_connect_done(wl, ndev, e, data, true); | |
2658 | } | |
cf2b4488 HP |
2659 | |
2660 | return err; | |
2661 | } | |
2662 | ||
3e26416e GKH |
2663 | static __used s32 |
2664 | wl_dev_bufvar_set(struct net_device *dev, s8 *name, s8 *buf, s32 len) | |
cf2b4488 HP |
2665 | { |
2666 | struct wl_priv *wl = ndev_to_wl(dev); | |
66cbd3ab | 2667 | u32 buflen; |
cf2b4488 HP |
2668 | |
2669 | buflen = bcm_mkiovar(name, buf, len, wl->ioctl_buf, WL_IOCTL_LEN_MAX); | |
d7ddd169 | 2670 | BUG_ON(!buflen); |
cf2b4488 HP |
2671 | |
2672 | return wl_dev_ioctl(dev, WLC_SET_VAR, wl->ioctl_buf, buflen); | |
2673 | } | |
2674 | ||
3e26416e | 2675 | static s32 |
562c8850 | 2676 | wl_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf, |
3e26416e | 2677 | s32 buf_len) |
cf2b4488 HP |
2678 | { |
2679 | struct wl_priv *wl = ndev_to_wl(dev); | |
66cbd3ab | 2680 | u32 len; |
3e26416e | 2681 | s32 err = 0; |
cf2b4488 HP |
2682 | |
2683 | len = bcm_mkiovar(name, NULL, 0, wl->ioctl_buf, WL_IOCTL_LEN_MAX); | |
d7ddd169 | 2684 | BUG_ON(!len); |
76c06459 JC |
2685 | err = wl_dev_ioctl(dev, WLC_GET_VAR, (void *)wl->ioctl_buf, |
2686 | WL_IOCTL_LEN_MAX); | |
2687 | if (unlikely(err)) { | |
f4528696 | 2688 | WL_ERR("error (%d)\n", err); |
cf2b4488 HP |
2689 | return err; |
2690 | } | |
2691 | memcpy(buf, wl->ioctl_buf, buf_len); | |
2692 | ||
2693 | return err; | |
2694 | } | |
2695 | ||
3e26416e | 2696 | static s32 wl_get_assoc_ies(struct wl_priv *wl) |
cf2b4488 HP |
2697 | { |
2698 | struct net_device *ndev = wl_to_ndev(wl); | |
2699 | struct wl_assoc_ielen *assoc_info; | |
2700 | struct wl_connect_info *conn_info = wl_to_conn(wl); | |
66cbd3ab GKH |
2701 | u32 req_len; |
2702 | u32 resp_len; | |
3e26416e | 2703 | s32 err = 0; |
cf2b4488 | 2704 | |
e494632e SS |
2705 | wl_clear_assoc_ies(wl); |
2706 | ||
76c06459 JC |
2707 | err = wl_dev_bufvar_get(ndev, "assoc_info", wl->extra_buf, |
2708 | WL_ASSOC_INFO_MAX); | |
2709 | if (unlikely(err)) { | |
f4528696 | 2710 | WL_ERR("could not get assoc info (%d)\n", err); |
cf2b4488 HP |
2711 | return err; |
2712 | } | |
2713 | assoc_info = (struct wl_assoc_ielen *)wl->extra_buf; | |
2714 | req_len = assoc_info->req_len; | |
2715 | resp_len = assoc_info->resp_len; | |
2716 | if (req_len) { | |
76c06459 JC |
2717 | err = wl_dev_bufvar_get(ndev, "assoc_req_ies", wl->extra_buf, |
2718 | WL_ASSOC_INFO_MAX); | |
2719 | if (unlikely(err)) { | |
f4528696 | 2720 | WL_ERR("could not get assoc req (%d)\n", err); |
cf2b4488 HP |
2721 | return err; |
2722 | } | |
2723 | conn_info->req_ie_len = req_len; | |
2724 | conn_info->req_ie = | |
2725 | kmemdup(wl->extra_buf, conn_info->req_ie_len, GFP_KERNEL); | |
2726 | } else { | |
2727 | conn_info->req_ie_len = 0; | |
2728 | conn_info->req_ie = NULL; | |
2729 | } | |
2730 | if (resp_len) { | |
76c06459 JC |
2731 | err = wl_dev_bufvar_get(ndev, "assoc_resp_ies", wl->extra_buf, |
2732 | WL_ASSOC_INFO_MAX); | |
2733 | if (unlikely(err)) { | |
f4528696 | 2734 | WL_ERR("could not get assoc resp (%d)\n", err); |
cf2b4488 HP |
2735 | return err; |
2736 | } | |
2737 | conn_info->resp_ie_len = resp_len; | |
2738 | conn_info->resp_ie = | |
2739 | kmemdup(wl->extra_buf, conn_info->resp_ie_len, GFP_KERNEL); | |
2740 | } else { | |
2741 | conn_info->resp_ie_len = 0; | |
2742 | conn_info->resp_ie = NULL; | |
2743 | } | |
1ce4784e | 2744 | WL_CONN("req len (%d) resp len (%d)\n", |
f4528696 | 2745 | conn_info->req_ie_len, conn_info->resp_ie_len); |
cf2b4488 HP |
2746 | |
2747 | return err; | |
2748 | } | |
2749 | ||
e494632e SS |
2750 | static void wl_clear_assoc_ies(struct wl_priv *wl) |
2751 | { | |
2752 | struct wl_connect_info *conn_info = wl_to_conn(wl); | |
2753 | ||
2754 | kfree(conn_info->req_ie); | |
2755 | conn_info->req_ie = NULL; | |
2756 | conn_info->req_ie_len = 0; | |
2757 | kfree(conn_info->resp_ie); | |
2758 | conn_info->resp_ie = NULL; | |
2759 | conn_info->resp_ie_len = 0; | |
2760 | } | |
2761 | ||
2762 | ||
fb693a71 | 2763 | static void wl_ch_to_chanspec(int ch, struct wl_join_params *join_params, |
2764 | size_t *join_params_size) | |
2765 | { | |
2766 | chanspec_t chanspec = 0; | |
2767 | ||
2768 | if (ch != 0) { | |
2769 | join_params->params.chanspec_num = 1; | |
2770 | join_params->params.chanspec_list[0] = ch; | |
2771 | ||
e494632e | 2772 | if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL) |
fb693a71 | 2773 | chanspec |= WL_CHANSPEC_BAND_2G; |
2774 | else | |
2775 | chanspec |= WL_CHANSPEC_BAND_5G; | |
2776 | ||
2777 | chanspec |= WL_CHANSPEC_BW_20; | |
2778 | chanspec |= WL_CHANSPEC_CTL_SB_NONE; | |
2779 | ||
2780 | *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE + | |
2781 | join_params->params.chanspec_num * sizeof(chanspec_t); | |
2782 | ||
2783 | join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; | |
2784 | join_params->params.chanspec_list[0] |= chanspec; | |
2785 | join_params->params.chanspec_list[0] = | |
29750b90 | 2786 | cpu_to_le16(join_params->params.chanspec_list[0]); |
fb693a71 | 2787 | |
2788 | join_params->params.chanspec_num = | |
29750b90 | 2789 | cpu_to_le32(join_params->params.chanspec_num); |
fb693a71 | 2790 | |
1ce4784e SS |
2791 | WL_CONN("join_params->params.chanspec_list[0]= %#X," |
2792 | "channel %d, chanspec %#X\n", | |
f4528696 | 2793 | join_params->params.chanspec_list[0], ch, chanspec); |
fb693a71 | 2794 | } |
2795 | } | |
2796 | ||
3e26416e | 2797 | static s32 wl_update_bss_info(struct wl_priv *wl) |
cf2b4488 | 2798 | { |
cf2b4488 HP |
2799 | struct wl_bss_info *bi; |
2800 | struct wlc_ssid *ssid; | |
1e0645c3 | 2801 | struct bcm_tlv *tim; |
2802 | u16 beacon_interval; | |
2803 | u8 dtim_period; | |
2804 | size_t ie_len; | |
2805 | u8 *ie; | |
3e26416e | 2806 | s32 err = 0; |
cf2b4488 | 2807 | |
1ce4784e | 2808 | WL_TRACE("Enter\n"); |
cf2b4488 HP |
2809 | if (wl_is_ibssmode(wl)) |
2810 | return err; | |
2811 | ||
2812 | ssid = (struct wlc_ssid *)wl_read_prof(wl, WL_PROF_SSID); | |
cf2b4488 | 2813 | |
e494632e SS |
2814 | *(u32 *)wl->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX); |
2815 | err = wl_dev_ioctl(wl_to_ndev(wl), WLC_GET_BSS_INFO, | |
2816 | wl->extra_buf, WL_EXTRA_BUF_MAX); | |
2817 | if (unlikely(err)) { | |
2818 | WL_ERR("Could not get bss info %d\n", err); | |
2819 | goto update_bss_info_out; | |
cf2b4488 HP |
2820 | } |
2821 | ||
e494632e SS |
2822 | bi = (struct wl_bss_info *)(wl->extra_buf + 4); |
2823 | err = wl_inform_single_bss(wl, bi); | |
2824 | if (unlikely(err)) | |
2825 | goto update_bss_info_out; | |
2826 | ||
2827 | ie = ((u8 *)bi) + bi->ie_offset; | |
2828 | ie_len = bi->ie_length; | |
2829 | beacon_interval = cpu_to_le16(bi->beacon_period); | |
2830 | ||
1e0645c3 | 2831 | tim = bcm_parse_tlvs(ie, ie_len, WLAN_EID_TIM); |
e494632e | 2832 | if (tim) |
1e0645c3 | 2833 | dtim_period = tim->data[1]; |
e494632e | 2834 | else { |
1e0645c3 | 2835 | /* |
2836 | * active scan was done so we could not get dtim | |
2837 | * information out of probe response. | |
2838 | * so we speficially query dtim information to dongle. | |
2839 | */ | |
e494632e SS |
2840 | u32 var; |
2841 | err = wl_dev_intvar_get(wl_to_ndev(wl), "dtim_assoc", &var); | |
1e0645c3 | 2842 | if (unlikely(err)) { |
e494632e | 2843 | WL_ERR("wl dtim_assoc failed (%d)\n", err); |
1e0645c3 | 2844 | goto update_bss_info_out; |
2845 | } | |
e494632e | 2846 | dtim_period = (u8)var; |
1e0645c3 | 2847 | } |
2848 | ||
2849 | wl_update_prof(wl, NULL, &beacon_interval, WL_PROF_BEACONINT); | |
2850 | wl_update_prof(wl, NULL, &dtim_period, WL_PROF_DTIMPERIOD); | |
2851 | ||
cf2b4488 | 2852 | update_bss_info_out: |
1ce4784e | 2853 | WL_TRACE("Exit"); |
cf2b4488 HP |
2854 | return err; |
2855 | } | |
2856 | ||
3e26416e | 2857 | static s32 |
cf2b4488 HP |
2858 | wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, |
2859 | const wl_event_msg_t *e, void *data) | |
2860 | { | |
2861 | struct wl_connect_info *conn_info = wl_to_conn(wl); | |
3e26416e | 2862 | s32 err = 0; |
cf2b4488 | 2863 | |
1ce4784e SS |
2864 | WL_TRACE("Enter\n"); |
2865 | ||
cf2b4488 | 2866 | wl_get_assoc_ies(wl); |
e494632e | 2867 | wl_update_prof(wl, NULL, &e->addr, WL_PROF_BSSID); |
cf2b4488 | 2868 | wl_update_bss_info(wl); |
e494632e | 2869 | |
ed9d0102 | 2870 | cfg80211_roamed(ndev, NULL, |
e494632e | 2871 | (u8 *)wl_read_prof(wl, WL_PROF_BSSID), |
cf2b4488 HP |
2872 | conn_info->req_ie, conn_info->req_ie_len, |
2873 | conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); | |
1ce4784e | 2874 | WL_CONN("Report roaming result\n"); |
cf2b4488 HP |
2875 | |
2876 | set_bit(WL_STATUS_CONNECTED, &wl->status); | |
1ce4784e | 2877 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
2878 | return err; |
2879 | } | |
2880 | ||
3e26416e | 2881 | static s32 |
cf2b4488 | 2882 | wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, |
b3164c71 | 2883 | const wl_event_msg_t *e, void *data, bool completed) |
cf2b4488 HP |
2884 | { |
2885 | struct wl_connect_info *conn_info = wl_to_conn(wl); | |
3e26416e | 2886 | s32 err = 0; |
cf2b4488 | 2887 | |
1ce4784e | 2888 | WL_TRACE("Enter\n"); |
e494632e | 2889 | |
cf2b4488 | 2890 | if (test_and_clear_bit(WL_STATUS_CONNECTING, &wl->status)) { |
e494632e SS |
2891 | if (completed) { |
2892 | wl_get_assoc_ies(wl); | |
2893 | wl_update_prof(wl, NULL, &e->addr, WL_PROF_BSSID); | |
2894 | wl_update_bss_info(wl); | |
2895 | } | |
cf2b4488 | 2896 | cfg80211_connect_result(ndev, |
e494632e | 2897 | (u8 *)wl_read_prof(wl, WL_PROF_BSSID), |
cf2b4488 HP |
2898 | conn_info->req_ie, |
2899 | conn_info->req_ie_len, | |
2900 | conn_info->resp_ie, | |
2901 | conn_info->resp_ie_len, | |
b3164c71 | 2902 | completed ? WLAN_STATUS_SUCCESS : WLAN_STATUS_AUTH_TIMEOUT, |
2903 | GFP_KERNEL); | |
e494632e SS |
2904 | if (completed) |
2905 | set_bit(WL_STATUS_CONNECTED, &wl->status); | |
1ce4784e | 2906 | WL_CONN("Report connect result - connection %s\n", |
e494632e | 2907 | completed ? "succeeded" : "failed"); |
cf2b4488 | 2908 | } |
1ce4784e | 2909 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
2910 | return err; |
2911 | } | |
2912 | ||
3e26416e | 2913 | static s32 |
cf2b4488 HP |
2914 | wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, |
2915 | const wl_event_msg_t *e, void *data) | |
2916 | { | |
628f10ba | 2917 | u16 flags = be16_to_cpu(e->flags); |
cf2b4488 HP |
2918 | enum nl80211_key_type key_type; |
2919 | ||
2920 | rtnl_lock(); | |
2921 | if (flags & WLC_EVENT_MSG_GROUP) | |
2922 | key_type = NL80211_KEYTYPE_GROUP; | |
2923 | else | |
2924 | key_type = NL80211_KEYTYPE_PAIRWISE; | |
2925 | ||
3fd79f7c | 2926 | cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1, |
cf2b4488 HP |
2927 | NULL, GFP_KERNEL); |
2928 | rtnl_unlock(); | |
2929 | ||
2930 | return 0; | |
2931 | } | |
2932 | ||
3e26416e | 2933 | static s32 |
cf2b4488 HP |
2934 | wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, |
2935 | const wl_event_msg_t *e, void *data) | |
2936 | { | |
2937 | struct channel_info channel_inform; | |
2938 | struct wl_scan_results *bss_list; | |
66cbd3ab | 2939 | u32 len = WL_SCAN_BUF_MAX; |
3e26416e | 2940 | s32 err = 0; |
9446af06 | 2941 | bool scan_abort = false; |
cf2b4488 | 2942 | |
1ce4784e SS |
2943 | WL_TRACE("Enter\n"); |
2944 | ||
2945 | if (wl->iscan_on && wl->iscan_kickstart) { | |
2946 | WL_TRACE("Exit\n"); | |
cf2b4488 | 2947 | return wl_wakeup_iscan(wl_to_iscan(wl)); |
1ce4784e | 2948 | } |
cf2b4488 HP |
2949 | |
2950 | if (unlikely(!test_and_clear_bit(WL_STATUS_SCANNING, &wl->status))) { | |
f4528696 | 2951 | WL_ERR("Scan complete while device not scanning\n"); |
9446af06 SS |
2952 | scan_abort = true; |
2953 | err = -EINVAL; | |
2954 | goto scan_done_out; | |
cf2b4488 | 2955 | } |
9446af06 | 2956 | |
76c06459 JC |
2957 | err = wl_dev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, |
2958 | sizeof(channel_inform)); | |
2959 | if (unlikely(err)) { | |
f4528696 | 2960 | WL_ERR("scan busy (%d)\n", err); |
9446af06 | 2961 | scan_abort = true; |
cf2b4488 HP |
2962 | goto scan_done_out; |
2963 | } | |
29750b90 | 2964 | channel_inform.scan_channel = le32_to_cpu(channel_inform.scan_channel); |
cf2b4488 HP |
2965 | if (unlikely(channel_inform.scan_channel)) { |
2966 | ||
1ce4784e | 2967 | WL_CONN("channel_inform.scan_channel (%d)\n", |
f4528696 | 2968 | channel_inform.scan_channel); |
cf2b4488 HP |
2969 | } |
2970 | wl->bss_list = wl->scan_results; | |
2971 | bss_list = wl->bss_list; | |
2972 | memset(bss_list, 0, len); | |
29750b90 | 2973 | bss_list->buflen = cpu_to_le32(len); |
9446af06 | 2974 | |
76c06459 JC |
2975 | err = wl_dev_ioctl(ndev, WLC_SCAN_RESULTS, bss_list, len); |
2976 | if (unlikely(err)) { | |
f4528696 | 2977 | WL_ERR("%s Scan_results error (%d)\n", ndev->name, err); |
cf2b4488 | 2978 | err = -EINVAL; |
9446af06 | 2979 | scan_abort = true; |
cf2b4488 HP |
2980 | goto scan_done_out; |
2981 | } | |
29750b90 SF |
2982 | bss_list->buflen = le32_to_cpu(bss_list->buflen); |
2983 | bss_list->version = le32_to_cpu(bss_list->version); | |
2984 | bss_list->count = le32_to_cpu(bss_list->count); | |
cf2b4488 | 2985 | |
76c06459 | 2986 | err = wl_inform_bss(wl); |
9446af06 SS |
2987 | if (err) { |
2988 | scan_abort = true; | |
cf2b4488 | 2989 | goto scan_done_out; |
9446af06 | 2990 | } |
cf2b4488 HP |
2991 | |
2992 | scan_done_out: | |
2993 | if (wl->scan_request) { | |
1ce4784e | 2994 | WL_SCAN("calling cfg80211_scan_done\n"); |
9446af06 | 2995 | cfg80211_scan_done(wl->scan_request, scan_abort); |
fb693a71 | 2996 | wl_set_mpc(ndev, 1); |
cf2b4488 HP |
2997 | wl->scan_request = NULL; |
2998 | } | |
1ce4784e SS |
2999 | |
3000 | WL_TRACE("Exit\n"); | |
3001 | ||
cf2b4488 HP |
3002 | return err; |
3003 | } | |
3004 | ||
3005 | static void wl_init_conf(struct wl_conf *conf) | |
3006 | { | |
66cbd3ab GKH |
3007 | conf->mode = (u32)-1; |
3008 | conf->frag_threshold = (u32)-1; | |
3009 | conf->rts_threshold = (u32)-1; | |
3010 | conf->retry_short = (u32)-1; | |
3011 | conf->retry_long = (u32)-1; | |
e9887c9d | 3012 | conf->tx_power = -1; |
cf2b4488 HP |
3013 | } |
3014 | ||
3015 | static void wl_init_prof(struct wl_profile *prof) | |
3016 | { | |
3017 | memset(prof, 0, sizeof(*prof)); | |
3018 | } | |
3019 | ||
3020 | static void wl_init_eloop_handler(struct wl_event_loop *el) | |
3021 | { | |
3022 | memset(el, 0, sizeof(*el)); | |
3023 | el->handler[WLC_E_SCAN_COMPLETE] = wl_notify_scan_status; | |
cf2b4488 | 3024 | el->handler[WLC_E_LINK] = wl_notify_connect_status; |
cf2b4488 HP |
3025 | el->handler[WLC_E_ROAM] = wl_notify_roaming_status; |
3026 | el->handler[WLC_E_MIC_ERROR] = wl_notify_mic_status; | |
b3164c71 | 3027 | el->handler[WLC_E_SET_SSID] = wl_notify_connect_status; |
cf2b4488 HP |
3028 | } |
3029 | ||
3e26416e | 3030 | static s32 wl_init_priv_mem(struct wl_priv *wl) |
cf2b4488 | 3031 | { |
3b785a8c | 3032 | wl->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL); |
cf2b4488 | 3033 | if (unlikely(!wl->scan_results)) { |
f4528696 | 3034 | WL_ERR("Scan results alloc failed\n"); |
cf2b4488 HP |
3035 | goto init_priv_mem_out; |
3036 | } | |
3b785a8c | 3037 | wl->conf = kzalloc(sizeof(*wl->conf), GFP_KERNEL); |
cf2b4488 | 3038 | if (unlikely(!wl->conf)) { |
f4528696 | 3039 | WL_ERR("wl_conf alloc failed\n"); |
cf2b4488 HP |
3040 | goto init_priv_mem_out; |
3041 | } | |
3b785a8c | 3042 | wl->profile = kzalloc(sizeof(*wl->profile), GFP_KERNEL); |
cf2b4488 | 3043 | if (unlikely(!wl->profile)) { |
f4528696 | 3044 | WL_ERR("wl_profile alloc failed\n"); |
cf2b4488 HP |
3045 | goto init_priv_mem_out; |
3046 | } | |
3b785a8c | 3047 | wl->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); |
cf2b4488 | 3048 | if (unlikely(!wl->bss_info)) { |
f4528696 | 3049 | WL_ERR("Bss information alloc failed\n"); |
cf2b4488 HP |
3050 | goto init_priv_mem_out; |
3051 | } | |
3b785a8c | 3052 | wl->scan_req_int = kzalloc(sizeof(*wl->scan_req_int), GFP_KERNEL); |
cf2b4488 | 3053 | if (unlikely(!wl->scan_req_int)) { |
f4528696 | 3054 | WL_ERR("Scan req alloc failed\n"); |
cf2b4488 HP |
3055 | goto init_priv_mem_out; |
3056 | } | |
3b785a8c | 3057 | wl->ioctl_buf = kzalloc(WL_IOCTL_LEN_MAX, GFP_KERNEL); |
cf2b4488 | 3058 | if (unlikely(!wl->ioctl_buf)) { |
f4528696 | 3059 | WL_ERR("Ioctl buf alloc failed\n"); |
cf2b4488 HP |
3060 | goto init_priv_mem_out; |
3061 | } | |
3b785a8c | 3062 | wl->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); |
cf2b4488 | 3063 | if (unlikely(!wl->extra_buf)) { |
f4528696 | 3064 | WL_ERR("Extra buf alloc failed\n"); |
cf2b4488 HP |
3065 | goto init_priv_mem_out; |
3066 | } | |
3b785a8c | 3067 | wl->iscan = kzalloc(sizeof(*wl->iscan), GFP_KERNEL); |
cf2b4488 | 3068 | if (unlikely(!wl->iscan)) { |
f4528696 | 3069 | WL_ERR("Iscan buf alloc failed\n"); |
cf2b4488 HP |
3070 | goto init_priv_mem_out; |
3071 | } | |
3b785a8c | 3072 | wl->fw = kzalloc(sizeof(*wl->fw), GFP_KERNEL); |
cf2b4488 | 3073 | if (unlikely(!wl->fw)) { |
f4528696 | 3074 | WL_ERR("fw object alloc failed\n"); |
cf2b4488 HP |
3075 | goto init_priv_mem_out; |
3076 | } | |
3b785a8c | 3077 | wl->pmk_list = kzalloc(sizeof(*wl->pmk_list), GFP_KERNEL); |
cf2b4488 | 3078 | if (unlikely(!wl->pmk_list)) { |
f4528696 | 3079 | WL_ERR("pmk list alloc failed\n"); |
cf2b4488 HP |
3080 | goto init_priv_mem_out; |
3081 | } | |
3082 | ||
3083 | return 0; | |
3084 | ||
3085 | init_priv_mem_out: | |
3086 | wl_deinit_priv_mem(wl); | |
3087 | ||
3088 | return -ENOMEM; | |
3089 | } | |
3090 | ||
3091 | static void wl_deinit_priv_mem(struct wl_priv *wl) | |
3092 | { | |
3093 | kfree(wl->scan_results); | |
3094 | wl->scan_results = NULL; | |
3095 | kfree(wl->bss_info); | |
3096 | wl->bss_info = NULL; | |
3097 | kfree(wl->conf); | |
3098 | wl->conf = NULL; | |
3099 | kfree(wl->profile); | |
3100 | wl->profile = NULL; | |
3101 | kfree(wl->scan_req_int); | |
3102 | wl->scan_req_int = NULL; | |
3103 | kfree(wl->ioctl_buf); | |
3104 | wl->ioctl_buf = NULL; | |
3105 | kfree(wl->extra_buf); | |
3106 | wl->extra_buf = NULL; | |
3107 | kfree(wl->iscan); | |
3108 | wl->iscan = NULL; | |
3109 | kfree(wl->fw); | |
3110 | wl->fw = NULL; | |
3111 | kfree(wl->pmk_list); | |
3112 | wl->pmk_list = NULL; | |
3113 | } | |
3114 | ||
3e26416e | 3115 | static s32 wl_create_event_handler(struct wl_priv *wl) |
cf2b4488 HP |
3116 | { |
3117 | sema_init(&wl->event_sync, 0); | |
7716314b JC |
3118 | wl->event_tsk = kthread_run(wl_event_handler, wl, "wl_event_handler"); |
3119 | if (IS_ERR(wl->event_tsk)) { | |
3120 | wl->event_tsk = NULL; | |
f4528696 | 3121 | WL_ERR("failed to create event thread\n"); |
cf2b4488 HP |
3122 | return -ENOMEM; |
3123 | } | |
cf2b4488 HP |
3124 | return 0; |
3125 | } | |
3126 | ||
3127 | static void wl_destroy_event_handler(struct wl_priv *wl) | |
3128 | { | |
7716314b | 3129 | if (wl->event_tsk) { |
7356f429 | 3130 | send_sig(SIGTERM, wl->event_tsk, 1); |
7716314b JC |
3131 | kthread_stop(wl->event_tsk); |
3132 | wl->event_tsk = NULL; | |
cf2b4488 HP |
3133 | } |
3134 | } | |
3135 | ||
3136 | static void wl_term_iscan(struct wl_priv *wl) | |
3137 | { | |
3138 | struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); | |
3139 | ||
7716314b | 3140 | if (wl->iscan_on && iscan->tsk) { |
cf2b4488 | 3141 | iscan->state = WL_ISCAN_STATE_IDLE; |
7356f429 | 3142 | send_sig(SIGTERM, iscan->tsk, 1); |
7716314b JC |
3143 | kthread_stop(iscan->tsk); |
3144 | iscan->tsk = NULL; | |
cf2b4488 HP |
3145 | } |
3146 | } | |
3147 | ||
3148 | static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) | |
3149 | { | |
3150 | struct wl_priv *wl = iscan_to_wl(iscan); | |
fb693a71 | 3151 | struct net_device *ndev = wl_to_ndev(wl); |
cf2b4488 HP |
3152 | |
3153 | if (unlikely(!test_and_clear_bit(WL_STATUS_SCANNING, &wl->status))) { | |
f4528696 | 3154 | WL_ERR("Scan complete while device not scanning\n"); |
cf2b4488 HP |
3155 | return; |
3156 | } | |
3157 | if (likely(wl->scan_request)) { | |
1ce4784e SS |
3158 | WL_SCAN("ISCAN Completed scan: %s\n", |
3159 | aborted ? "Aborted" : "Done"); | |
cf2b4488 | 3160 | cfg80211_scan_done(wl->scan_request, aborted); |
fb693a71 | 3161 | wl_set_mpc(ndev, 1); |
cf2b4488 HP |
3162 | wl->scan_request = NULL; |
3163 | } | |
0965ae88 | 3164 | wl->iscan_kickstart = false; |
cf2b4488 HP |
3165 | } |
3166 | ||
3e26416e | 3167 | static s32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan) |
cf2b4488 HP |
3168 | { |
3169 | if (likely(iscan->state != WL_ISCAN_STATE_IDLE)) { | |
1ce4784e | 3170 | WL_SCAN("wake up iscan\n"); |
cf2b4488 HP |
3171 | up(&iscan->sync); |
3172 | return 0; | |
3173 | } | |
3174 | ||
3175 | return -EIO; | |
3176 | } | |
3177 | ||
3e26416e | 3178 | static s32 |
66cbd3ab | 3179 | wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, |
cf2b4488 HP |
3180 | struct wl_scan_results **bss_list) |
3181 | { | |
3182 | struct wl_iscan_results list; | |
3183 | struct wl_scan_results *results; | |
3184 | struct wl_iscan_results *list_buf; | |
3e26416e | 3185 | s32 err = 0; |
cf2b4488 HP |
3186 | |
3187 | memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX); | |
3188 | list_buf = (struct wl_iscan_results *)iscan->scan_buf; | |
3189 | results = &list_buf->results; | |
3190 | results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; | |
3191 | results->version = 0; | |
3192 | results->count = 0; | |
3193 | ||
3194 | memset(&list, 0, sizeof(list)); | |
29750b90 | 3195 | list.results.buflen = cpu_to_le32(WL_ISCAN_BUF_MAX); |
76c06459 JC |
3196 | err = wl_dev_iovar_getbuf(iscan->dev, "iscanresults", &list, |
3197 | WL_ISCAN_RESULTS_FIXED_SIZE, iscan->scan_buf, | |
3198 | WL_ISCAN_BUF_MAX); | |
3199 | if (unlikely(err)) { | |
f4528696 | 3200 | WL_ERR("error (%d)\n", err); |
cf2b4488 HP |
3201 | return err; |
3202 | } | |
29750b90 SF |
3203 | results->buflen = le32_to_cpu(results->buflen); |
3204 | results->version = le32_to_cpu(results->version); | |
3205 | results->count = le32_to_cpu(results->count); | |
1ce4784e SS |
3206 | WL_SCAN("results->count = %d\n", results->count); |
3207 | WL_SCAN("results->buflen = %d\n", results->buflen); | |
29750b90 | 3208 | *status = le32_to_cpu(list_buf->status); |
cf2b4488 HP |
3209 | *bss_list = results; |
3210 | ||
3211 | return err; | |
3212 | } | |
3213 | ||
3e26416e | 3214 | static s32 wl_iscan_done(struct wl_priv *wl) |
cf2b4488 HP |
3215 | { |
3216 | struct wl_iscan_ctrl *iscan = wl->iscan; | |
3e26416e | 3217 | s32 err = 0; |
cf2b4488 HP |
3218 | |
3219 | iscan->state = WL_ISCAN_STATE_IDLE; | |
3220 | rtnl_lock(); | |
3221 | wl_inform_bss(wl); | |
0965ae88 | 3222 | wl_notify_iscan_complete(iscan, false); |
cf2b4488 HP |
3223 | rtnl_unlock(); |
3224 | ||
3225 | return err; | |
3226 | } | |
3227 | ||
3e26416e | 3228 | static s32 wl_iscan_pending(struct wl_priv *wl) |
cf2b4488 HP |
3229 | { |
3230 | struct wl_iscan_ctrl *iscan = wl->iscan; | |
3e26416e | 3231 | s32 err = 0; |
cf2b4488 HP |
3232 | |
3233 | /* Reschedule the timer */ | |
3234 | mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); | |
3235 | iscan->timer_on = 1; | |
3236 | ||
3237 | return err; | |
3238 | } | |
3239 | ||
3e26416e | 3240 | static s32 wl_iscan_inprogress(struct wl_priv *wl) |
cf2b4488 HP |
3241 | { |
3242 | struct wl_iscan_ctrl *iscan = wl->iscan; | |
3e26416e | 3243 | s32 err = 0; |
cf2b4488 HP |
3244 | |
3245 | rtnl_lock(); | |
3246 | wl_inform_bss(wl); | |
3247 | wl_run_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); | |
3248 | rtnl_unlock(); | |
3249 | /* Reschedule the timer */ | |
3250 | mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); | |
3251 | iscan->timer_on = 1; | |
3252 | ||
3253 | return err; | |
3254 | } | |
3255 | ||
3e26416e | 3256 | static s32 wl_iscan_aborted(struct wl_priv *wl) |
cf2b4488 HP |
3257 | { |
3258 | struct wl_iscan_ctrl *iscan = wl->iscan; | |
3e26416e | 3259 | s32 err = 0; |
cf2b4488 HP |
3260 | |
3261 | iscan->state = WL_ISCAN_STATE_IDLE; | |
3262 | rtnl_lock(); | |
0f0881b0 | 3263 | wl_notify_iscan_complete(iscan, true); |
cf2b4488 HP |
3264 | rtnl_unlock(); |
3265 | ||
3266 | return err; | |
3267 | } | |
3268 | ||
3e26416e | 3269 | static s32 wl_iscan_thread(void *data) |
cf2b4488 HP |
3270 | { |
3271 | struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 }; | |
3272 | struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; | |
3273 | struct wl_priv *wl = iscan_to_wl(iscan); | |
3274 | struct wl_iscan_eloop *el = &iscan->el; | |
66cbd3ab | 3275 | u32 status; |
cf2b4488 HP |
3276 | int err = 0; |
3277 | ||
3278 | sched_setscheduler(current, SCHED_FIFO, ¶m); | |
af737136 | 3279 | allow_signal(SIGTERM); |
cf2b4488 HP |
3280 | status = WL_SCAN_RESULTS_PARTIAL; |
3281 | while (likely(!down_interruptible(&iscan->sync))) { | |
7716314b JC |
3282 | if (kthread_should_stop()) |
3283 | break; | |
cf2b4488 HP |
3284 | if (iscan->timer_on) { |
3285 | del_timer_sync(&iscan->timer); | |
3286 | iscan->timer_on = 0; | |
3287 | } | |
3288 | rtnl_lock(); | |
76c06459 JC |
3289 | err = wl_get_iscan_results(iscan, &status, &wl->bss_list); |
3290 | if (unlikely(err)) { | |
cf2b4488 | 3291 | status = WL_SCAN_RESULTS_ABORTED; |
f4528696 | 3292 | WL_ERR("Abort iscan\n"); |
cf2b4488 HP |
3293 | } |
3294 | rtnl_unlock(); | |
3295 | el->handler[status] (wl); | |
3296 | } | |
3297 | if (iscan->timer_on) { | |
3298 | del_timer_sync(&iscan->timer); | |
3299 | iscan->timer_on = 0; | |
3300 | } | |
1ce4784e | 3301 | WL_SCAN("ISCAN thread terminated\n"); |
cf2b4488 HP |
3302 | |
3303 | return 0; | |
3304 | } | |
3305 | ||
3deea904 | 3306 | static void wl_iscan_timer(unsigned long data) |
cf2b4488 HP |
3307 | { |
3308 | struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; | |
3309 | ||
3310 | if (iscan) { | |
3311 | iscan->timer_on = 0; | |
1ce4784e | 3312 | WL_SCAN("timer expired\n"); |
cf2b4488 HP |
3313 | wl_wakeup_iscan(iscan); |
3314 | } | |
3315 | } | |
3316 | ||
3e26416e | 3317 | static s32 wl_invoke_iscan(struct wl_priv *wl) |
cf2b4488 HP |
3318 | { |
3319 | struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); | |
3320 | int err = 0; | |
3321 | ||
7716314b | 3322 | if (wl->iscan_on && !iscan->tsk) { |
cf2b4488 HP |
3323 | iscan->state = WL_ISCAN_STATE_IDLE; |
3324 | sema_init(&iscan->sync, 0); | |
7716314b JC |
3325 | iscan->tsk = kthread_run(wl_iscan_thread, iscan, "wl_iscan"); |
3326 | if (IS_ERR(iscan->tsk)) { | |
f4528696 | 3327 | WL_ERR("Could not create iscan thread\n"); |
7716314b | 3328 | iscan->tsk = NULL; |
cf2b4488 HP |
3329 | return -ENOMEM; |
3330 | } | |
3331 | } | |
3332 | ||
3333 | return err; | |
3334 | } | |
3335 | ||
3336 | static void wl_init_iscan_eloop(struct wl_iscan_eloop *el) | |
3337 | { | |
3338 | memset(el, 0, sizeof(*el)); | |
3339 | el->handler[WL_SCAN_RESULTS_SUCCESS] = wl_iscan_done; | |
3340 | el->handler[WL_SCAN_RESULTS_PARTIAL] = wl_iscan_inprogress; | |
3341 | el->handler[WL_SCAN_RESULTS_PENDING] = wl_iscan_pending; | |
3342 | el->handler[WL_SCAN_RESULTS_ABORTED] = wl_iscan_aborted; | |
3343 | el->handler[WL_SCAN_RESULTS_NO_MEM] = wl_iscan_aborted; | |
3344 | } | |
3345 | ||
3e26416e | 3346 | static s32 wl_init_iscan(struct wl_priv *wl) |
cf2b4488 HP |
3347 | { |
3348 | struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); | |
3349 | int err = 0; | |
3350 | ||
3351 | if (wl->iscan_on) { | |
3352 | iscan->dev = wl_to_ndev(wl); | |
3353 | iscan->state = WL_ISCAN_STATE_IDLE; | |
3354 | wl_init_iscan_eloop(&iscan->el); | |
3355 | iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS; | |
3356 | init_timer(&iscan->timer); | |
3deea904 | 3357 | iscan->timer.data = (unsigned long) iscan; |
cf2b4488 HP |
3358 | iscan->timer.function = wl_iscan_timer; |
3359 | sema_init(&iscan->sync, 0); | |
7716314b JC |
3360 | iscan->tsk = kthread_run(wl_iscan_thread, iscan, "wl_iscan"); |
3361 | if (IS_ERR(iscan->tsk)) { | |
f4528696 | 3362 | WL_ERR("Could not create iscan thread\n"); |
7716314b | 3363 | iscan->tsk = NULL; |
cf2b4488 HP |
3364 | return -ENOMEM; |
3365 | } | |
3366 | iscan->data = wl; | |
3367 | } | |
3368 | ||
3369 | return err; | |
3370 | } | |
3371 | ||
3372 | static void wl_init_fw(struct wl_fw_ctrl *fw) | |
3373 | { | |
3374 | fw->status = 0; /* init fw loading status. | |
3375 | 0 means nothing was loaded yet */ | |
3376 | } | |
3377 | ||
3e26416e | 3378 | static s32 wl_init_priv(struct wl_priv *wl) |
cf2b4488 HP |
3379 | { |
3380 | struct wiphy *wiphy = wl_to_wiphy(wl); | |
3e26416e | 3381 | s32 err = 0; |
cf2b4488 HP |
3382 | |
3383 | wl->scan_request = NULL; | |
3384 | wl->pwr_save = !!(wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT); | |
0f0881b0 | 3385 | wl->iscan_on = true; /* iscan on & off switch. |
cf2b4488 | 3386 | we enable iscan per default */ |
f49200c3 | 3387 | wl->roam_on = false; /* roam on & off switch. |
cf2b4488 | 3388 | we enable roam per default */ |
cf2b4488 | 3389 | |
0965ae88 | 3390 | wl->iscan_kickstart = false; |
0f0881b0 | 3391 | wl->active_scan = true; /* we do active scan for |
cf2b4488 | 3392 | specific scan per default */ |
0965ae88 | 3393 | wl->dongle_up = false; /* dongle is not up yet */ |
cf2b4488 | 3394 | wl_init_eq(wl); |
76c06459 JC |
3395 | err = wl_init_priv_mem(wl); |
3396 | if (unlikely(err)) | |
cf2b4488 HP |
3397 | return err; |
3398 | if (unlikely(wl_create_event_handler(wl))) | |
3399 | return -ENOMEM; | |
3400 | wl_init_eloop_handler(&wl->el); | |
3401 | mutex_init(&wl->usr_sync); | |
76c06459 JC |
3402 | err = wl_init_iscan(wl); |
3403 | if (unlikely(err)) | |
cf2b4488 HP |
3404 | return err; |
3405 | wl_init_fw(wl->fw); | |
3406 | wl_init_conf(wl->conf); | |
3407 | wl_init_prof(wl->profile); | |
3408 | wl_link_down(wl); | |
3409 | ||
3410 | return err; | |
3411 | } | |
3412 | ||
3413 | static void wl_deinit_priv(struct wl_priv *wl) | |
3414 | { | |
3415 | wl_destroy_event_handler(wl); | |
0965ae88 | 3416 | wl->dongle_up = false; /* dongle down */ |
cf2b4488 HP |
3417 | wl_flush_eq(wl); |
3418 | wl_link_down(wl); | |
3419 | wl_term_iscan(wl); | |
3420 | wl_deinit_priv_mem(wl); | |
3421 | } | |
3422 | ||
3e26416e | 3423 | s32 wl_cfg80211_attach(struct net_device *ndev, void *data) |
cf2b4488 HP |
3424 | { |
3425 | struct wireless_dev *wdev; | |
3426 | struct wl_priv *wl; | |
3427 | struct wl_iface *ci; | |
3e26416e | 3428 | s32 err = 0; |
cf2b4488 HP |
3429 | |
3430 | if (unlikely(!ndev)) { | |
f4528696 | 3431 | WL_ERR("ndev is invalid\n"); |
cf2b4488 HP |
3432 | return -ENODEV; |
3433 | } | |
3434 | wl_cfg80211_dev = kzalloc(sizeof(struct wl_dev), GFP_KERNEL); | |
3435 | if (unlikely(!wl_cfg80211_dev)) { | |
f4528696 | 3436 | WL_ERR("wl_cfg80211_dev is invalid\n"); |
cf2b4488 HP |
3437 | return -ENOMEM; |
3438 | } | |
1ce4784e | 3439 | WL_INFO("func %p\n", wl_cfg80211_get_sdio_func()); |
93ad12cf | 3440 | wdev = wl_alloc_wdev(sizeof(struct wl_iface), &wl_cfg80211_get_sdio_func()->dev); |
310d6052 | 3441 | if (IS_ERR(wdev)) |
cf2b4488 HP |
3442 | return -ENOMEM; |
3443 | ||
3444 | wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); | |
3445 | wl = wdev_to_wl(wdev); | |
3446 | wl->wdev = wdev; | |
3447 | wl->pub = data; | |
3448 | ci = (struct wl_iface *)wl_to_ci(wl); | |
3449 | ci->wl = wl; | |
3450 | ndev->ieee80211_ptr = wdev; | |
fb693a71 | 3451 | SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); |
cf2b4488 | 3452 | wdev->netdev = ndev; |
76c06459 JC |
3453 | err = wl_init_priv(wl); |
3454 | if (unlikely(err)) { | |
f4528696 | 3455 | WL_ERR("Failed to init iwm_priv (%d)\n", err); |
cf2b4488 HP |
3456 | goto cfg80211_attach_out; |
3457 | } | |
3458 | wl_set_drvdata(wl_cfg80211_dev, ci); | |
cf2b4488 HP |
3459 | |
3460 | return err; | |
3461 | ||
3462 | cfg80211_attach_out: | |
3463 | wl_free_wdev(wl); | |
3464 | return err; | |
3465 | } | |
3466 | ||
3467 | void wl_cfg80211_detach(void) | |
3468 | { | |
3469 | struct wl_priv *wl; | |
3470 | ||
3471 | wl = WL_PRIV_GET(); | |
3472 | ||
3473 | wl_deinit_priv(wl); | |
3474 | wl_free_wdev(wl); | |
3475 | wl_set_drvdata(wl_cfg80211_dev, NULL); | |
3476 | kfree(wl_cfg80211_dev); | |
3477 | wl_cfg80211_dev = NULL; | |
3478 | wl_clear_sdio_func(); | |
3479 | } | |
3480 | ||
3481 | static void wl_wakeup_event(struct wl_priv *wl) | |
3482 | { | |
3483 | up(&wl->event_sync); | |
3484 | } | |
3485 | ||
3e26416e | 3486 | static s32 wl_event_handler(void *data) |
cf2b4488 HP |
3487 | { |
3488 | struct wl_priv *wl = (struct wl_priv *)data; | |
3489 | struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 }; | |
3490 | struct wl_event_q *e; | |
3491 | ||
3492 | sched_setscheduler(current, SCHED_FIFO, ¶m); | |
af737136 | 3493 | allow_signal(SIGTERM); |
cf2b4488 | 3494 | while (likely(!down_interruptible(&wl->event_sync))) { |
eeb8e46b | 3495 | if (kthread_should_stop()) |
7716314b | 3496 | break; |
76c06459 JC |
3497 | e = wl_deq_event(wl); |
3498 | if (unlikely(!e)) { | |
f4528696 | 3499 | WL_ERR("event queue empty...\n"); |
cf2b4488 HP |
3500 | BUG(); |
3501 | } | |
1ce4784e | 3502 | WL_INFO("event type (%d)\n", e->etype); |
cf2b4488 HP |
3503 | if (wl->el.handler[e->etype]) { |
3504 | wl->el.handler[e->etype] (wl, wl_to_ndev(wl), &e->emsg, | |
3505 | e->edata); | |
3506 | } else { | |
1ce4784e | 3507 | WL_INFO("Unknown Event (%d): ignoring\n", e->etype); |
cf2b4488 HP |
3508 | } |
3509 | wl_put_event(e); | |
3510 | } | |
1ce4784e | 3511 | WL_INFO("was terminated\n"); |
7716314b | 3512 | return 0; |
cf2b4488 HP |
3513 | } |
3514 | ||
3515 | void | |
3516 | wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) | |
3517 | { | |
628f10ba | 3518 | u32 event_type = be32_to_cpu(e->event_type); |
cf2b4488 | 3519 | struct wl_priv *wl = ndev_to_wl(ndev); |
1ce4784e | 3520 | |
cf2b4488 HP |
3521 | if (likely(!wl_enq_event(wl, event_type, e, data))) |
3522 | wl_wakeup_event(wl); | |
3523 | } | |
3524 | ||
3525 | static void wl_init_eq(struct wl_priv *wl) | |
3526 | { | |
3527 | wl_init_eq_lock(wl); | |
3528 | INIT_LIST_HEAD(&wl->eq_list); | |
3529 | } | |
3530 | ||
3531 | static void wl_flush_eq(struct wl_priv *wl) | |
3532 | { | |
3533 | struct wl_event_q *e; | |
3534 | ||
3535 | wl_lock_eq(wl); | |
3536 | while (!list_empty(&wl->eq_list)) { | |
3537 | e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); | |
3538 | list_del(&e->eq_list); | |
3539 | kfree(e); | |
3540 | } | |
3541 | wl_unlock_eq(wl); | |
3542 | } | |
3543 | ||
3544 | /* | |
3545 | * retrieve first queued event from head | |
3546 | */ | |
3547 | ||
3548 | static struct wl_event_q *wl_deq_event(struct wl_priv *wl) | |
3549 | { | |
3550 | struct wl_event_q *e = NULL; | |
3551 | ||
3552 | wl_lock_eq(wl); | |
3553 | if (likely(!list_empty(&wl->eq_list))) { | |
3554 | e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); | |
3555 | list_del(&e->eq_list); | |
3556 | } | |
3557 | wl_unlock_eq(wl); | |
3558 | ||
3559 | return e; | |
3560 | } | |
3561 | ||
3562 | /* | |
3563 | ** push event to tail of the queue | |
3564 | */ | |
3565 | ||
3e26416e | 3566 | static s32 |
66cbd3ab | 3567 | wl_enq_event(struct wl_priv *wl, u32 event, const wl_event_msg_t *msg, |
cf2b4488 HP |
3568 | void *data) |
3569 | { | |
3570 | struct wl_event_q *e; | |
3e26416e | 3571 | s32 err = 0; |
cf2b4488 | 3572 | |
76c06459 JC |
3573 | e = kzalloc(sizeof(struct wl_event_q), GFP_KERNEL); |
3574 | if (unlikely(!e)) { | |
f4528696 | 3575 | WL_ERR("event alloc failed\n"); |
cf2b4488 HP |
3576 | return -ENOMEM; |
3577 | } | |
3578 | ||
3579 | e->etype = event; | |
3580 | memcpy(&e->emsg, msg, sizeof(wl_event_msg_t)); | |
3581 | if (data) { | |
3582 | } | |
3583 | wl_lock_eq(wl); | |
3584 | list_add_tail(&e->eq_list, &wl->eq_list); | |
3585 | wl_unlock_eq(wl); | |
3586 | ||
3587 | return err; | |
3588 | } | |
3589 | ||
3590 | static void wl_put_event(struct wl_event_q *e) | |
3591 | { | |
3592 | kfree(e); | |
3593 | } | |
3594 | ||
3595 | void wl_cfg80211_sdio_func(void *func) | |
3596 | { | |
3597 | cfg80211_sdio_func = (struct sdio_func *)func; | |
3598 | } | |
3599 | ||
3600 | static void wl_clear_sdio_func(void) | |
3601 | { | |
3602 | cfg80211_sdio_func = NULL; | |
3603 | } | |
3604 | ||
93ad12cf | 3605 | struct sdio_func *wl_cfg80211_get_sdio_func(void) |
cf2b4488 HP |
3606 | { |
3607 | return cfg80211_sdio_func; | |
3608 | } | |
3609 | ||
3e26416e | 3610 | static s32 wl_dongle_mode(struct net_device *ndev, s32 iftype) |
cf2b4488 | 3611 | { |
3e26416e | 3612 | s32 infra = 0; |
3e26416e | 3613 | s32 err = 0; |
cf2b4488 HP |
3614 | |
3615 | switch (iftype) { | |
3616 | case NL80211_IFTYPE_MONITOR: | |
3617 | case NL80211_IFTYPE_WDS: | |
f4528696 JP |
3618 | WL_ERR("type (%d) : currently we do not support this mode\n", |
3619 | iftype); | |
cf2b4488 HP |
3620 | err = -EINVAL; |
3621 | return err; | |
3622 | case NL80211_IFTYPE_ADHOC: | |
69274f02 | 3623 | infra = 0; |
cf2b4488 HP |
3624 | break; |
3625 | case NL80211_IFTYPE_STATION: | |
3626 | infra = 1; | |
3627 | break; | |
3628 | default: | |
3629 | err = -EINVAL; | |
f4528696 | 3630 | WL_ERR("invalid type (%d)\n", iftype); |
cf2b4488 HP |
3631 | return err; |
3632 | } | |
29750b90 | 3633 | infra = cpu_to_le32(infra); |
76c06459 JC |
3634 | err = wl_dev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra)); |
3635 | if (unlikely(err)) { | |
f4528696 | 3636 | WL_ERR("WLC_SET_INFRA error (%d)\n", err); |
cf2b4488 HP |
3637 | return err; |
3638 | } | |
3639 | ||
69274f02 | 3640 | return 0; |
cf2b4488 HP |
3641 | } |
3642 | ||
3643 | #ifndef EMBEDDED_PLATFORM | |
3e26416e | 3644 | static s32 wl_dongle_country(struct net_device *ndev, u8 ccode) |
cf2b4488 HP |
3645 | { |
3646 | ||
3e26416e | 3647 | s32 err = 0; |
cf2b4488 HP |
3648 | |
3649 | return err; | |
3650 | } | |
3651 | ||
3e26416e | 3652 | static s32 wl_dongle_up(struct net_device *ndev, u32 up) |
cf2b4488 | 3653 | { |
3e26416e | 3654 | s32 err = 0; |
cf2b4488 | 3655 | |
76c06459 JC |
3656 | err = wl_dev_ioctl(ndev, WLC_UP, &up, sizeof(up)); |
3657 | if (unlikely(err)) { | |
f4528696 | 3658 | WL_ERR("WLC_UP error (%d)\n", err); |
cf2b4488 HP |
3659 | } |
3660 | return err; | |
3661 | } | |
3662 | ||
3e26416e | 3663 | static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode) |
cf2b4488 | 3664 | { |
3e26416e | 3665 | s32 err = 0; |
cf2b4488 | 3666 | |
76c06459 JC |
3667 | err = wl_dev_ioctl(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode)); |
3668 | if (unlikely(err)) { | |
f4528696 | 3669 | WL_ERR("WLC_SET_PM error (%d)\n", err); |
cf2b4488 HP |
3670 | } |
3671 | return err; | |
3672 | } | |
3673 | ||
3e26416e | 3674 | static s32 |
66cbd3ab | 3675 | wl_dongle_glom(struct net_device *ndev, u32 glom, u32 dongle_align) |
cf2b4488 | 3676 | { |
562c8850 | 3677 | s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + |
cf2b4488 | 3678 | '\0' + bitvec */ |
3e26416e | 3679 | s32 err = 0; |
cf2b4488 HP |
3680 | |
3681 | /* Match Host and Dongle rx alignment */ | |
3682 | bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, | |
3683 | sizeof(iovbuf)); | |
76c06459 JC |
3684 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); |
3685 | if (unlikely(err)) { | |
f4528696 | 3686 | WL_ERR("txglomalign error (%d)\n", err); |
cf2b4488 HP |
3687 | goto dongle_glom_out; |
3688 | } | |
3689 | /* disable glom option per default */ | |
3690 | bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); | |
76c06459 JC |
3691 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); |
3692 | if (unlikely(err)) { | |
f4528696 | 3693 | WL_ERR("txglom error (%d)\n", err); |
cf2b4488 HP |
3694 | goto dongle_glom_out; |
3695 | } | |
3696 | dongle_glom_out: | |
3697 | return err; | |
3698 | } | |
3699 | ||
3e26416e GKH |
3700 | static s32 |
3701 | wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol) | |
cf2b4488 | 3702 | { |
562c8850 | 3703 | s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + |
cf2b4488 | 3704 | '\0' + bitvec */ |
3e26416e | 3705 | s32 err = 0; |
cf2b4488 HP |
3706 | |
3707 | /* Set ARP offload */ | |
3708 | bcm_mkiovar("arpoe", (char *)&arpoe, 4, iovbuf, sizeof(iovbuf)); | |
76c06459 JC |
3709 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); |
3710 | if (err) { | |
cf2b4488 | 3711 | if (err == -EOPNOTSUPP) |
f4528696 | 3712 | WL_INFO("arpoe is not supported\n"); |
cf2b4488 | 3713 | else |
f4528696 | 3714 | WL_ERR("arpoe error (%d)\n", err); |
cf2b4488 HP |
3715 | |
3716 | goto dongle_offload_out; | |
3717 | } | |
3718 | bcm_mkiovar("arp_ol", (char *)&arp_ol, 4, iovbuf, sizeof(iovbuf)); | |
76c06459 JC |
3719 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); |
3720 | if (err) { | |
cf2b4488 | 3721 | if (err == -EOPNOTSUPP) |
f4528696 | 3722 | WL_INFO("arp_ol is not supported\n"); |
cf2b4488 | 3723 | else |
f4528696 | 3724 | WL_ERR("arp_ol error (%d)\n", err); |
cf2b4488 HP |
3725 | |
3726 | goto dongle_offload_out; | |
3727 | } | |
3728 | ||
3729 | dongle_offload_out: | |
3730 | return err; | |
3731 | } | |
3732 | ||
3e26416e | 3733 | static s32 wl_pattern_atoh(s8 *src, s8 *dst) |
cf2b4488 | 3734 | { |
cf2b4488 HP |
3735 | int i; |
3736 | if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) { | |
f4528696 | 3737 | WL_ERR("Mask invalid format. Needs to start with 0x\n"); |
cf2b4488 HP |
3738 | return -1; |
3739 | } | |
3740 | src = src + 2; /* Skip past 0x */ | |
3741 | if (strlen(src) % 2 != 0) { | |
f4528696 | 3742 | WL_ERR("Mask invalid format. Needs to be of even length\n"); |
cf2b4488 HP |
3743 | return -1; |
3744 | } | |
3745 | for (i = 0; *src != '\0'; i++) { | |
3746 | char num[3]; | |
3747 | strncpy(num, src, 2); | |
3748 | num[2] = '\0'; | |
d5642d3b | 3749 | dst[i] = (u8) simple_strtoul(num, NULL, 16); |
cf2b4488 HP |
3750 | src += 2; |
3751 | } | |
3752 | return i; | |
3753 | } | |
3754 | ||
3e26416e | 3755 | static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode) |
cf2b4488 | 3756 | { |
562c8850 | 3757 | s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + |
cf2b4488 | 3758 | '\0' + bitvec */ |
562c8850 | 3759 | const s8 *str; |
cf2b4488 HP |
3760 | struct wl_pkt_filter pkt_filter; |
3761 | struct wl_pkt_filter *pkt_filterp; | |
3e26416e GKH |
3762 | s32 buf_len; |
3763 | s32 str_len; | |
66cbd3ab GKH |
3764 | u32 mask_size; |
3765 | u32 pattern_size; | |
562c8850 | 3766 | s8 buf[256]; |
3e26416e | 3767 | s32 err = 0; |
cf2b4488 HP |
3768 | |
3769 | /* add a default packet filter pattern */ | |
3770 | str = "pkt_filter_add"; | |
3771 | str_len = strlen(str); | |
3772 | strncpy(buf, str, str_len); | |
3773 | buf[str_len] = '\0'; | |
3774 | buf_len = str_len + 1; | |
3775 | ||
3776 | pkt_filterp = (struct wl_pkt_filter *)(buf + str_len + 1); | |
3777 | ||
3778 | /* Parse packet filter id. */ | |
29750b90 | 3779 | pkt_filter.id = cpu_to_le32(100); |
cf2b4488 HP |
3780 | |
3781 | /* Parse filter polarity. */ | |
29750b90 | 3782 | pkt_filter.negate_match = cpu_to_le32(0); |
cf2b4488 HP |
3783 | |
3784 | /* Parse filter type. */ | |
29750b90 | 3785 | pkt_filter.type = cpu_to_le32(0); |
cf2b4488 HP |
3786 | |
3787 | /* Parse pattern filter offset. */ | |
29750b90 | 3788 | pkt_filter.u.pattern.offset = cpu_to_le32(0); |
cf2b4488 HP |
3789 | |
3790 | /* Parse pattern filter mask. */ | |
29750b90 SF |
3791 | mask_size = cpu_to_le32(wl_pattern_atoh("0xff", |
3792 | (char *)pkt_filterp->u.pattern. | |
3793 | mask_and_pattern)); | |
cf2b4488 HP |
3794 | |
3795 | /* Parse pattern filter pattern. */ | |
29750b90 SF |
3796 | pattern_size = cpu_to_le32(wl_pattern_atoh("0x00", |
3797 | (char *)&pkt_filterp->u. | |
3798 | pattern. | |
3799 | mask_and_pattern | |
3800 | [mask_size])); | |
cf2b4488 HP |
3801 | |
3802 | if (mask_size != pattern_size) { | |
f4528696 | 3803 | WL_ERR("Mask and pattern not the same size\n"); |
cf2b4488 HP |
3804 | err = -EINVAL; |
3805 | goto dongle_filter_out; | |
3806 | } | |
3807 | ||
3808 | pkt_filter.u.pattern.size_bytes = mask_size; | |
3809 | buf_len += WL_PKT_FILTER_FIXED_LEN; | |
3810 | buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); | |
3811 | ||
3812 | /* Keep-alive attributes are set in local | |
3813 | * variable (keep_alive_pkt), and | |
3814 | * then memcpy'ed into buffer (keep_alive_pktp) since there is no | |
3815 | * guarantee that the buffer is properly aligned. | |
3816 | */ | |
3817 | memcpy((char *)pkt_filterp, &pkt_filter, | |
3818 | WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); | |
3819 | ||
76c06459 JC |
3820 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, buf, buf_len); |
3821 | if (err) { | |
cf2b4488 | 3822 | if (err == -EOPNOTSUPP) { |
f4528696 | 3823 | WL_INFO("filter not supported\n"); |
cf2b4488 | 3824 | } else { |
f4528696 | 3825 | WL_ERR("filter (%d)\n", err); |
cf2b4488 HP |
3826 | } |
3827 | goto dongle_filter_out; | |
3828 | } | |
3829 | ||
3830 | /* set mode to allow pattern */ | |
3831 | bcm_mkiovar("pkt_filter_mode", (char *)&filter_mode, 4, iovbuf, | |
3832 | sizeof(iovbuf)); | |
76c06459 JC |
3833 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); |
3834 | if (err) { | |
cf2b4488 | 3835 | if (err == -EOPNOTSUPP) { |
f4528696 | 3836 | WL_INFO("filter_mode not supported\n"); |
cf2b4488 | 3837 | } else { |
f4528696 | 3838 | WL_ERR("filter_mode (%d)\n", err); |
cf2b4488 HP |
3839 | } |
3840 | goto dongle_filter_out; | |
3841 | } | |
3842 | ||
3843 | dongle_filter_out: | |
3844 | return err; | |
3845 | } | |
3846 | #endif /* !EMBEDDED_PLATFORM */ | |
3847 | ||
e494632e SS |
3848 | static s32 wl_dongle_eventmsg(struct net_device *ndev) |
3849 | { | |
3850 | s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + | |
3851 | '\0' + bitvec */ | |
3852 | s8 eventmask[WL_EVENTING_MASK_LEN]; | |
3853 | s32 err = 0; | |
3854 | ||
1ce4784e SS |
3855 | WL_TRACE("Enter\n"); |
3856 | ||
e494632e SS |
3857 | /* Setup event_msgs */ |
3858 | bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, | |
3859 | sizeof(iovbuf)); | |
3860 | err = wl_dev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf)); | |
3861 | if (unlikely(err)) { | |
3862 | WL_ERR("Get event_msgs error (%d)\n", err); | |
3863 | goto dongle_eventmsg_out; | |
3864 | } | |
3865 | memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); | |
3866 | ||
3867 | setbit(eventmask, WLC_E_SET_SSID); | |
3868 | setbit(eventmask, WLC_E_ROAM); | |
3869 | setbit(eventmask, WLC_E_PRUNE); | |
3870 | setbit(eventmask, WLC_E_AUTH); | |
3871 | setbit(eventmask, WLC_E_REASSOC); | |
3872 | setbit(eventmask, WLC_E_REASSOC_IND); | |
3873 | setbit(eventmask, WLC_E_DEAUTH_IND); | |
3874 | setbit(eventmask, WLC_E_DISASSOC_IND); | |
3875 | setbit(eventmask, WLC_E_DISASSOC); | |
3876 | setbit(eventmask, WLC_E_JOIN); | |
3877 | setbit(eventmask, WLC_E_ASSOC_IND); | |
3878 | setbit(eventmask, WLC_E_PSK_SUP); | |
3879 | setbit(eventmask, WLC_E_LINK); | |
3880 | setbit(eventmask, WLC_E_NDIS_LINK); | |
3881 | setbit(eventmask, WLC_E_MIC_ERROR); | |
3882 | setbit(eventmask, WLC_E_PMKID_CACHE); | |
3883 | setbit(eventmask, WLC_E_TXFAIL); | |
3884 | setbit(eventmask, WLC_E_JOIN_START); | |
3885 | setbit(eventmask, WLC_E_SCAN_COMPLETE); | |
3886 | ||
3887 | bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, | |
3888 | sizeof(iovbuf)); | |
3889 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); | |
3890 | if (unlikely(err)) { | |
3891 | WL_ERR("Set event_msgs error (%d)\n", err); | |
3892 | goto dongle_eventmsg_out; | |
3893 | } | |
3894 | ||
3895 | dongle_eventmsg_out: | |
1ce4784e | 3896 | WL_TRACE("Exit\n"); |
e494632e SS |
3897 | return err; |
3898 | } | |
3899 | ||
a1e962b6 SS |
3900 | static s32 |
3901 | wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) | |
3902 | { | |
3903 | s8 iovbuf[32]; | |
3904 | s32 roamtrigger[2]; | |
3905 | s32 roam_delta[2]; | |
3906 | s32 err = 0; | |
3907 | ||
3908 | /* | |
3909 | * Setup timeout if Beacons are lost and roam is | |
3910 | * off to report link down | |
3911 | */ | |
3912 | if (roamvar) { | |
3913 | bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, | |
3914 | sizeof(bcn_timeout), iovbuf, sizeof(iovbuf)); | |
3915 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); | |
3916 | if (unlikely(err)) { | |
3917 | WL_ERR("bcn_timeout error (%d)\n", err); | |
3918 | goto dongle_rom_out; | |
3919 | } | |
3920 | } | |
3921 | ||
3922 | /* | |
3923 | * Enable/Disable built-in roaming to allow supplicant | |
3924 | * to take care of roaming | |
3925 | */ | |
3926 | WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On"); | |
3927 | bcm_mkiovar("roam_off", (char *)&roamvar, | |
3928 | sizeof(roamvar), iovbuf, sizeof(iovbuf)); | |
3929 | err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); | |
3930 | if (unlikely(err)) { | |
3931 | WL_ERR("roam_off error (%d)\n", err); | |
3932 | goto dongle_rom_out; | |
3933 | } | |
3934 | ||
3935 | roamtrigger[0] = WL_ROAM_TRIGGER_LEVEL; | |
3936 | roamtrigger[1] = WLC_BAND_ALL; | |
3937 | err = wl_dev_ioctl(ndev, WLC_SET_ROAM_TRIGGER, | |
3938 | (void *)roamtrigger, sizeof(roamtrigger)); | |
3939 | if (unlikely(err)) { | |
3940 | WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err); | |
3941 | goto dongle_rom_out; | |
3942 | } | |
3943 | ||
3944 | roam_delta[0] = WL_ROAM_DELTA; | |
3945 | roam_delta[1] = WLC_BAND_ALL; | |
3946 | err = wl_dev_ioctl(ndev, WLC_SET_ROAM_DELTA, | |
3947 | (void *)roam_delta, sizeof(roam_delta)); | |
3948 | if (unlikely(err)) { | |
3949 | WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err); | |
3950 | goto dongle_rom_out; | |
3951 | } | |
3952 | ||
3953 | dongle_rom_out: | |
3954 | return err; | |
3955 | } | |
3956 | ||
e4dd6325 SS |
3957 | static s32 |
3958 | wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, | |
3959 | s32 scan_unassoc_time, s32 scan_passive_time) | |
3960 | { | |
3961 | s32 err = 0; | |
3962 | ||
3963 | err = wl_dev_ioctl(ndev, WLC_SET_SCAN_CHANNEL_TIME, &scan_assoc_time, | |
3964 | sizeof(scan_assoc_time)); | |
3965 | if (err) { | |
3966 | if (err == -EOPNOTSUPP) | |
3967 | WL_INFO("Scan assoc time is not supported\n"); | |
3968 | else | |
3969 | WL_ERR("Scan assoc time error (%d)\n", err); | |
3970 | goto dongle_scantime_out; | |
3971 | } | |
3972 | err = wl_dev_ioctl(ndev, WLC_SET_SCAN_UNASSOC_TIME, &scan_unassoc_time, | |
3973 | sizeof(scan_unassoc_time)); | |
3974 | if (err) { | |
3975 | if (err == -EOPNOTSUPP) | |
3976 | WL_INFO("Scan unassoc time is not supported\n"); | |
3977 | else | |
3978 | WL_ERR("Scan unassoc time error (%d)\n", err); | |
3979 | goto dongle_scantime_out; | |
3980 | } | |
3981 | ||
3982 | err = wl_dev_ioctl(ndev, WLC_SET_SCAN_PASSIVE_TIME, &scan_passive_time, | |
3983 | sizeof(scan_passive_time)); | |
3984 | if (err) { | |
3985 | if (err == -EOPNOTSUPP) | |
3986 | WL_INFO("Scan passive time is not supported\n"); | |
3987 | else | |
3988 | WL_ERR("Scan passive time error (%d)\n", err); | |
3989 | goto dongle_scantime_out; | |
3990 | } | |
3991 | ||
3992 | dongle_scantime_out: | |
3993 | return err; | |
3994 | } | |
3995 | ||
3e26416e | 3996 | s32 wl_config_dongle(struct wl_priv *wl, bool need_lock) |
cf2b4488 HP |
3997 | { |
3998 | #ifndef DHD_SDALIGN | |
3999 | #define DHD_SDALIGN 32 | |
4000 | #endif | |
4001 | struct net_device *ndev; | |
4002 | struct wireless_dev *wdev; | |
3e26416e | 4003 | s32 err = 0; |
cf2b4488 HP |
4004 | |
4005 | if (wl->dongle_up) | |
4006 | return err; | |
4007 | ||
4008 | ndev = wl_to_ndev(wl); | |
4009 | wdev = ndev->ieee80211_ptr; | |
4010 | if (need_lock) | |
4011 | rtnl_lock(); | |
4012 | ||
4013 | #ifndef EMBEDDED_PLATFORM | |
76c06459 JC |
4014 | err = wl_dongle_up(ndev, 0); |
4015 | if (unlikely(err)) | |
cf2b4488 | 4016 | goto default_conf_out; |
76c06459 JC |
4017 | err = wl_dongle_country(ndev, 0); |
4018 | if (unlikely(err)) | |
cf2b4488 | 4019 | goto default_conf_out; |
76c06459 JC |
4020 | err = wl_dongle_power(ndev, PM_FAST); |
4021 | if (unlikely(err)) | |
cf2b4488 | 4022 | goto default_conf_out; |
76c06459 | 4023 | err = wl_dongle_glom(ndev, 0, DHD_SDALIGN); |
76c06459 | 4024 | if (unlikely(err)) |
cf2b4488 | 4025 | goto default_conf_out; |
e4dd6325 | 4026 | |
cf2b4488 HP |
4027 | wl_dongle_offload(ndev, 1, 0xf); |
4028 | wl_dongle_filter(ndev, 1); | |
e4dd6325 SS |
4029 | #endif /* !EMBEDDED_PLATFORM */ |
4030 | ||
4031 | wl_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME, | |
4032 | WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); | |
cf2b4488 | 4033 | |
e494632e | 4034 | err = wl_dongle_eventmsg(ndev); |
a1e962b6 SS |
4035 | if (unlikely(err)) |
4036 | goto default_conf_out; | |
4037 | err = wl_dongle_roam(ndev, (wl->roam_on ? 0 : 1), WL_BEACON_TIMEOUT); | |
e494632e SS |
4038 | if (unlikely(err)) |
4039 | goto default_conf_out; | |
cf2b4488 HP |
4040 | err = wl_dongle_mode(ndev, wdev->iftype); |
4041 | if (unlikely(err && err != -EINPROGRESS)) | |
4042 | goto default_conf_out; | |
76c06459 JC |
4043 | err = wl_dongle_probecap(wl); |
4044 | if (unlikely(err)) | |
cf2b4488 HP |
4045 | goto default_conf_out; |
4046 | ||
4047 | /* -EINPROGRESS: Call commit handler */ | |
4048 | ||
4049 | default_conf_out: | |
4050 | if (need_lock) | |
4051 | rtnl_unlock(); | |
4052 | ||
0f0881b0 | 4053 | wl->dongle_up = true; |
cf2b4488 HP |
4054 | |
4055 | return err; | |
4056 | ||
4057 | } | |
4058 | ||
3e26416e | 4059 | static s32 wl_update_wiphybands(struct wl_priv *wl) |
cf2b4488 HP |
4060 | { |
4061 | struct wiphy *wiphy; | |
3e26416e | 4062 | s32 phy_list; |
562c8850 | 4063 | s8 phy; |
3e26416e | 4064 | s32 err = 0; |
cf2b4488 | 4065 | |
76c06459 JC |
4066 | err = wl_dev_ioctl(wl_to_ndev(wl), WLC_GET_PHYLIST, &phy_list, |
4067 | sizeof(phy_list)); | |
4068 | if (unlikely(err)) { | |
f4528696 | 4069 | WL_ERR("error (%d)\n", err); |
cf2b4488 HP |
4070 | return err; |
4071 | } | |
4072 | ||
4073 | phy = ((char *)&phy_list)[1]; | |
1ce4784e | 4074 | WL_INFO("%c phy\n", phy); |
cf2b4488 HP |
4075 | if (phy == 'n' || phy == 'a') { |
4076 | wiphy = wl_to_wiphy(wl); | |
4077 | wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n; | |
4078 | } | |
4079 | ||
4080 | return err; | |
4081 | } | |
4082 | ||
3e26416e | 4083 | static s32 __wl_cfg80211_up(struct wl_priv *wl) |
cf2b4488 | 4084 | { |
3e26416e | 4085 | s32 err = 0; |
cf2b4488 | 4086 | |
65dd4892 SS |
4087 | set_bit(WL_STATUS_READY, &wl->status); |
4088 | ||
cd389a34 | 4089 | wl_debugfs_add_netdev_params(wl); |
4090 | ||
0965ae88 | 4091 | err = wl_config_dongle(wl, false); |
76c06459 | 4092 | if (unlikely(err)) |
cf2b4488 HP |
4093 | return err; |
4094 | ||
4095 | wl_invoke_iscan(wl); | |
65dd4892 | 4096 | |
cf2b4488 HP |
4097 | return err; |
4098 | } | |
4099 | ||
3e26416e | 4100 | static s32 __wl_cfg80211_down(struct wl_priv *wl) |
cf2b4488 | 4101 | { |
cf2b4488 HP |
4102 | set_bit(WL_STATUS_SCAN_ABORTING, &wl->status); |
4103 | wl_term_iscan(wl); | |
4104 | if (wl->scan_request) { | |
65dd4892 SS |
4105 | cfg80211_scan_done(wl->scan_request, true); |
4106 | /* May need to perform this to cover rmmod */ | |
4107 | /* wl_set_mpc(wl_to_ndev(wl), 1); */ | |
cf2b4488 HP |
4108 | wl->scan_request = NULL; |
4109 | } | |
4110 | clear_bit(WL_STATUS_READY, &wl->status); | |
4111 | clear_bit(WL_STATUS_SCANNING, &wl->status); | |
4112 | clear_bit(WL_STATUS_SCAN_ABORTING, &wl->status); | |
65dd4892 | 4113 | clear_bit(WL_STATUS_CONNECTING, &wl->status); |
cf2b4488 HP |
4114 | clear_bit(WL_STATUS_CONNECTED, &wl->status); |
4115 | ||
cd389a34 | 4116 | wl_debugfs_remove_netdev(wl); |
4117 | ||
65dd4892 | 4118 | return 0; |
cf2b4488 HP |
4119 | } |
4120 | ||
3e26416e | 4121 | s32 wl_cfg80211_up(void) |
cf2b4488 HP |
4122 | { |
4123 | struct wl_priv *wl; | |
3e26416e | 4124 | s32 err = 0; |
cf2b4488 HP |
4125 | |
4126 | wl = WL_PRIV_GET(); | |
4127 | mutex_lock(&wl->usr_sync); | |
4128 | err = __wl_cfg80211_up(wl); | |
4129 | mutex_unlock(&wl->usr_sync); | |
4130 | ||
4131 | return err; | |
4132 | } | |
4133 | ||
3e26416e | 4134 | s32 wl_cfg80211_down(void) |
cf2b4488 HP |
4135 | { |
4136 | struct wl_priv *wl; | |
3e26416e | 4137 | s32 err = 0; |
cf2b4488 HP |
4138 | |
4139 | wl = WL_PRIV_GET(); | |
4140 | mutex_lock(&wl->usr_sync); | |
4141 | err = __wl_cfg80211_down(wl); | |
4142 | mutex_unlock(&wl->usr_sync); | |
4143 | ||
4144 | return err; | |
4145 | } | |
4146 | ||
3e26416e | 4147 | static s32 wl_dongle_probecap(struct wl_priv *wl) |
cf2b4488 | 4148 | { |
3e26416e | 4149 | s32 err = 0; |
cf2b4488 | 4150 | |
76c06459 JC |
4151 | err = wl_update_wiphybands(wl); |
4152 | if (unlikely(err)) | |
cf2b4488 HP |
4153 | return err; |
4154 | ||
4155 | return err; | |
4156 | } | |
4157 | ||
3e26416e | 4158 | static void *wl_read_prof(struct wl_priv *wl, s32 item) |
cf2b4488 HP |
4159 | { |
4160 | switch (item) { | |
4161 | case WL_PROF_SEC: | |
4162 | return &wl->profile->sec; | |
cf2b4488 HP |
4163 | case WL_PROF_BSSID: |
4164 | return &wl->profile->bssid; | |
4165 | case WL_PROF_SSID: | |
4166 | return &wl->profile->ssid; | |
4167 | } | |
f4528696 | 4168 | WL_ERR("invalid item (%d)\n", item); |
cf2b4488 HP |
4169 | return NULL; |
4170 | } | |
4171 | ||
3e26416e | 4172 | static s32 |
cf2b4488 | 4173 | wl_update_prof(struct wl_priv *wl, const wl_event_msg_t *e, void *data, |
3e26416e | 4174 | s32 item) |
cf2b4488 | 4175 | { |
3e26416e | 4176 | s32 err = 0; |
cf2b4488 HP |
4177 | struct wlc_ssid *ssid; |
4178 | ||
4179 | switch (item) { | |
4180 | case WL_PROF_SSID: | |
4181 | ssid = (wlc_ssid_t *) data; | |
4182 | memset(wl->profile->ssid.SSID, 0, | |
4183 | sizeof(wl->profile->ssid.SSID)); | |
4184 | memcpy(wl->profile->ssid.SSID, ssid->SSID, ssid->SSID_len); | |
4185 | wl->profile->ssid.SSID_len = ssid->SSID_len; | |
4186 | break; | |
4187 | case WL_PROF_BSSID: | |
4188 | if (data) | |
b8d63078 | 4189 | memcpy(wl->profile->bssid, data, ETH_ALEN); |
cf2b4488 | 4190 | else |
b8d63078 | 4191 | memset(wl->profile->bssid, 0, ETH_ALEN); |
cf2b4488 HP |
4192 | break; |
4193 | case WL_PROF_SEC: | |
4194 | memcpy(&wl->profile->sec, data, sizeof(wl->profile->sec)); | |
4195 | break; | |
1e0645c3 | 4196 | case WL_PROF_BEACONINT: |
4197 | wl->profile->beacon_interval = *(u16 *)data; | |
4198 | break; | |
4199 | case WL_PROF_DTIMPERIOD: | |
4200 | wl->profile->dtim_period = *(u8 *)data; | |
cf2b4488 HP |
4201 | break; |
4202 | default: | |
f4528696 | 4203 | WL_ERR("unsupported item (%d)\n", item); |
cf2b4488 HP |
4204 | err = -EOPNOTSUPP; |
4205 | break; | |
4206 | } | |
4207 | ||
4208 | return err; | |
4209 | } | |
4210 | ||
cf2b4488 HP |
4211 | static bool wl_is_ibssmode(struct wl_priv *wl) |
4212 | { | |
4213 | return wl->conf->mode == WL_MODE_IBSS; | |
4214 | } | |
4215 | ||
5dc56c9f | 4216 | static __used s32 wl_add_ie(struct wl_priv *wl, u8 t, u8 l, u8 *v) |
cf2b4488 HP |
4217 | { |
4218 | struct wl_ie *ie = wl_to_ie(wl); | |
3e26416e | 4219 | s32 err = 0; |
cf2b4488 HP |
4220 | |
4221 | if (unlikely(ie->offset + l + 2 > WL_TLV_INFO_MAX)) { | |
f4528696 | 4222 | WL_ERR("ei crosses buffer boundary\n"); |
cf2b4488 HP |
4223 | return -ENOSPC; |
4224 | } | |
4225 | ie->buf[ie->offset] = t; | |
4226 | ie->buf[ie->offset + 1] = l; | |
4227 | memcpy(&ie->buf[ie->offset + 2], v, l); | |
4228 | ie->offset += l + 2; | |
4229 | ||
4230 | return err; | |
4231 | } | |
4232 | ||
cf2b4488 | 4233 | |
cf2b4488 HP |
4234 | static void wl_link_down(struct wl_priv *wl) |
4235 | { | |
e494632e SS |
4236 | struct net_device *dev = NULL; |
4237 | s32 err = 0; | |
cf2b4488 | 4238 | |
1ce4784e | 4239 | WL_TRACE("Enter\n"); |
e494632e SS |
4240 | clear_bit(WL_STATUS_CONNECTED, &wl->status); |
4241 | ||
4242 | if (wl->link_up) { | |
4243 | dev = wl_to_ndev(wl); | |
4244 | WL_INFO("Call WLC_DISASSOC to stop excess roaming\n "); | |
4245 | err = wl_dev_ioctl(dev, WLC_DISASSOC, NULL, 0); | |
4246 | if (unlikely(err)) | |
4247 | WL_ERR("WLC_DISASSOC failed (%d)\n", err); | |
4248 | wl->link_up = false; | |
4249 | } | |
1ce4784e | 4250 | WL_TRACE("Exit\n"); |
cf2b4488 HP |
4251 | } |
4252 | ||
4253 | static void wl_lock_eq(struct wl_priv *wl) | |
4254 | { | |
4255 | spin_lock_irq(&wl->eq_lock); | |
4256 | } | |
4257 | ||
4258 | static void wl_unlock_eq(struct wl_priv *wl) | |
4259 | { | |
4260 | spin_unlock_irq(&wl->eq_lock); | |
4261 | } | |
4262 | ||
4263 | static void wl_init_eq_lock(struct wl_priv *wl) | |
4264 | { | |
4265 | spin_lock_init(&wl->eq_lock); | |
4266 | } | |
4267 | ||
66cbd3ab | 4268 | static void wl_delay(u32 ms) |
cf2b4488 HP |
4269 | { |
4270 | if (ms < 1000 / HZ) { | |
4271 | cond_resched(); | |
4272 | mdelay(ms); | |
4273 | } else { | |
4274 | msleep(ms); | |
4275 | } | |
4276 | } | |
4277 | ||
4278 | static void wl_set_drvdata(struct wl_dev *dev, void *data) | |
4279 | { | |
4280 | dev->driver_data = data; | |
4281 | } | |
4282 | ||
4283 | static void *wl_get_drvdata(struct wl_dev *dev) | |
4284 | { | |
4285 | return dev->driver_data; | |
4286 | } | |
4287 | ||
3e26416e | 4288 | s32 wl_cfg80211_read_fw(s8 *buf, u32 size) |
cf2b4488 HP |
4289 | { |
4290 | const struct firmware *fw_entry; | |
4291 | struct wl_priv *wl; | |
4292 | ||
4293 | wl = WL_PRIV_GET(); | |
4294 | ||
4295 | fw_entry = wl->fw->fw_entry; | |
4296 | ||
4297 | if (fw_entry->size < wl->fw->ptr + size) | |
4298 | size = fw_entry->size - wl->fw->ptr; | |
4299 | ||
4300 | memcpy(buf, &fw_entry->data[wl->fw->ptr], size); | |
4301 | wl->fw->ptr += size; | |
4302 | return size; | |
4303 | } | |
4304 | ||
4305 | void wl_cfg80211_release_fw(void) | |
4306 | { | |
4307 | struct wl_priv *wl; | |
4308 | ||
4309 | wl = WL_PRIV_GET(); | |
4310 | release_firmware(wl->fw->fw_entry); | |
4311 | wl->fw->ptr = 0; | |
4312 | } | |
4313 | ||
562c8850 | 4314 | void *wl_cfg80211_request_fw(s8 *file_name) |
cf2b4488 HP |
4315 | { |
4316 | struct wl_priv *wl; | |
4317 | const struct firmware *fw_entry = NULL; | |
3e26416e | 4318 | s32 err = 0; |
cf2b4488 | 4319 | |
1ce4784e | 4320 | WL_INFO("file name : \"%s\"\n", file_name); |
cf2b4488 HP |
4321 | wl = WL_PRIV_GET(); |
4322 | ||
4323 | if (!test_bit(WL_FW_LOADING_DONE, &wl->fw->status)) { | |
76c06459 JC |
4324 | err = request_firmware(&wl->fw->fw_entry, file_name, |
4325 | &wl_cfg80211_get_sdio_func()->dev); | |
4326 | if (unlikely(err)) { | |
f4528696 | 4327 | WL_ERR("Could not download fw (%d)\n", err); |
cf2b4488 HP |
4328 | goto req_fw_out; |
4329 | } | |
4330 | set_bit(WL_FW_LOADING_DONE, &wl->fw->status); | |
4331 | fw_entry = wl->fw->fw_entry; | |
4332 | if (fw_entry) { | |
1ce4784e | 4333 | WL_INFO("fw size (%zd), data (%p)\n", |
f4528696 | 4334 | fw_entry->size, fw_entry->data); |
cf2b4488 HP |
4335 | } |
4336 | } else if (!test_bit(WL_NVRAM_LOADING_DONE, &wl->fw->status)) { | |
76c06459 JC |
4337 | err = request_firmware(&wl->fw->fw_entry, file_name, |
4338 | &wl_cfg80211_get_sdio_func()->dev); | |
4339 | if (unlikely(err)) { | |
f4528696 | 4340 | WL_ERR("Could not download nvram (%d)\n", err); |
cf2b4488 HP |
4341 | goto req_fw_out; |
4342 | } | |
4343 | set_bit(WL_NVRAM_LOADING_DONE, &wl->fw->status); | |
4344 | fw_entry = wl->fw->fw_entry; | |
4345 | if (fw_entry) { | |
1ce4784e | 4346 | WL_INFO("nvram size (%zd), data (%p)\n", |
f4528696 | 4347 | fw_entry->size, fw_entry->data); |
cf2b4488 HP |
4348 | } |
4349 | } else { | |
1ce4784e | 4350 | WL_INFO("Downloading already done. Nothing to do more\n"); |
cf2b4488 HP |
4351 | err = -EPERM; |
4352 | } | |
4353 | ||
4354 | req_fw_out: | |
4355 | if (unlikely(err)) { | |
4356 | return NULL; | |
4357 | } | |
4358 | wl->fw->ptr = 0; | |
4359 | return (void *)fw_entry->data; | |
4360 | } | |
4361 | ||
562c8850 | 4362 | s8 *wl_cfg80211_get_fwname(void) |
cf2b4488 HP |
4363 | { |
4364 | struct wl_priv *wl; | |
4365 | ||
4366 | wl = WL_PRIV_GET(); | |
4367 | strcpy(wl->fw->fw_name, WL_4329_FW_FILE); | |
4368 | return wl->fw->fw_name; | |
4369 | } | |
4370 | ||
562c8850 | 4371 | s8 *wl_cfg80211_get_nvramname(void) |
cf2b4488 HP |
4372 | { |
4373 | struct wl_priv *wl; | |
4374 | ||
4375 | wl = WL_PRIV_GET(); | |
4376 | strcpy(wl->fw->nvram_name, WL_4329_NVRAM_FILE); | |
4377 | return wl->fw->nvram_name; | |
4378 | } | |
fb693a71 | 4379 | |
4380 | static void wl_set_mpc(struct net_device *ndev, int mpc) | |
4381 | { | |
4382 | s32 err = 0; | |
9446af06 | 4383 | struct wl_priv *wl = ndev_to_wl(ndev); |
fb693a71 | 4384 | |
9446af06 SS |
4385 | if (test_bit(WL_STATUS_READY, &wl->status)) { |
4386 | err = wl_dev_intvar_set(ndev, "mpc", mpc); | |
4387 | if (unlikely(err)) { | |
4388 | WL_ERR("fail to set mpc\n"); | |
4389 | return; | |
4390 | } | |
4391 | WL_INFO("MPC : %d\n", mpc); | |
fb693a71 | 4392 | } |
fb693a71 | 4393 | } |
cd389a34 | 4394 | |
4395 | static int wl_debugfs_add_netdev_params(struct wl_priv *wl) | |
4396 | { | |
4397 | char buf[10+IFNAMSIZ]; | |
4398 | struct dentry *fd; | |
4399 | s32 err = 0; | |
4400 | ||
4401 | sprintf(buf, "netdev:%s", wl_to_ndev(wl)->name); | |
4402 | wl->debugfsdir = debugfs_create_dir(buf, wl_to_wiphy(wl)->debugfsdir); | |
4403 | ||
4404 | fd = debugfs_create_u16("beacon_int", S_IRUGO, wl->debugfsdir, | |
4405 | (u16 *)&wl->profile->beacon_interval); | |
4406 | if (!fd) { | |
4407 | err = -ENOMEM; | |
4408 | goto err_out; | |
4409 | } | |
4410 | ||
4411 | fd = debugfs_create_u8("dtim_period", S_IRUGO, wl->debugfsdir, | |
4412 | (u8 *)&wl->profile->dtim_period); | |
4413 | if (!fd) { | |
4414 | err = -ENOMEM; | |
4415 | goto err_out; | |
4416 | } | |
4417 | ||
4418 | err_out: | |
4419 | return err; | |
4420 | } | |
4421 | ||
4422 | static void wl_debugfs_remove_netdev(struct wl_priv *wl) | |
4423 | { | |
4424 | debugfs_remove_recursive(wl->debugfsdir); | |
4425 | wl->debugfsdir = NULL; | |
4426 | } |