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