summaryrefslogtreecommitdiffstats
path: root/src/wl
diff options
context:
space:
mode:
Diffstat (limited to 'src/wl')
-rw-r--r--src/wl/sys/wl_cfg80211_hybrid.c3022
-rw-r--r--src/wl/sys/wl_cfg80211_hybrid.h231
-rw-r--r--src/wl/sys/wl_dbg.h74
-rw-r--r--src/wl/sys/wl_export.h81
-rw-r--r--src/wl/sys/wl_iw.c2830
-rw-r--r--src/wl/sys/wl_iw.h150
-rw-r--r--src/wl/sys/wl_linux.c3371
-rw-r--r--src/wl/sys/wl_linux.h192
-rw-r--r--src/wl/sys/wlc_ethereal.h129
-rw-r--r--src/wl/sys/wlc_key.h76
-rw-r--r--src/wl/sys/wlc_pub.h927
-rw-r--r--src/wl/sys/wlc_types.h137
-rw-r--r--src/wl/sys/wlc_utils.h51
-rw-r--r--src/wl/sys/wlc_wowl.h86
14 files changed, 11357 insertions, 0 deletions
diff --git a/src/wl/sys/wl_cfg80211_hybrid.c b/src/wl/sys/wl_cfg80211_hybrid.c
new file mode 100644
index 0000000..7b606e0
--- /dev/null
+++ b/src/wl/sys/wl_cfg80211_hybrid.c
@@ -0,0 +1,3022 @@
+/*
+ * Linux-specific portion of Broadcom 802.11abg Networking Device Driver
+ * cfg80211 interface
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wl_cfg80211.c,v 1.1.6.4 2011-02-11 00:22:09 $
+ */
+
+#if defined(USE_CFG80211)
+
+#define LINUX_PORT
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <linux/nl80211.h>
+#include <net/rtnetlink.h>
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <wlioctl.h>
+#include <proto/802.11.h>
+#include <wl_cfg80211_hybrid.h>
+
+#define EVENT_TYPE(e) dtoh32((e)->event_type)
+#define EVENT_FLAGS(e) dtoh16((e)->flags)
+#define EVENT_STATUS(e) dtoh32((e)->status)
+
+#ifdef BCMDBG
+u32 wl_dbg_level = WL_DBG_ERR | WL_DBG_INFO;
+#else
+u32 wl_dbg_level = WL_DBG_ERR;
+#endif
+
+static s32 wl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
+ enum nl80211_iftype type, u32 *flags, struct vif_params *params);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+static s32
+wl_cfg80211_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request);
+#else
+static s32 wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_scan_request *request);
+#endif
+static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed);
+static s32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ibss_params *params);
+static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
+static s32 wl_cfg80211_get_station(struct wiphy *wiphy,
+ struct net_device *dev, u8 *mac, struct station_info *sinfo);
+#else
+static s32 wl_cfg80211_get_station(struct wiphy *wiphy,
+ struct net_device *dev, const u8 *mac, struct station_info *sinfo);
+#endif
+
+static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *dev, bool enabled, s32 timeout);
+static int wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme);
+static s32 wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+static s32
+wl_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, s32 dbm);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
+static s32 wl_cfg80211_set_tx_power(struct wiphy *wiphy,
+ enum nl80211_tx_power_setting type, s32 dbm);
+#else
+static s32 wl_cfg80211_set_tx_power(struct wiphy *wiphy,
+ enum tx_power_setting type, s32 dbm);
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, s32 *dbm);
+#else
+static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm);
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+static s32 wl_cfg80211_config_default_key(struct wiphy *wiphy,
+ struct net_device *dev, u8 key_idx, bool unicast, bool multicast);
+#else
+static s32 wl_cfg80211_config_default_key(struct wiphy *wiphy,
+ struct net_device *dev, u8 key_idx);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+static s32 wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr, struct key_params *params);
+static s32 wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr);
+static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
+ void *cookie, void (*callback) (void *cookie, struct key_params *params));
+#else
+static s32 wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, const u8 *mac_addr, struct key_params *params);
+static s32 wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, const u8 *mac_addr);
+static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, const u8 *mac_addr,
+ void *cookie, void (*callback) (void *cookie, struct key_params *params));
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
+static s32 wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_pmksa *pmksa);
+static s32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_pmksa *pmksa);
+static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev);
+#endif
+
+static s32 wl_create_event_handler(struct wl_cfg80211_priv *wl);
+static void wl_destroy_event_handler(struct wl_cfg80211_priv *wl);
+static s32 wl_event_handler(void *data);
+static void wl_init_eq(struct wl_cfg80211_priv *wl);
+static void wl_flush_eq(struct wl_cfg80211_priv *wl);
+static void wl_lock_eq(struct wl_cfg80211_priv *wl);
+static void wl_unlock_eq(struct wl_cfg80211_priv *wl);
+static void wl_init_eq_lock(struct wl_cfg80211_priv *wl);
+static void wl_init_eloop_handler(struct wl_cfg80211_event_loop *el);
+static struct wl_cfg80211_event_q *wl_deq_event(struct wl_cfg80211_priv *wl);
+static s32 wl_enq_event(struct wl_cfg80211_priv *wl, u32 type,
+ const wl_event_msg_t *msg, void *data);
+static void wl_put_event(struct wl_cfg80211_event_q *e);
+static void wl_wakeup_event(struct wl_cfg80211_priv *wl);
+
+static s32 wl_notify_connect_status(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data);
+static s32 wl_notify_roaming_status(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data);
+static s32 wl_notify_scan_status(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data);
+static s32 wl_bss_connect_done(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data, bool completed);
+static s32 wl_bss_roaming_done(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data);
+static s32 wl_notify_mic_status(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data);
+
+static s32 wl_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf, s32 buf_len);
+static __used s32 wl_dev_bufvar_set(struct net_device *dev, s8 *name, s8 *buf, s32 len);
+static s32 wl_dev_intvar_set(struct net_device *dev, s8 *name, s32 val);
+static s32 wl_dev_intvar_get(struct net_device *dev, s8 *name, s32 *retval);
+static s32 wl_dev_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len);
+
+static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold);
+static s32 wl_set_rts(struct net_device *dev, u32 frag_threshold);
+static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l);
+
+static void wl_init_prof(struct wl_cfg80211_profile *prof);
+
+static s32 wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme);
+static s32 wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme);
+static s32 wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme);
+static s32 wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme);
+static s32 wl_set_set_sharedkey(struct net_device *dev, struct cfg80211_connect_params *sme);
+static s32 wl_get_assoc_ies(struct wl_cfg80211_priv *wl);
+static void wl_ch_to_chanspec(struct ieee80211_channel *chan,
+ struct wl_join_params *join_params, size_t *join_params_size);
+
+static void wl_rst_ie(struct wl_cfg80211_priv *wl);
+static __used s32 wl_add_ie(struct wl_cfg80211_priv *wl, u8 t, u8 l, u8 *v);
+static s32 wl_mrg_ie(struct wl_cfg80211_priv *wl, u8 *ie_stream, u16 ie_size);
+static s32 wl_cp_ie(struct wl_cfg80211_priv *wl, u8 *dst, u16 dst_size);
+static u32 wl_get_ielen(struct wl_cfg80211_priv *wl);
+
+static s32 wl_mode_to_nl80211_iftype(s32 mode);
+
+static s32 wl_alloc_wdev(struct device *dev, struct wireless_dev **rwdev);
+static void wl_free_wdev(struct wl_cfg80211_priv *wl);
+
+static s32 wl_inform_bss(struct wl_cfg80211_priv *wl, struct wl_scan_results *bss_list);
+static s32 wl_inform_single_bss(struct wl_cfg80211_priv *wl, struct wl_bss_info *bi);
+static s32 wl_update_bss_info(struct wl_cfg80211_priv *wl);
+
+static void key_endian_to_device(struct wl_wsec_key *key);
+static void key_endian_to_host(struct wl_wsec_key *key);
+
+static s32 wl_init_priv_mem(struct wl_cfg80211_priv *wl);
+static void wl_deinit_priv_mem(struct wl_cfg80211_priv *wl);
+
+static bool wl_is_ibssmode(struct wl_cfg80211_priv *wl);
+
+static void wl_link_up(struct wl_cfg80211_priv *wl);
+static void wl_link_down(struct wl_cfg80211_priv *wl);
+static s32 wl_set_mode(struct net_device *ndev, s32 iftype);
+
+static void wl_init_conf(struct wl_cfg80211_conf *conf);
+
+static s32 wl_update_wiphybands(struct wl_cfg80211_priv *wl);
+
+static __used s32 wl_update_pmklist(struct net_device *dev,
+ struct wl_cfg80211_pmk_list *pmk_list, s32 err);
+
+#if defined(WL_DBGMSG_ENABLE)
+#define WL_DBG_ESTR_MAX 32
+static s8 wl_dbg_estr[][WL_DBG_ESTR_MAX] = {
+ "SET_SSID", "JOIN", "START", "AUTH", "AUTH_IND",
+ "DEAUTH", "DEAUTH_IND", "ASSOC", "ASSOC_IND", "REASSOC",
+ "REASSOC_IND", "DISASSOC", "DISASSOC_IND", "QUIET_START", "QUIET_END",
+ "BEACON_RX", "LINK", "MIC_ERROR", "NDIS_LINK", "ROAM",
+ "TXFAIL", "PMKID_CACHE", "RETROGRADE_TSF", "PRUNE", "AUTOAUTH",
+ "EAPOL_MSG", "SCAN_COMPLETE", "ADDTS_IND", "DELTS_IND", "BCNSENT_IND",
+ "BCNRX_MSG", "BCNLOST_MSG", "ROAM_PREP", "PFN_NET_FOUND",
+ "PFN_NET_LOST",
+ "RESET_COMPLETE", "JOIN_START", "ROAM_START", "ASSOC_START",
+ "IBSS_ASSOC",
+ "RADIO", "PSM_WATCHDOG",
+ "PROBREQ_MSG",
+ "SCAN_CONFIRM_IND", "PSK_SUP", "COUNTRY_CODE_CHANGED",
+ "EXCEEDED_MEDIUM_TIME", "ICV_ERROR",
+ "UNICAST_DECODE_ERROR", "MULTICAST_DECODE_ERROR", "TRACE",
+ "IF",
+ "RSSI", "PFN_SCAN_COMPLETE", "ACTION_FRAME", "ACTION_FRAME_COMPLETE",
+};
+#endif
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel, _flags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
+#define RATETAB_ENT(_rateid, _flags) \
+ { \
+ .bitrate = RATE_TO_BASE100KBPS(_rateid), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+ }
+
+static struct ieee80211_rate __wl_rates[] = {
+ RATETAB_ENT(DOT11_RATE_1M, 0),
+ RATETAB_ENT(DOT11_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(DOT11_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(DOT11_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATETAB_ENT(DOT11_RATE_6M, 0),
+ RATETAB_ENT(DOT11_RATE_9M, 0),
+ RATETAB_ENT(DOT11_RATE_12M, 0),
+ RATETAB_ENT(DOT11_RATE_18M, 0),
+ RATETAB_ENT(DOT11_RATE_24M, 0),
+ RATETAB_ENT(DOT11_RATE_36M, 0),
+ RATETAB_ENT(DOT11_RATE_48M, 0),
+ RATETAB_ENT(DOT11_RATE_54M, 0),
+};
+
+#define wl_a_rates (__wl_rates + 4)
+#define wl_a_rates_size 8
+#define wl_g_rates (__wl_rates + 0)
+#define wl_g_rates_size 12
+
+static struct ieee80211_channel __wl_2ghz_channels[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel __wl_5ghz_a_channels[] = {
+ CHAN5G(34, 0), CHAN5G(36, 0),
+ CHAN5G(38, 0), CHAN5G(40, 0),
+ CHAN5G(42, 0), CHAN5G(44, 0),
+ CHAN5G(46, 0), CHAN5G(48, 0),
+ CHAN5G(52, 0), CHAN5G(56, 0),
+ CHAN5G(60, 0), CHAN5G(64, 0),
+ CHAN5G(100, 0), CHAN5G(104, 0),
+ CHAN5G(108, 0), CHAN5G(112, 0),
+ CHAN5G(116, 0), CHAN5G(120, 0),
+ CHAN5G(124, 0), CHAN5G(128, 0),
+ CHAN5G(132, 0), CHAN5G(136, 0),
+ CHAN5G(140, 0), CHAN5G(149, 0),
+ CHAN5G(153, 0), CHAN5G(157, 0),
+ CHAN5G(161, 0), CHAN5G(165, 0),
+ CHAN5G(184, 0), CHAN5G(188, 0),
+ CHAN5G(192, 0), CHAN5G(196, 0),
+ CHAN5G(200, 0), CHAN5G(204, 0),
+ CHAN5G(208, 0), CHAN5G(212, 0),
+ CHAN5G(216, 0),
+};
+
+static struct ieee80211_channel __wl_5ghz_n_channels[] = {
+ CHAN5G(32, 0), CHAN5G(34, 0),
+ CHAN5G(36, 0), CHAN5G(38, 0),
+ CHAN5G(40, 0), CHAN5G(42, 0),
+ CHAN5G(44, 0), CHAN5G(46, 0),
+ CHAN5G(48, 0), CHAN5G(50, 0),
+ CHAN5G(52, 0), CHAN5G(54, 0),
+ CHAN5G(56, 0), CHAN5G(58, 0),
+ CHAN5G(60, 0), CHAN5G(62, 0),
+ CHAN5G(64, 0), CHAN5G(66, 0),
+ CHAN5G(68, 0), CHAN5G(70, 0),
+ CHAN5G(72, 0), CHAN5G(74, 0),
+ CHAN5G(76, 0), CHAN5G(78, 0),
+ CHAN5G(80, 0), CHAN5G(82, 0),
+ CHAN5G(84, 0), CHAN5G(86, 0),
+ CHAN5G(88, 0), CHAN5G(90, 0),
+ CHAN5G(92, 0), CHAN5G(94, 0),
+ CHAN5G(96, 0), CHAN5G(98, 0),
+ CHAN5G(100, 0), CHAN5G(102, 0),
+ CHAN5G(104, 0), CHAN5G(106, 0),
+ CHAN5G(108, 0), CHAN5G(110, 0),
+ CHAN5G(112, 0), CHAN5G(114, 0),
+ CHAN5G(116, 0), CHAN5G(118, 0),
+ CHAN5G(120, 0), CHAN5G(122, 0),
+ CHAN5G(124, 0), CHAN5G(126, 0),
+ CHAN5G(128, 0), CHAN5G(130, 0),
+ CHAN5G(132, 0), CHAN5G(134, 0),
+ CHAN5G(136, 0), CHAN5G(138, 0),
+ CHAN5G(140, 0), CHAN5G(142, 0),
+ CHAN5G(144, 0), CHAN5G(145, 0),
+ CHAN5G(146, 0), CHAN5G(147, 0),
+ CHAN5G(148, 0), CHAN5G(149, 0),
+ CHAN5G(150, 0), CHAN5G(151, 0),
+ CHAN5G(152, 0), CHAN5G(153, 0),
+ CHAN5G(154, 0), CHAN5G(155, 0),
+ CHAN5G(156, 0), CHAN5G(157, 0),
+ CHAN5G(158, 0), CHAN5G(159, 0),
+ CHAN5G(160, 0), CHAN5G(161, 0),
+ CHAN5G(162, 0), CHAN5G(163, 0),
+ CHAN5G(164, 0), CHAN5G(165, 0),
+ CHAN5G(166, 0), CHAN5G(168, 0),
+ CHAN5G(170, 0), CHAN5G(172, 0),
+ CHAN5G(174, 0), CHAN5G(176, 0),
+ CHAN5G(178, 0), CHAN5G(180, 0),
+ CHAN5G(182, 0), CHAN5G(184, 0),
+ CHAN5G(186, 0), CHAN5G(188, 0),
+ CHAN5G(190, 0), CHAN5G(192, 0),
+ CHAN5G(194, 0), CHAN5G(196, 0),
+ CHAN5G(198, 0), CHAN5G(200, 0),
+ CHAN5G(202, 0), CHAN5G(204, 0),
+ CHAN5G(206, 0), CHAN5G(208, 0),
+ CHAN5G(210, 0), CHAN5G(212, 0),
+ CHAN5G(214, 0), CHAN5G(216, 0),
+ CHAN5G(218, 0), CHAN5G(220, 0),
+ CHAN5G(222, 0), CHAN5G(224, 0),
+ CHAN5G(226, 0), CHAN5G(228, 0),
+};
+
+static struct ieee80211_supported_band __wl_band_2ghz = {
+ .band = IEEE80211_BAND_2GHZ,
+ .channels = __wl_2ghz_channels,
+ .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
+ .bitrates = wl_g_rates,
+ .n_bitrates = wl_g_rates_size,
+};
+
+static struct ieee80211_supported_band __wl_band_5ghz_a = {
+ .band = IEEE80211_BAND_5GHZ,
+ .channels = __wl_5ghz_a_channels,
+ .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
+ .bitrates = wl_a_rates,
+ .n_bitrates = wl_a_rates_size,
+};
+
+static struct ieee80211_supported_band __wl_band_5ghz_n = {
+ .band = IEEE80211_BAND_5GHZ,
+ .channels = __wl_5ghz_n_channels,
+ .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
+ .bitrates = wl_a_rates,
+ .n_bitrates = wl_a_rates_size,
+};
+
+static const u32 __wl_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC,
+};
+
+static void key_endian_to_device(struct wl_wsec_key *key)
+{
+ key->index = htod32(key->index);
+ key->len = htod32(key->len);
+ key->algo = htod32(key->algo);
+ key->flags = htod32(key->flags);
+ key->rxiv.hi = htod32(key->rxiv.hi);
+ key->rxiv.lo = htod16(key->rxiv.lo);
+ key->iv_initialized = htod32(key->iv_initialized);
+}
+
+static void key_endian_to_host(struct wl_wsec_key *key)
+{
+ key->index = dtoh32(key->index);
+ key->len = dtoh32(key->len);
+ key->algo = dtoh32(key->algo);
+ key->flags = dtoh32(key->flags);
+ key->rxiv.hi = dtoh32(key->rxiv.hi);
+ key->rxiv.lo = dtoh16(key->rxiv.lo);
+ key->iv_initialized = dtoh32(key->iv_initialized);
+}
+
+static s32
+wl_dev_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len)
+{
+ struct ifreq ifr;
+ struct wl_ioctl ioc;
+ mm_segment_t fs;
+ s32 err = 0;
+
+ BUG_ON(len < sizeof(int));
+
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.cmd = cmd;
+ ioc.buf = arg;
+ ioc.len = len;
+ strcpy(ifr.ifr_name, dev->name);
+ ifr.ifr_data = (caddr_t)&ioc;
+
+ fs = get_fs();
+ set_fs(get_ds());
+#if defined(WL_USE_NETDEV_OPS)
+ err = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+#else
+ err = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+#endif
+ set_fs(fs);
+
+ return err;
+}
+
+static s32
+wl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ struct wireless_dev *wdev;
+ s32 infra = 0;
+ s32 ap = 0;
+ s32 err = 0;
+
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_WDS:
+ WL_ERR(("type (%d) : currently we do not support this type\n",
+ type));
+ return -EOPNOTSUPP;
+ case NL80211_IFTYPE_ADHOC:
+ wl->conf->mode = WL_MODE_IBSS;
+ break;
+ case NL80211_IFTYPE_STATION:
+ wl->conf->mode = WL_MODE_BSS;
+ infra = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ infra = htod32(infra);
+ ap = htod32(ap);
+ wdev = ndev->ieee80211_ptr;
+ wdev->iftype = type;
+ WL_DBG(("%s : ap (%d), infra (%d)\n", ndev->name, ap, infra));
+ err = wl_dev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra));
+ if (err) {
+ WL_ERR(("WLC_SET_INFRA error (%d)\n", err));
+ return err;
+ }
+ err = wl_dev_ioctl(ndev, WLC_SET_AP, &ap, sizeof(ap));
+ if (err) {
+ WL_ERR(("WLC_SET_AP error (%d)\n", err));
+ return err;
+ }
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+static s32
+wl_cfg80211_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+#else
+static s32
+wl_cfg80211_scan(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_scan_request *request)
+#endif
+
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct net_device *ndev = request->wdev->netdev;
+#endif
+ struct wl_cfg80211_priv *wl = ndev_to_wl(ndev);
+ struct cfg80211_ssid *ssids;
+ struct wl_cfg80211_scan_req *sr = wl_to_sr(wl);
+ s32 passive_scan;
+ s32 err = 0;
+
+ if (request) {
+ ssids = request->ssids;
+ }
+ else {
+
+ ssids = NULL;
+ }
+ wl->scan_request = request;
+
+ memset(&sr->ssid, 0, sizeof(sr->ssid));
+
+ if (ssids) {
+ WL_DBG(("ssid \"%s\", ssid_len (%d)\n", ssids->ssid, ssids->ssid_len));
+ sr->ssid.SSID_len = min_t(u8, sizeof(sr->ssid.SSID), ssids->ssid_len);
+ }
+
+ if (sr->ssid.SSID_len) {
+ memcpy(sr->ssid.SSID, ssids->ssid, sr->ssid.SSID_len);
+ sr->ssid.SSID_len = htod32(sr->ssid.SSID_len);
+ WL_DBG(("Specific scan ssid=\"%s\" len=%d\n", sr->ssid.SSID, sr->ssid.SSID_len));
+ } else {
+ WL_DBG(("Broadcast scan\n"));
+ }
+ WL_DBG(("sr->ssid.SSID_len (%d)\n", sr->ssid.SSID_len));
+ passive_scan = wl->active_scan ? 0 : 1;
+ err = wl_dev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, &passive_scan, sizeof(passive_scan));
+ if (err) {
+ WL_ERR(("WLC_SET_PASSIVE_SCAN error (%d)\n", err));
+ goto scan_out;
+ }
+ err = wl_dev_ioctl(ndev, WLC_SCAN, &sr->ssid, sizeof(sr->ssid));
+ if (err) {
+ if (err == -EBUSY) {
+ WL_INF(("system busy : scan for \"%s\" "
+ "canceled\n", sr->ssid.SSID));
+ } else {
+ WL_ERR(("WLC_SCAN error (%d)\n", err));
+ }
+ goto scan_out;
+ }
+
+ return 0;
+
+scan_out:
+ wl->scan_request = NULL;
+ return err;
+}
+
+static s32 wl_dev_intvar_set(struct net_device *dev, s8 *name, s32 val)
+{
+ s8 buf[WLC_IOCTL_SMLEN];
+ u32 len;
+ s32 err = 0;
+
+ val = htod32(val);
+ len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf));
+ BUG_ON(!len);
+
+ err = wl_dev_ioctl(dev, WLC_SET_VAR, buf, len);
+ if (err) {
+ WL_ERR(("error (%d)\n", err));
+ }
+
+ return err;
+}
+
+static s32
+wl_dev_intvar_get(struct net_device *dev, s8 *name, s32 *retval)
+{
+ union {
+ s8 buf[WLC_IOCTL_SMLEN];
+ s32 val;
+ } var;
+ u32 len;
+ u32 data_null;
+ s32 err = 0;
+
+ len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf));
+ BUG_ON(!len);
+ err = wl_dev_ioctl(dev, WLC_GET_VAR, &var, len);
+ if (err) {
+ WL_ERR(("error (%d)\n", err));
+ }
+ *retval = dtoh32(var.val);
+
+ return err;
+}
+
+static s32 wl_set_rts(struct net_device *dev, u32 rts_threshold)
+{
+ s32 err = 0;
+
+ err = wl_dev_intvar_set(dev, "rtsthresh", rts_threshold);
+ if (err) {
+ WL_ERR(("Error (%d)\n", err));
+ return err;
+ }
+ return err;
+}
+
+static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold)
+{
+ s32 err = 0;
+
+ err = wl_dev_intvar_set(dev, "fragthresh", frag_threshold);
+ if (err) {
+ WL_ERR(("Error (%d)\n", err));
+ return err;
+ }
+ return err;
+}
+
+static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l)
+{
+ s32 err = 0;
+ u32 cmd = (l ? WLC_SET_LRL : WLC_SET_SRL);
+
+ retry = htod32(retry);
+ err = wl_dev_ioctl(dev, cmd, &retry, sizeof(retry));
+ if (err) {
+ WL_ERR(("cmd (%d) , error (%d)\n", cmd, err));
+ return err;
+ }
+ return err;
+}
+
+static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ struct net_device *ndev = wl_to_ndev(wl);
+ s32 err = 0;
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
+ (wl->conf->rts_threshold != wiphy->rts_threshold)) {
+ wl->conf->rts_threshold = wiphy->rts_threshold;
+ err = wl_set_rts(ndev, wl->conf->rts_threshold);
+ if (!err)
+ return err;
+ }
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
+ (wl->conf->frag_threshold != wiphy->frag_threshold)) {
+ wl->conf->frag_threshold = wiphy->frag_threshold;
+ err = wl_set_frag(ndev, wl->conf->frag_threshold);
+ if (!err)
+ return err;
+ }
+ if (changed & WIPHY_PARAM_RETRY_LONG && (wl->conf->retry_long != wiphy->retry_long)) {
+ wl->conf->retry_long = wiphy->retry_long;
+ err = wl_set_retry(ndev, wl->conf->retry_long, true);
+ if (!err)
+ return err;
+ }
+ if (changed & WIPHY_PARAM_RETRY_SHORT && (wl->conf->retry_short != wiphy->retry_short)) {
+ wl->conf->retry_short = wiphy->retry_short;
+ err = wl_set_retry(ndev, wl->conf->retry_short, false);
+ if (!err) {
+ return err;
+ }
+ }
+
+ return err;
+}
+
+static s32
+wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+{
+ struct wl_join_params join_params;
+ size_t join_params_size;
+ s32 val;
+ s32 err = 0;
+
+ WL_DBG(("\n"));
+
+ if (params->bssid) {
+ WL_ERR(("Invalid bssid\n"));
+ return -EOPNOTSUPP;
+ }
+
+ if ((err = wl_dev_intvar_set(dev, "auth", 0))) {
+ return err;
+ }
+ if ((err = wl_dev_intvar_set(dev, "wpa_auth", WPA_AUTH_NONE))) {
+ return err;
+ }
+ if ((err = wl_dev_intvar_get(dev, "wsec", &val))) {
+ return err;
+ }
+ val &= ~(WEP_ENABLED | TKIP_ENABLED | AES_ENABLED);
+ if ((err = wl_dev_intvar_set(dev, "wsec", val))) {
+ return err;
+ }
+
+ memset(&join_params, 0, sizeof(join_params));
+ join_params_size = sizeof(join_params.ssid);
+
+ memcpy((void *)join_params.ssid.SSID, (void *)params->ssid, params->ssid_len);
+ join_params.ssid.SSID_len = htod32(params->ssid_len);
+ if (params->bssid)
+ memcpy(&join_params.params.bssid, params->bssid, ETHER_ADDR_LEN);
+ else
+ memset(&join_params.params.bssid, 0, ETHER_ADDR_LEN);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ wl_ch_to_chanspec(params->chandef.chan, &join_params, &join_params_size);
+#else
+ wl_ch_to_chanspec(params->channel, &join_params, &join_params_size);
+#endif
+ err = wl_dev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size);
+ if (err) {
+ WL_ERR(("Error (%d)\n", err));
+ return err;
+ }
+ return err;
+}
+
+static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ s32 err = 0;
+
+ WL_DBG(("\n"));
+
+ wl_link_down(wl);
+
+ return err;
+}
+
+static s32
+wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(dev);
+ s32 val = 0;
+ s32 err = 0;
+
+ if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
+ else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+ else
+ val = WPA_AUTH_DISABLED;
+ WL_DBG(("setting wpa_auth to 0x%0x\n", val));
+ err = wl_dev_intvar_set(dev, "wpa_auth", val);
+ if (err) {
+ WL_ERR(("set wpa_auth failed (%d)\n", err));
+ return err;
+ }
+ wl->profile->sec.wpa_versions = sme->crypto.wpa_versions;
+ return err;
+}
+
+static s32
+wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(dev);
+ s32 val = 0;
+ s32 err = 0;
+
+ switch (sme->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ val = 0;
+ WL_DBG(("open system\n"));
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ val = 1;
+ WL_DBG(("shared key\n"));
+ break;
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ val = 2;
+ WL_DBG(("automatic\n"));
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ WL_DBG(("network eap\n"));
+ default:
+ val = 2;
+ WL_ERR(("invalid auth type (%d)\n", sme->auth_type));
+ break;
+ }
+
+ err = wl_dev_intvar_set(dev, "auth", val);
+ if (err) {
+ WL_ERR(("set auth failed (%d)\n", err));
+ return err;
+ }
+
+ wl->profile->sec.auth_type = sme->auth_type;
+ return err;
+}
+
+static s32
+wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(dev);
+ s32 pval = 0;
+ s32 gval = 0;
+ s32 val = 0;
+ s32 err = 0;
+
+ if (sme->crypto.n_ciphers_pairwise) {
+ switch (sme->crypto.ciphers_pairwise[0]) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ pval = WEP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ pval = TKIP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ pval = AES_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ pval = AES_ENABLED;
+ break;
+ default:
+ WL_ERR(("invalid cipher pairwise (%d)\n", sme->crypto.ciphers_pairwise[0]));
+ return -EINVAL;
+ }
+ }
+ if (sme->crypto.cipher_group) {
+ switch (sme->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ gval = WEP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ gval = TKIP_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ gval = AES_ENABLED;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ gval = AES_ENABLED;
+ break;
+ default:
+ WL_ERR(("invalid cipher group (%d)\n", sme->crypto.cipher_group));
+ return -EINVAL;
+ }
+ }
+
+ if ((err = wl_dev_intvar_get(dev, "wsec", &val))) {
+ return err;
+ }
+ val &= ~(WEP_ENABLED | TKIP_ENABLED | AES_ENABLED);
+ val |= pval | gval;
+ WL_DBG(("set wsec to %d\n", val));
+ err = wl_dev_intvar_set(dev, "wsec", val);
+ if (err) {
+ WL_ERR(("error (%d)\n", err));
+ return err;
+ }
+
+ wl->profile->sec.cipher_pairwise = sme->crypto.ciphers_pairwise[0];
+ wl->profile->sec.cipher_group = sme->crypto.cipher_group;
+
+ return err;
+}
+
+static s32
+wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(dev);
+ s32 val = 0;
+ s32 err = 0;
+
+ if (sme->crypto.n_akm_suites) {
+ err = wl_dev_intvar_get(dev, "wpa_auth", &val);
+ if (err) {
+ WL_ERR(("could not get wpa_auth (%d)\n", err));
+ return err;
+ }
+ if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA_AUTH_UNSPECIFIED;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA_AUTH_PSK;
+ break;
+ default:
+ WL_ERR(("invalid cipher group (%d)\n", sme->crypto.cipher_group));
+ return -EINVAL;
+ }
+ } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA2_AUTH_UNSPECIFIED;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA2_AUTH_PSK;
+ break;
+ default:
+ WL_ERR(("invalid cipher group (%d)\n", sme->crypto.cipher_group));
+ return -EINVAL;
+ }
+ }
+
+ WL_DBG(("setting wpa_auth to %d\n", val));
+ err = wl_dev_intvar_set(dev, "wpa_auth", val);
+ if (err) {
+ WL_ERR(("could not set wpa_auth (%d)\n", err));
+ return err;
+ }
+ }
+
+ wl->profile->sec.wpa_auth = sme->crypto.akm_suites[0];
+
+ return err;
+}
+
+static s32
+wl_set_set_sharedkey(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(dev);
+ struct wl_cfg80211_security *sec;
+ struct wl_wsec_key key;
+ s32 err = 0;
+
+ WL_DBG(("key len (%d)\n", sme->key_len));
+ if (sme->key_len) {
+ sec = &wl->profile->sec;
+ WL_DBG(("wpa_versions 0x%x cipher_pairwise 0x%x\n",
+ sec->wpa_versions, sec->cipher_pairwise));
+ if (!(sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) &&
+ (sec->cipher_pairwise &
+ (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104))) {
+ memset(&key, 0, sizeof(key));
+ key.len = (u32) sme->key_len;
+ key.index = (u32) sme->key_idx;
+ if (key.len > sizeof(key.data)) {
+ WL_ERR(("Too long key length (%u)\n", key.len));
+ return -EINVAL;
+ }
+ memcpy(key.data, sme->key, key.len);
+ key.flags = WL_PRIMARY_KEY;
+ switch (sec->cipher_pairwise) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key.algo = CRYPTO_ALGO_WEP1;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key.algo = CRYPTO_ALGO_WEP128;
+ break;
+ default:
+ WL_ERR(("Invalid algorithm (%d)\n",
+ sme->crypto.ciphers_pairwise[0]));
+ return -EINVAL;
+ }
+
+ WL_DBG(("key length (%d) key index (%d) algo (%d)\n", key.len,
+ key.index, key.algo));
+ WL_DBG(("key \"%s\"\n", key.data));
+ key_endian_to_device(&key);
+ err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+ if (err) {
+ WL_ERR(("WLC_SET_KEY error (%d)\n", err));
+ return err;
+ }
+ }
+ }
+ return err;
+}
+
+static s32
+wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ struct wl_join_params join_params;
+ size_t join_params_size;
+ char valc;
+ s32 err = 0;
+
+ if (!sme->ssid) {
+ WL_ERR(("Invalid ssid\n"));
+ return -EOPNOTSUPP;
+ }
+
+ WL_DBG(("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len));
+
+ err = wl_set_auth_type(dev, sme);
+ if (err)
+ return err;
+
+ err = wl_set_wpa_version(dev, sme);
+ if (err)
+ return err;
+
+ err = wl_set_set_cipher(dev, sme);
+ if (err)
+ return err;
+
+ err = wl_set_key_mgmt(dev, sme);
+ if (err)
+ return err;
+
+ err = wl_set_set_sharedkey(dev, sme);
+ if (err)
+ return err;
+
+ valc = 1;
+ wl_dev_bufvar_set(dev, "wsec_restrict", &valc, 1);
+
+ if (sme->bssid) {
+ memcpy(wl->profile->bssid, sme->bssid, ETHER_ADDR_LEN);
+ }
+ else {
+ memset(wl->profile->bssid, 0, ETHER_ADDR_LEN);
+ }
+
+ memset(&join_params, 0, sizeof(join_params));
+ join_params_size = sizeof(join_params.ssid);
+
+ join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), sme->ssid_len);
+ memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len);
+ join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len);
+ memcpy(&join_params.params.bssid, &ether_bcast, ETHER_ADDR_LEN);
+
+ memcpy(wl->profile->ssid.SSID, &join_params.ssid.SSID, join_params.ssid.SSID_len);
+ wl->profile->ssid.SSID_len = join_params.ssid.SSID_len;
+
+ wl_ch_to_chanspec(sme->channel, &join_params, &join_params_size);
+ WL_DBG(("join_param_size %u\n", (unsigned int)join_params_size));
+
+ if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
+ WL_DBG(("ssid \"%s\", len (%d)\n", join_params.ssid.SSID,
+ join_params.ssid.SSID_len));
+ }
+ err = wl_dev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size);
+ if (err) {
+ WL_ERR(("error (%d)\n", err));
+ return err;
+ }
+
+ set_bit(WL_STATUS_CONNECTING, &wl->status);
+
+ return err;
+}
+
+static s32
+wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ scb_val_t scbval;
+ s32 err = 0;
+
+ WL_DBG(("Reason %d\n", reason_code));
+
+ if (wl->profile->active) {
+ scbval.val = reason_code;
+ memcpy(&scbval.ea, &wl->bssid, ETHER_ADDR_LEN);
+ scbval.val = htod32(scbval.val);
+ err = wl_dev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
+ if (err) {
+ WL_ERR(("error (%d)\n", err));
+ return err;
+ }
+ }
+
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+static s32
+wl_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, s32 dbm)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
+static s32
+wl_cfg80211_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, s32 dbm)
+#else
+#define NL80211_TX_POWER_AUTOMATIC TX_POWER_AUTOMATIC
+#define NL80211_TX_POWER_LIMITED TX_POWER_LIMITED
+#define NL80211_TX_POWER_FIXED TX_POWER_FIXED
+static s32
+wl_cfg80211_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type, s32 dbm)
+#endif
+{
+
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ struct net_device *ndev = wl_to_ndev(wl);
+ u16 txpwrmw;
+ s32 err = 0;
+ s32 disable = 0;
+
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ if (dbm < 0) {
+ WL_ERR(("TX_POWER_LIMITTED - dbm is negative\n"));
+ return -EINVAL;
+ }
+ break;
+ case NL80211_TX_POWER_FIXED:
+ if (dbm < 0) {
+ WL_ERR(("TX_POWER_FIXED - dbm is negative..\n"));
+ return -EINVAL;
+ }
+ break;
+ }
+
+ disable = WL_RADIO_SW_DISABLE << 16;
+ disable = htod32(disable);
+ err = wl_dev_ioctl(ndev, WLC_SET_RADIO, &disable, sizeof(disable));
+ if (err) {
+ WL_ERR(("WLC_SET_RADIO error (%d)\n", err));
+ return err;
+ }
+
+ if (dbm > 0xffff)
+ txpwrmw = 0xffff;
+ else
+ txpwrmw = (u16) dbm;
+ err = wl_dev_intvar_set(ndev, "qtxpower", (s32) (bcm_mw_to_qdbm(txpwrmw)));
+ if (err) {
+ WL_ERR(("qtxpower error (%d)\n", err));
+ return err;
+ }
+ wl->conf->tx_power = dbm;
+
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, s32 *dbm)
+#else
+static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
+#endif
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ struct net_device *ndev = wl_to_ndev(wl);
+ s32 txpwrdbm;
+ u8 result;
+ s32 err = 0;
+
+ err = wl_dev_intvar_get(ndev, "qtxpower", &txpwrdbm);
+ if (err) {
+ WL_ERR(("error (%d)\n", err));
+ return err;
+ }
+ result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
+ *dbm = (s32) bcm_qdbm_to_mw(result);
+
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+static s32
+wl_cfg80211_config_default_key(struct wiphy *wiphy,
+ struct net_device *dev, u8 key_idx, bool unicast, bool multicast)
+#else
+static s32
+wl_cfg80211_config_default_key(struct wiphy *wiphy,
+ struct net_device *dev, u8 key_idx)
+#endif
+{
+ u32 index;
+ s32 err = 0;
+
+ WL_DBG(("key index (%d)\n", key_idx));
+
+ index = (u32) key_idx;
+ index = htod32(index);
+ err = wl_dev_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, sizeof(index));
+ if (err) {
+ WL_DBG(("error (%d)\n", err));
+ }
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+static s32
+wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr, struct key_params *params)
+#else
+static s32
+wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, const u8 *mac_addr, struct key_params *params)
+#endif
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(dev);
+ struct wl_wsec_key key;
+ s32 secval, secnew = 0;
+ s32 err = 0;
+
+ WL_DBG(("key index %u len %u\n", (unsigned)key_idx, params->key_len));
+
+ memset(&key, 0, sizeof(key));
+
+ key.index = (u32) key_idx;
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ key.algo = CRYPTO_ALGO_WEP1;
+ secnew = WEP_ENABLED;
+ WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n"));
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key.algo = CRYPTO_ALGO_WEP128;
+ secnew = WEP_ENABLED;
+ WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n"));
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ key.algo = CRYPTO_ALGO_TKIP;
+ secnew = TKIP_ENABLED;
+ WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n"));
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ secnew = AES_ENABLED;
+ WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n"));
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ secnew = AES_ENABLED;
+ WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n"));
+ break;
+ default:
+ WL_ERR(("Invalid cipher (0x%x)\n", params->cipher));
+ return -EINVAL;
+ }
+
+ if (mac_addr) {
+ if (!ETHER_ISMULTI(mac_addr)) {
+ memcpy((char *)&key.ea, (void *)mac_addr, ETHER_ADDR_LEN);
+ }
+ }
+
+ key.len = (u32) params->key_len;
+ if (key.len > sizeof(key.data)) {
+ WL_ERR(("Too long key length (%u)\n", key.len));
+ return -EINVAL;
+ }
+ memcpy(key.data, params->key, key.len);
+
+ if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ u8 keybuf[8];
+ memcpy(keybuf, &key.data[24], sizeof(keybuf));
+ memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
+ memcpy(&key.data[16], keybuf, sizeof(keybuf));
+ }
+
+ if (params->seq_len) {
+ u8 *ivptr;
+ if (params->seq_len != 6) {
+ WL_ERR(("seq_len %d is unexpected, check implementation.\n",
+ params->seq_len));
+ }
+ ivptr = (u8 *) params->seq;
+ key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | (ivptr[3] << 8) | ivptr[2];
+ key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
+ key.iv_initialized = true;
+ }
+
+ key_endian_to_device(&key);
+ if (wl->passive) {
+ schedule();
+ }
+ err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+ if (err) {
+ WL_ERR(("WLC_SET_KEY error (%d)\n", err));
+ return err;
+ }
+
+ if ((err = wl_dev_intvar_get(dev, "wsec", &secval))) {
+ return err;
+ }
+ if (secnew == WEP_ENABLED) {
+ secval &= ~(TKIP_ENABLED | AES_ENABLED);
+ }
+ else {
+ secval &= ~(WEP_ENABLED);
+ }
+ secval |= secnew;
+ WL_DBG(("set wsec to %d\n", secval));
+ err = wl_dev_intvar_set(dev, "wsec", secval);
+ if (err) {
+ WL_ERR(("error (%d)\n", err));
+ return err;
+ }
+
+ if (mac_addr) {
+ wl->profile->sec.cipher_pairwise = params->cipher;
+ }
+ else {
+ wl->profile->sec.cipher_group = params->cipher;
+ }
+
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+static s32
+wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr)
+#else
+static s32
+wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, const u8 *mac_addr)
+#endif
+{
+ struct wl_wsec_key key;
+ s32 err = 0;
+
+ WL_DBG(("key index (%d)\n", key_idx));
+
+ memset(&key, 0, sizeof(key));
+
+ key.index = (u32) key_idx;
+ key.len = 0;
+ if (mac_addr) {
+ if (!ETHER_ISMULTI(mac_addr)) {
+ memcpy((char *)&key.ea, (void *)mac_addr, ETHER_ADDR_LEN);
+ }
+ }
+ key.algo = CRYPTO_ALGO_OFF;
+
+ key_endian_to_device(&key);
+ err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+ if (err) {
+ if (err == -EINVAL) {
+ if (key.index >= DOT11_MAX_DEFAULT_KEYS) {
+
+ WL_DBG(("invalid key index (%d)\n", key_idx));
+ }
+ } else {
+ WL_ERR(("WLC_SET_KEY error (%d)\n", err));
+ }
+ return err;
+ }
+
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+static s32
+wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback) (void *cookie, struct key_params * params))
+#else
+static s32
+wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_idx, const u8 *mac_addr, void *cookie,
+ void (*callback) (void *cookie, struct key_params * params))
+#endif
+{
+ struct key_params params;
+ struct wl_wsec_key key;
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ struct wl_cfg80211_security *sec;
+ s32 wsec;
+ s32 err = 0;
+
+ WL_DBG(("key index (%d)\n", key_idx));
+
+ memset(&params, 0, sizeof(params));
+
+ memset(&key, 0, sizeof(key));
+ key.index = key_idx;
+ key_endian_to_device(&key);
+
+ if ((err = wl_dev_ioctl(dev, WLC_GET_KEY, &key, sizeof(key)))) {
+ return err;
+ }
+ key_endian_to_host(&key);
+
+ params.key_len = (u8) min_t(u8, DOT11_MAX_KEY_SIZE, key.len);
+ memcpy((char *)params.key, key.data, params.key_len);
+
+ if ((err = wl_dev_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec)))) {
+ return err;
+ }
+ wsec = dtoh32(wsec);
+ switch (wsec) {
+ case WEP_ENABLED:
+ sec = &wl->profile->sec;
+ if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
+ params.cipher = WLAN_CIPHER_SUITE_WEP40;
+ WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n"));
+ } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
+ params.cipher = WLAN_CIPHER_SUITE_WEP104;
+ WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n"));
+ }
+ break;
+ case TKIP_ENABLED:
+ params.cipher = WLAN_CIPHER_SUITE_TKIP;
+ WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n"));
+ break;
+ case AES_ENABLED:
+ params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
+ WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n"));
+ break;
+ default:
+ WL_ERR(("Invalid algo (0x%x)\n", wsec));
+ return -EINVAL;
+ }
+
+ callback(cookie, &params);
+ return err;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
+static s32
+wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac, struct station_info *sinfo)
+#else
+static s32
+wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo)
+#endif
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ scb_val_t scb_val;
+ int rssi;
+ s32 rate;
+ s32 err = 0;
+
+ if (memcmp(mac, wl->profile->bssid, ETHER_ADDR_LEN)) {
+ WL_ERR(("Wrong Mac address, mac = %pM profile =%pM\n", mac, wl->profile->bssid));
+ return -ENOENT;
+ }
+
+ err = wl_dev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate));
+ if (err) {
+ WL_DBG(("Could not get rate (%d)\n", err));
+ } else {
+ rate = dtoh32(rate);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+#else
+ sinfo->filled |= STATION_INFO_TX_BITRATE;
+#endif
+ sinfo->txrate.legacy = rate * 5;
+ WL_DBG(("Rate %d Mbps\n", (rate / 2)));
+ }
+
+ if (test_bit(WL_STATUS_CONNECTED, &wl->status)) {
+ memset(&scb_val, 0, sizeof(scb_val));
+ err = wl_dev_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t));
+ if (err) {
+ WL_DBG(("Could not get rssi (%d)\n", err));
+ return err;
+ }
+ rssi = dtoh32(scb_val.val);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+#else
+ sinfo->filled |= STATION_INFO_SIGNAL;
+#endif
+ sinfo->signal = rssi;
+ WL_DBG(("RSSI %d dBm\n", rssi));
+ }
+
+ return err;
+}
+
+static s32
+wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
+ bool enabled, s32 timeout)
+{
+ s32 pm;
+ s32 err = 0;
+
+ pm = enabled ? PM_FAST : PM_OFF;
+ pm = htod32(pm);
+ WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled")));
+ err = wl_dev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
+ if (err) {
+ if (err == -ENODEV)
+ WL_DBG(("net_device is not ready yet\n"));
+ else
+ WL_ERR(("error (%d)\n", err));
+ return err;
+ }
+ return err;
+}
+
+static __used s32
+wl_update_pmklist(struct net_device *dev, struct wl_cfg80211_pmk_list *pmk_list, s32 err)
+{
+ int i, j;
+
+ WL_DBG(("No of elements %d\n", pmk_list->pmkids.npmkid));
+ for (i = 0; i < pmk_list->pmkids.npmkid; i++) {
+ WL_DBG(("PMKID[%d]: %pM =\n", i,
+ &pmk_list->pmkids.pmkid[i].BSSID));
+ for (j = 0; j < WPA2_PMKID_LEN; j++) {
+ WL_DBG(("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]));
+ }
+ }
+ if (!err) {
+ err = wl_dev_bufvar_set(dev, "pmkid_info", (char *)pmk_list, sizeof(*pmk_list));
+ }
+
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
+
+static s32
+wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ s32 err = 0;
+ int i;
+
+ for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++)
+ if (!memcmp(pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, ETHER_ADDR_LEN))
+ break;
+ if (i < WL_NUM_PMKIDS_MAX) {
+ memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, pmksa->bssid, ETHER_ADDR_LEN);
+ memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, pmksa->pmkid, WPA2_PMKID_LEN);
+ if (i == wl->pmk_list->pmkids.npmkid)
+ wl->pmk_list->pmkids.npmkid++;
+ } else {
+ err = -EINVAL;
+ }
+ WL_DBG(("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
+ &wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid].BSSID));
+ for (i = 0; i < WPA2_PMKID_LEN; i++) {
+ WL_DBG(("%02x\n",
+ wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid].PMKID[i]));
+ }
+
+ err = wl_update_pmklist(dev, wl->pmk_list, err);
+
+ return err;
+}
+
+static s32
+wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ struct _pmkid_list pmkid;
+ s32 err = 0;
+ int i;
+
+ memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETHER_ADDR_LEN);
+ memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WPA2_PMKID_LEN);
+
+ WL_DBG(("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
+ &pmkid.pmkid[0].BSSID));
+ for (i = 0; i < WPA2_PMKID_LEN; i++) {
+ WL_DBG(("%02x\n", pmkid.pmkid[0].PMKID[i]));
+ }
+
+ for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++)
+ if (!memcmp(pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, ETHER_ADDR_LEN))
+ break;
+
+ if ((wl->pmk_list->pmkids.npmkid > 0) && (i < wl->pmk_list->pmkids.npmkid)) {
+ memset(&wl->pmk_list->pmkids.pmkid[i], 0, sizeof(pmkid_t));
+ for (; i < (wl->pmk_list->pmkids.npmkid - 1); i++) {
+ memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID,
+ &wl->pmk_list->pmkids.pmkid[i + 1].BSSID, ETHER_ADDR_LEN);
+ memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID,
+ &wl->pmk_list->pmkids.pmkid[i + 1].PMKID, WPA2_PMKID_LEN);
+ }
+ wl->pmk_list->pmkids.npmkid--;
+ } else {
+ err = -EINVAL;
+ }
+
+ err = wl_update_pmklist(dev, wl->pmk_list, err);
+
+ return err;
+
+}
+
+static s32
+wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ s32 err = 0;
+
+ memset(wl->pmk_list, 0, sizeof(*wl->pmk_list));
+ err = wl_update_pmklist(dev, wl->pmk_list, err);
+ return err;
+
+}
+
+#endif
+
+#ifdef CONFIG_PM
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+
+static int
+wl_wowl_ind_wake_reason(struct wl_cfg80211_priv *wl, struct cfg80211_wowlan_wakeup *wakeup)
+{
+ wl_wowl_wakeind_t wowl_ind;
+ s32 err;
+
+ err = wl_dev_bufvar_get(wl_to_ndev(wl), "wowl_wakeind",
+ (s8 *)&wowl_ind, sizeof(wowl_ind));
+ if (err != 0) {
+ WL_ERR(("Unable to get wake reason, err = %d\n", err));
+ return -1;
+ }
+
+ if (wowl_ind.ucode_wakeind == 0) {
+ WL_DBG(("System woke, but not by us\n"));
+ return 0;
+ }
+ WL_DBG(("wake reason is 0x%x\n", wowl_ind.ucode_wakeind));
+
+ if (wowl_ind.ucode_wakeind & WL_WOWL_MAGIC) {
+ WL_ERR(("WOWLAN Woke for: Magic Pkt\n"));
+ wakeup->magic_pkt = true;
+ }
+ if (wowl_ind.ucode_wakeind & WL_WOWL_DIS) {
+ WL_ERR(("WOWLAN Woke for: Disconnect\n"));
+ wakeup->disconnect = true;
+ }
+ if (wowl_ind.ucode_wakeind & WL_WOWL_BCN) {
+ WL_ERR(("WOWLAN Woke for: Beacon Loss\n"));
+ wakeup->disconnect = true;
+ }
+ if (wowl_ind.ucode_wakeind & WL_WOWL_GTK_FAILURE) {
+ WL_ERR(("WOWLAN Woke for: GTK failure\n"));
+ wakeup->gtk_rekey_failure = true;
+ }
+ if (wowl_ind.ucode_wakeind & WL_WOWL_EAPID) {
+ WL_ERR(("WOWLAN Woke for: EAP identify request\n"));
+ wakeup->eap_identity_req = true;
+ }
+ if (wowl_ind.ucode_wakeind & WL_WOWL_M1) {
+ WL_ERR(("WOWLAN Woke for: 4-way handshake request\n"));
+ wakeup->four_way_handshake = true;
+ }
+ return 1;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
+static int
+wl_cfg80211_rekey(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ wlc_rekey_info_t rekey;
+ s32 err;
+
+ if (!wl->offloads) {
+ return 0;
+ }
+
+ memset(&rekey, 0, sizeof(rekey));
+ memcpy(&rekey.kek, data->kek, WLC_KEK_LEN);
+ memcpy(&rekey.kck, data->kck, WLC_KCK_LEN);
+ memcpy(&rekey.replay_counter, data->replay_ctr, WLC_REPLAY_CTR_LEN);
+ WL_INF(("Send down replay counter %x%x%x%x%x%x%x%x\n",
+ rekey.replay_counter[0], rekey.replay_counter[1], rekey.replay_counter[2],
+ rekey.replay_counter[3], rekey.replay_counter[4], rekey.replay_counter[5],
+ rekey.replay_counter[6], rekey.replay_counter[7]));
+
+ err = wl_dev_bufvar_set(wl_to_ndev(wl), "wowl_replay", (s8 *)&rekey, sizeof(rekey));
+ if (err) {
+ WL_ERR(("Error calling wowl_set_key\n"));
+ return err;
+ }
+ return err;
+}
+#endif
+
+static int wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan)
+{
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ uint wowl = 0;
+ s32 err;
+
+ if (!wowlan) {
+ WL_DBG(("No wowlan requested\n"));
+ return 0;
+ }
+ if (!test_bit(WL_STATUS_CONNECTED, &wl->status)) {
+ WL_INF(("No wowl when not associated.\n"));
+ return 0;
+ }
+
+ err = wl_dev_intvar_get(wl_to_ndev(wl), "wowl", &wowl);
+ if (err) {
+ WL_ERR(("Error fetching WOWL %d\n", err));
+ }
+ if (wowlan->disconnect) {
+ WL_INF(("Requesting wake on Disconnect\n"));
+ wowl |= WL_WOWL_DIS | WL_WOWL_BCN;
+ }
+ if (wowlan->magic_pkt) {
+ WL_INF(("Requesting wake on Magic Pkt\n"));
+ wowl |= WL_WOWL_MAGIC;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
+ if (wowlan->gtk_rekey_failure) {
+ WL_INF(("Requesting wake GTK rekey failure Pkt\n"));
+ wowl |= WL_WOWL_GTK_FAILURE;
+ }
+ if (wowlan->four_way_handshake) {
+ WL_INF(("Requesting wake on 4way handshake request\n"));
+ wowl |= WL_WOWL_M1;
+ }
+#endif
+
+ wowl |= WL_WOWL_KEYROT;
+
+ err = wl_dev_intvar_set(wl_to_ndev(wl), "wowl", wowl);
+ if (err) {
+ WL_ERR(("Error enabling WOWL %d\n", err));
+ }
+
+ return err;
+}
+#else
+static int wl_cfg80211_suspend(struct wiphy *wiphy)
+{
+ return 0;
+}
+#endif
+
+static int wl_cfg80211_resume(struct wiphy *wiphy)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
+ struct wl_cfg80211_priv *wl = wiphy_to_wl(wiphy);
+ wlc_rekey_info_t *rekey = (wlc_rekey_info_t *)wl->extra_buf;
+ s32 err;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ struct cfg80211_wowlan_wakeup wakeup;
+ int result;
+
+ memset(&wakeup, 0, sizeof(wakeup));
+ wakeup.pattern_idx = -1;
+
+ result = wl_wowl_ind_wake_reason(wl, &wakeup);
+ switch (result) {
+ case -1:
+ break;
+ case 0:
+ cfg80211_report_wowlan_wakeup(wl_to_wdev(wl), NULL, GFP_KERNEL);
+ break;
+ case 1:
+ cfg80211_report_wowlan_wakeup(wl_to_wdev(wl), &wakeup, GFP_KERNEL);
+ break;
+ }
+#endif
+
+ err = wl_dev_bufvar_get(wl_to_ndev(wl), "wowl_replay", (s8 *)rekey,
+ sizeof(wlc_rekey_info_t));
+ if (!err) {
+ WL_INF(("Send up replay counter %x%x%x%x%x%x%x%x\n",
+ rekey->replay_counter[0], rekey->replay_counter[1],
+ rekey->replay_counter[2], rekey->replay_counter[3],
+ rekey->replay_counter[4], rekey->replay_counter[5],
+ rekey->replay_counter[6], rekey->replay_counter[7]));
+ cfg80211_gtk_rekey_notify(wl_to_ndev(wl), (u8 *)&wl->bssid.octet,
+ rekey->replay_counter, GFP_KERNEL);
+ }
+#endif
+ return 0;
+}
+#endif
+
+static struct cfg80211_ops wl_cfg80211_ops = {
+ .change_virtual_intf = wl_cfg80211_change_iface,
+ .scan = wl_cfg80211_scan,
+ .set_wiphy_params = wl_cfg80211_set_wiphy_params,
+ .join_ibss = wl_cfg80211_join_ibss,
+ .leave_ibss = wl_cfg80211_leave_ibss,
+ .get_station = wl_cfg80211_get_station,
+ .set_tx_power = wl_cfg80211_set_tx_power,
+ .get_tx_power = wl_cfg80211_get_tx_power,
+ .add_key = wl_cfg80211_add_key,
+ .del_key = wl_cfg80211_del_key,
+ .get_key = wl_cfg80211_get_key,
+ .set_default_key = wl_cfg80211_config_default_key,
+ .set_power_mgmt = wl_cfg80211_set_power_mgmt,
+ .connect = wl_cfg80211_connect,
+ .disconnect = wl_cfg80211_disconnect,
+#ifdef CONFIG_PM
+ .suspend = wl_cfg80211_suspend,
+ .resume = wl_cfg80211_resume,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
+ .set_rekey_data = wl_cfg80211_rekey,
+#endif
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
+ .set_pmksa = wl_cfg80211_set_pmksa,
+ .del_pmksa = wl_cfg80211_del_pmksa,
+ .flush_pmksa = wl_cfg80211_flush_pmksa
+#endif
+};
+
+#ifdef CONFIG_PM
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+static const struct wiphy_wowlan_support wl_wowlan_support = {
+#else
+static struct wiphy_wowlan_support wl_wowlan_support = {
+#endif
+ .flags = WIPHY_WOWLAN_MAGIC_PKT
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
+ | WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ
+#endif
+ | WIPHY_WOWLAN_DISCONNECT,
+};
+#endif
+#endif
+
+static s32 wl_mode_to_nl80211_iftype(s32 mode)
+{
+ s32 err = 0;
+
+ switch (mode) {
+ case WL_MODE_BSS:
+ return NL80211_IFTYPE_STATION;
+ case WL_MODE_IBSS:
+ return NL80211_IFTYPE_ADHOC;
+ default:
+ return NL80211_IFTYPE_UNSPECIFIED;
+ }
+
+ return err;
+}
+
+static s32 wl_alloc_wdev(struct device *dev, struct wireless_dev **rwdev)
+{
+ struct wireless_dev *wdev;
+ s32 err = 0;
+
+ wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
+ if (!wdev) {
+ WL_ERR(("Could not allocate wireless device\n"));
+ err = -ENOMEM;
+ goto early_out;
+ }
+ wdev->wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_cfg80211_priv));
+ if (!wdev->wiphy) {
+ WL_ERR(("Couldn not allocate wiphy device\n"));
+ err = -ENOMEM;
+ goto wiphy_new_out;
+ }
+ set_wiphy_dev(wdev->wiphy, dev);
+ wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
+ wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
+#endif
+ wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
+ wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a;
+ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wdev->wiphy->cipher_suites = __wl_cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
+
+ wdev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+#endif
+
+#ifdef CONFIG_PM
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ wdev->wiphy->wowlan = &wl_wowlan_support;
+#else
+ wdev->wiphy->wowlan = wl_wowlan_support;
+#endif
+#endif
+#endif
+
+ err = wiphy_register(wdev->wiphy);
+ if (err < 0) {
+ WL_ERR(("Couldn not register wiphy device (%d)\n", err));
+ goto wiphy_register_out;
+ }
+
+ *rwdev = wdev;
+ return err;
+
+wiphy_register_out:
+ wiphy_free(wdev->wiphy);
+
+wiphy_new_out:
+ kfree(wdev);
+
+early_out:
+ *rwdev = wdev;
+ return err;
+}
+
+static void wl_free_wdev(struct wl_cfg80211_priv *wl)
+{
+ struct wireless_dev *wdev = wl_to_wdev(wl);
+
+ if (!wdev) {
+ WL_ERR(("wdev is invalid\n"));
+ return;
+ }
+ wiphy_unregister(wdev->wiphy);
+ wiphy_free(wdev->wiphy);
+ kfree(wdev);
+ wl_to_wdev(wl) = NULL;
+}
+
+static s32 wl_inform_bss(struct wl_cfg80211_priv *wl, struct wl_scan_results *bss_list)
+{
+ struct wl_bss_info *bi = NULL;
+ s32 err = 0;
+ int i;
+
+ if (bss_list->version != WL_BSS_INFO_VERSION) {
+ WL_ERR(("Version %d != WL_BSS_INFO_VERSION\n", bss_list->version));
+ return -EOPNOTSUPP;
+ }
+ WL_DBG(("scanned AP count (%d)\n", bss_list->count));
+ bi = next_bss(bss_list, bi);
+ for_each_bss(bss_list, bi, i) {
+ err = wl_inform_single_bss(wl, bi);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+static s32 wl_inform_single_bss(struct wl_cfg80211_priv *wl, struct wl_bss_info *bi)
+{
+ struct wiphy *wiphy = wl_to_wiphy(wl);
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_channel *channel;
+ struct wl_cfg80211_bss_info *notif_bss_info;
+ struct wl_cfg80211_scan_req *sr = wl_to_sr(wl);
+ struct beacon_proberesp *beacon_proberesp;
+ struct cfg80211_bss *cbss = NULL;
+ s32 mgmt_type;
+ u32 signal;
+ u32 freq;
+ s32 err = 0;
+ u8 *notify_ie;
+ size_t notify_ielen;
+
+ if (dtoh32(bi->length) > WL_BSS_INFO_MAX) {
+ WL_DBG(("Beacon is larger than buffer. Discarding\n"));
+ return err;
+ }
+ notif_bss_info = kzalloc(sizeof(*notif_bss_info) + sizeof(*mgmt) - sizeof(u8) +
+ WL_BSS_INFO_MAX, GFP_KERNEL);
+ if (!notif_bss_info) {
+ WL_ERR(("notif_bss_info alloc failed\n"));
+ return -ENOMEM;
+ }
+ mgmt = (struct ieee80211_mgmt *)notif_bss_info->frame_buf;
+ notif_bss_info->channel = bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(bi->chanspec);
+
+ notif_bss_info->rssi = bi->RSSI;
+ memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN);
+ mgmt_type = wl->active_scan ? IEEE80211_STYPE_PROBE_RESP : IEEE80211_STYPE_BEACON;
+ if (!memcmp(bi->SSID, sr->ssid.SSID, bi->SSID_len)) {
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | mgmt_type);
+ }
+ beacon_proberesp = wl->active_scan ? (struct beacon_proberesp *)&mgmt->u.probe_resp :
+ (struct beacon_proberesp *)&mgmt->u.beacon;
+ beacon_proberesp->timestamp = 0;
+ beacon_proberesp->beacon_int = cpu_to_le16(bi->beacon_period);
+ beacon_proberesp->capab_info = cpu_to_le16(bi->capability);
+ wl_rst_ie(wl);
+
+ wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length);
+ wl_cp_ie(wl, beacon_proberesp->variable, WL_BSS_INFO_MAX -
+ offsetof(struct wl_cfg80211_bss_info, frame_buf));
+ notif_bss_info->frame_len = offsetof(struct ieee80211_mgmt, u.beacon.variable) +
+ wl_get_ielen(wl);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ freq = ieee80211_channel_to_frequency(notif_bss_info->channel,
+ (notif_bss_info->channel <= CH_MAX_2G_CHANNEL) ?
+ IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ);
+#else
+ freq = ieee80211_channel_to_frequency(notif_bss_info->channel);
+#endif
+ if (freq == 0) {
+ WL_ERR(("Invalid channel, fail to chcnage channel to freq\n"));
+ kfree(notif_bss_info);
+ return -EINVAL;
+ }
+ channel = ieee80211_get_channel(wiphy, freq);
+ if (unlikely(!channel)) {
+ WL_ERR(("ieee80211_get_channel error\n"));
+ kfree(notif_bss_info);
+ return -EINVAL;
+ }
+
+ WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM\n",
+ bi->SSID, notif_bss_info->rssi, notif_bss_info->channel,
+ mgmt->u.beacon.capab_info, &bi->BSSID));
+
+ signal = notif_bss_info->rssi * 100;
+ cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt,
+ le16_to_cpu(notif_bss_info->frame_len), signal, GFP_KERNEL);
+ if (unlikely(!cbss)) {
+ WL_ERR(("cfg80211_inform_bss_frame error\n"));
+ kfree(notif_bss_info);
+ return -EINVAL;
+ }
+
+ notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+ notify_ielen = le32_to_cpu(bi->ie_length);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
+ cbss = cfg80211_inform_bss(wiphy, channel, (const u8 *)(bi->BSSID.octet),
+ 0, beacon_proberesp->capab_info, beacon_proberesp->beacon_int,
+ (const u8 *)notify_ie, notify_ielen, signal, GFP_KERNEL);
+#else
+ cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, (const u8 *)(bi->BSSID.octet),
+ 0, beacon_proberesp->capab_info, beacon_proberesp->beacon_int,
+ (const u8 *)notify_ie, notify_ielen, signal, GFP_KERNEL);
+#endif
+
+ if (unlikely(!cbss))
+ return -ENOMEM;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ cfg80211_put_bss(wiphy, cbss);
+#else
+ cfg80211_put_bss(cbss);
+#endif
+
+ kfree(notif_bss_info);
+
+ return err;
+}
+
+static s32
+wl_notify_connect_status(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data)
+{
+ s32 err = 0;
+ u32 event = EVENT_TYPE(e);
+ u16 flags = EVENT_FLAGS(e);
+ u32 status = EVENT_STATUS(e);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+ struct ieee80211_channel *channel = NULL;
+ struct wiphy *wiphy;
+ u32 chanspec, chan;
+ u32 freq, band;
+#endif
+
+ WL_DBG(("\n"));
+
+ if (!wl_is_ibssmode(wl)) {
+ if (event == WLC_E_LINK && (flags & WLC_EVENT_MSG_LINK)) {
+ wl_link_up(wl);
+ wl_bss_connect_done(wl, ndev, e, data, true);
+ wl->profile->active = true;
+ }
+ else if ((event == WLC_E_LINK && ~(flags & WLC_EVENT_MSG_LINK)) ||
+ event == WLC_E_DEAUTH_IND || event == WLC_E_DISASSOC_IND) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
+ cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL);
+#else
+ cfg80211_disconnected(ndev, 0, NULL, 0, false, GFP_KERNEL);
+#endif
+ clear_bit(WL_STATUS_CONNECTED, &wl->status);
+ wl_link_down(wl);
+ wl_init_prof(wl->profile);
+ }
+ else if (event == WLC_E_SET_SSID && status == WLC_E_STATUS_NO_NETWORKS) {
+ wl_bss_connect_done(wl, ndev, e, data, false);
+ }
+ else {
+ WL_DBG(("no action (BSS mode)\n"));
+ }
+ }
+ else {
+ if (event == WLC_E_JOIN) {
+ WL_DBG(("joined in IBSS network\n"));
+ }
+ if (event == WLC_E_START) {
+ WL_DBG(("started IBSS network\n"));
+ }
+ if (event == WLC_E_JOIN || event == WLC_E_START) {
+ wl_link_up(wl);
+ wl_get_assoc_ies(wl);
+ memcpy(&wl->bssid, &e->addr, ETHER_ADDR_LEN);
+ wl_update_bss_info(wl);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+ wiphy = wl_to_wiphy(wl);
+ err = wl_dev_intvar_get(ndev, "chanspec", &chanspec);
+ if (err) {
+ WL_ERR(("Could not get chanspec, err %d\n", err));
+ return err;
+ }
+ chan = wf_chspec_ctlchan(chanspec);
+ band = (chan <= CH_MAX_2G_CHANNEL) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+ freq = ieee80211_channel_to_frequency(chan, band);
+ channel = ieee80211_get_channel(wiphy, freq);
+ cfg80211_ibss_joined(ndev, (u8 *)&wl->bssid, channel, GFP_KERNEL);
+#else
+ cfg80211_ibss_joined(ndev, (u8 *)&wl->bssid, GFP_KERNEL);
+#endif
+ set_bit(WL_STATUS_CONNECTED, &wl->status);
+ wl->profile->active = true;
+ }
+ else if ((event == WLC_E_LINK && ~(flags & WLC_EVENT_MSG_LINK)) ||
+ event == WLC_E_DEAUTH_IND || event == WLC_E_DISASSOC_IND) {
+ clear_bit(WL_STATUS_CONNECTED, &wl->status);
+ wl_link_down(wl);
+ wl_init_prof(wl->profile);
+ }
+ else if (event == WLC_E_SET_SSID && status == WLC_E_STATUS_NO_NETWORKS) {
+ WL_DBG(("no action - join fail (IBSS mode)\n"));
+ }
+ else {
+ WL_DBG(("no action (IBSS mode)\n"));
+ }
+ }
+
+ return err;
+}
+
+static s32
+wl_notify_roaming_status(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data)
+{
+ s32 err = 0;
+ u32 status = EVENT_STATUS(e);
+
+ WL_DBG(("\n"));
+
+ if (status == WLC_E_STATUS_SUCCESS) {
+ err = wl_bss_roaming_done(wl, ndev, e, data);
+ wl->profile->active = true;
+ }
+
+ return err;
+}
+
+static __used s32
+wl_dev_bufvar_set(struct net_device *dev, s8 *name, s8 *buf, s32 len)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(dev);
+ u32 buflen;
+
+ buflen = bcm_mkiovar(name, buf, len, wl->ioctl_buf, WL_IOCTL_LEN_MAX);
+ BUG_ON(!buflen);
+
+ return wl_dev_ioctl(dev, WLC_SET_VAR, wl->ioctl_buf, buflen);
+}
+
+static s32
+wl_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf, s32 buf_len)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(dev);
+ u32 len;
+ s32 err = 0;
+
+ len = bcm_mkiovar(name, NULL, 0, wl->ioctl_buf, WL_IOCTL_LEN_MAX);
+ BUG_ON(!len);
+ err = wl_dev_ioctl(dev, WLC_GET_VAR, (void *)wl->ioctl_buf, WL_IOCTL_LEN_MAX);
+ if (err) {
+ WL_INF(("error (%d)\n", err));
+ return err;
+ }
+ memcpy(buf, wl->ioctl_buf, buf_len);
+
+ return err;
+}
+
+static s32 wl_get_assoc_ies(struct wl_cfg80211_priv *wl)
+{
+ struct net_device *ndev = wl_to_ndev(wl);
+ struct wl_cfg80211_assoc_ielen *assoc_info;
+ struct wl_cfg80211_connect_info *conn_info = wl_to_conn(wl);
+ u32 req_len;
+ u32 resp_len;
+ s32 err = 0;
+
+ err = wl_dev_bufvar_get(ndev, "assoc_info", wl->extra_buf, WL_ASSOC_INFO_MAX);
+ if (err) {
+ WL_ERR(("could not get assoc info (%d)\n", err));
+ return err;
+ }
+ assoc_info = (struct wl_cfg80211_assoc_ielen *)wl->extra_buf;
+ req_len = assoc_info->req_len;
+ resp_len = assoc_info->resp_len;
+ if (req_len) {
+ err = wl_dev_bufvar_get(ndev, "assoc_req_ies", wl->extra_buf, WL_ASSOC_INFO_MAX);
+ if (err) {
+ WL_ERR(("could not get assoc req (%d)\n", err));
+ return err;
+ }
+ conn_info->req_ie_len = req_len;
+ conn_info->req_ie =
+ kmemdup(wl->extra_buf, conn_info->req_ie_len, GFP_KERNEL);
+ } else {
+ conn_info->req_ie_len = 0;
+ conn_info->req_ie = NULL;
+ }
+ if (resp_len) {
+ err = wl_dev_bufvar_get(ndev, "assoc_resp_ies", wl->extra_buf, WL_ASSOC_INFO_MAX);
+ if (err) {
+ WL_ERR(("could not get assoc resp (%d)\n", err));
+ return err;
+ }
+ conn_info->resp_ie_len = resp_len;
+ conn_info->resp_ie =
+ kmemdup(wl->extra_buf, conn_info->resp_ie_len, GFP_KERNEL);
+ } else {
+ conn_info->resp_ie_len = 0;
+ conn_info->resp_ie = NULL;
+ }
+ WL_DBG(("req len (%d) resp len (%d)\n", conn_info->req_ie_len,
+ conn_info->resp_ie_len));
+
+ return err;
+}
+
+static void wl_ch_to_chanspec(struct ieee80211_channel *chan, struct wl_join_params *join_params,
+ size_t *join_params_size)
+{
+ chanspec_t chanspec = 0;
+
+ if (chan) {
+ join_params->params.chanspec_num = 1;
+ join_params->params.chanspec_list[0] =
+ ieee80211_frequency_to_channel(chan->center_freq);
+
+ if (chan->band == IEEE80211_BAND_2GHZ) {
+ chanspec |= WL_CHANSPEC_BAND_2G;
+ }
+ else if (chan->band == IEEE80211_BAND_5GHZ) {
+ chanspec |= WL_CHANSPEC_BAND_5G;
+ }
+ else {
+ WL_ERR(("Unknown band\n"));
+ BUG();
+ }
+
+ chanspec |= WL_CHANSPEC_BW_20;
+
+ *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE +
+ join_params->params.chanspec_num * sizeof(chanspec_t);
+
+ join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK;
+ join_params->params.chanspec_list[0] |= chanspec;
+ join_params->params.chanspec_list[0] =
+ htodchanspec(join_params->params.chanspec_list[0]);
+
+ join_params->params.chanspec_num = htod32(join_params->params.chanspec_num);
+
+ WL_DBG(("join_params->params.chanspec_list[0]= %#X, channel %d, chanspec %#X\n",
+ join_params->params.chanspec_list[0],
+ join_params->params.chanspec_list[0], chanspec));
+ }
+}
+
+static s32 wl_update_bss_info(struct wl_cfg80211_priv *wl)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ struct wiphy *wiphy = wl_to_wiphy(wl);
+#endif
+ struct cfg80211_bss *bss;
+ struct wl_bss_info *bi;
+ struct wlc_ssid *ssid;
+ struct bcm_tlv *tim;
+ s32 dtim_period;
+ size_t ie_len;
+ u8 *ie;
+ s32 err = 0;
+
+ ssid = &wl->profile->ssid;
+ bss = cfg80211_get_bss(wl_to_wiphy(wl), NULL, (s8 *)&wl->bssid,
+ ssid->SSID, ssid->SSID_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+
+ rtnl_lock();
+ if (!bss) {
+ WL_DBG(("Could not find the AP\n"));
+ *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX);
+ err = wl_dev_ioctl(wl_to_ndev(wl), WLC_GET_BSS_INFO, wl->extra_buf,
+ WL_EXTRA_BUF_MAX);
+ if (err) {
+ WL_ERR(("Could not get bss info %d\n", err));
+ goto update_bss_info_out;
+ }
+ bi = (struct wl_bss_info *)(wl->extra_buf + 4);
+ if (memcmp(&bi->BSSID, &wl->bssid, ETHER_ADDR_LEN)) {
+ err = -EIO;
+ goto update_bss_info_out;
+ }
+ err = wl_inform_single_bss(wl, bi);
+ if (err)
+ goto update_bss_info_out;
+
+ ie = ((u8 *)bi) + bi->ie_offset;
+ ie_len = bi->ie_length;
+ } else {
+ WL_DBG(("Found the AP in the list - BSSID %pM\n", bss->bssid));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ ie = (u8 *)(bss->ies->data);
+ ie_len = bss->ies->len;
+#else
+ ie = bss->information_elements;
+ ie_len = bss->len_information_elements;
+#endif
+ wl->conf->channel = *bss->channel;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ cfg80211_put_bss(wiphy, bss);
+#else
+ cfg80211_put_bss(bss);
+#endif
+ }
+
+ tim = bcm_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
+ if (tim) {
+ dtim_period = tim->data[1];
+ } else {
+
+ err = wl_dev_ioctl(wl_to_ndev(wl), WLC_GET_DTIMPRD,
+ &dtim_period, sizeof(dtim_period));
+ if (err) {
+ WL_ERR(("WLC_GET_DTIMPRD error (%d)\n", err));
+ goto update_bss_info_out;
+ }
+ }
+
+update_bss_info_out:
+ rtnl_unlock();
+ return err;
+}
+
+static s32
+wl_bss_roaming_done(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data)
+{
+ struct wl_cfg80211_connect_info *conn_info = wl_to_conn(wl);
+ s32 err = 0;
+
+ wl_get_assoc_ies(wl);
+ memcpy(wl->profile->bssid, &e->addr, ETHER_ADDR_LEN);
+ memcpy(&wl->bssid, &e->addr, ETHER_ADDR_LEN);
+ wl_update_bss_info(wl);
+ cfg80211_roamed(ndev,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)
+ &wl->conf->channel,
+#endif
+ (u8 *)&wl->bssid,
+ conn_info->req_ie, conn_info->req_ie_len,
+ conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
+ WL_DBG(("Report roaming result\n"));
+
+ set_bit(WL_STATUS_CONNECTED, &wl->status);
+
+ return err;
+}
+
+static s32
+wl_bss_connect_done(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data, bool completed)
+{
+ struct wl_cfg80211_connect_info *conn_info = wl_to_conn(wl);
+ s32 err = 0;
+
+ if (wl->scan_request) {
+ WL_DBG(("%s: Aborting scan\n", __FUNCTION__));
+ cfg80211_scan_done(wl->scan_request, true);
+ wl->scan_request = NULL;
+ }
+
+ if (test_and_clear_bit(WL_STATUS_CONNECTING, &wl->status)) {
+ if (completed) {
+ wl_get_assoc_ies(wl);
+ memcpy(&wl->bssid, &e->addr, ETHER_ADDR_LEN);
+ memcpy(wl->profile->bssid, &e->addr, ETHER_ADDR_LEN);
+ wl_update_bss_info(wl);
+ set_bit(WL_STATUS_CONNECTED, &wl->status);
+ }
+
+ WL_DBG(("Reporting BSS network join result \"%s\"\n",
+ wl->profile->ssid.SSID));
+ cfg80211_connect_result(ndev, (u8 *)&wl->bssid, conn_info->req_ie,
+ conn_info->req_ie_len, conn_info->resp_ie, conn_info->resp_ie_len,
+ completed ? WLAN_STATUS_SUCCESS : WLAN_STATUS_AUTH_TIMEOUT, GFP_KERNEL);
+ WL_DBG(("Connection %s\n", completed ? "Succeeded" : "FAILed"));
+ }
+
+ return err;
+}
+
+static s32
+wl_notify_mic_status(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data)
+{
+ u16 flags = EVENT_FLAGS(e);
+ enum nl80211_key_type key_type;
+
+ WL_DBG(("\n"));
+
+ rtnl_lock();
+ if (flags & WLC_EVENT_MSG_GROUP)
+ key_type = NL80211_KEYTYPE_GROUP;
+ else
+ key_type = NL80211_KEYTYPE_PAIRWISE;
+
+ cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1, NULL, GFP_KERNEL);
+ rtnl_unlock();
+
+ return 0;
+}
+
+static s32
+wl_notify_scan_status(struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data)
+{
+ struct channel_info channel_inform;
+ struct wl_scan_results *bss_list;
+ u32 buflen;
+ s32 err = 0;
+
+ WL_DBG(("\n"));
+
+ rtnl_lock();
+ err = wl_dev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, sizeof(channel_inform));
+ if (err) {
+ WL_ERR(("scan busy (%d)\n", err));
+ goto scan_done_out;
+ }
+ channel_inform.scan_channel = dtoh32(channel_inform.scan_channel);
+ if (channel_inform.scan_channel) {
+
+ WL_DBG(("channel_inform.scan_channel (%d)\n", channel_inform.scan_channel));
+ }
+
+ for (buflen = WL_SCAN_BUF_BASE; ; ) {
+ bss_list = (struct wl_scan_results *) kmalloc(buflen, GFP_KERNEL);
+ if (!bss_list) {
+ WL_ERR(("%s Out of memory for scan results, (%d)\n", ndev->name, err));
+ goto scan_done_out;
+ }
+ memset(bss_list, 0, buflen);
+ bss_list->buflen = htod32(buflen);
+ err = wl_dev_ioctl(ndev, WLC_SCAN_RESULTS, bss_list, buflen);
+ if (!err) {
+ break;
+ }
+ else if (err == -E2BIG) {
+ kfree(bss_list);
+ buflen *= 2;
+ }
+ else {
+ WL_ERR(("%s Scan_results error (%d)\n", ndev->name, err));
+ kfree(bss_list);
+ err = -EINVAL;
+ goto scan_done_out;
+ }
+ }
+
+ bss_list->buflen = dtoh32(bss_list->buflen);
+ bss_list->version = dtoh32(bss_list->version);
+ bss_list->count = dtoh32(bss_list->count);
+
+ err = wl_inform_bss(wl, bss_list);
+ kfree(bss_list);
+
+scan_done_out:
+ if (wl->scan_request) {
+ cfg80211_scan_done(wl->scan_request, false);
+ wl->scan_request = NULL;
+ }
+ rtnl_unlock();
+ return err;
+}
+
+static void wl_init_conf(struct wl_cfg80211_conf *conf)
+{
+ conf->mode = (u32)-1;
+ conf->frag_threshold = (u32)-1;
+ conf->rts_threshold = (u32)-1;
+ conf->retry_short = (u32)-1;
+ conf->retry_long = (u32)-1;
+ conf->tx_power = -1;
+}
+
+static void wl_init_prof(struct wl_cfg80211_profile *prof)
+{
+ memset(prof, 0, sizeof(*prof));
+}
+
+static void wl_init_eloop_handler(struct wl_cfg80211_event_loop *el)
+{
+ memset(el, 0, sizeof(*el));
+ el->handler[WLC_E_SCAN_COMPLETE] = wl_notify_scan_status;
+ el->handler[WLC_E_JOIN] = wl_notify_connect_status;
+ el->handler[WLC_E_START] = wl_notify_connect_status;
+ el->handler[WLC_E_LINK] = wl_notify_connect_status;
+ el->handler[WLC_E_NDIS_LINK] = wl_notify_connect_status;
+ el->handler[WLC_E_SET_SSID] = wl_notify_connect_status;
+ el->handler[WLC_E_DISASSOC_IND] = wl_notify_connect_status;
+ el->handler[WLC_E_DEAUTH_IND] = wl_notify_connect_status;
+ el->handler[WLC_E_ROAM] = wl_notify_roaming_status;
+ el->handler[WLC_E_MIC_ERROR] = wl_notify_mic_status;
+}
+
+static s32 wl_init_priv_mem(struct wl_cfg80211_priv *wl)
+{
+ wl->conf = (void *)kzalloc(sizeof(*wl->conf), GFP_KERNEL);
+ if (!wl->conf) {
+ WL_ERR(("wl_cfg80211_conf alloc failed\n"));
+ goto init_priv_mem_out;
+ }
+ wl->profile = (void *)kzalloc(sizeof(*wl->profile), GFP_KERNEL);
+ if (!wl->profile) {
+ WL_ERR(("wl_cfg80211_profile alloc failed\n"));
+ goto init_priv_mem_out;
+ }
+ wl->scan_req_int = (void *)kzalloc(sizeof(*wl->scan_req_int), GFP_KERNEL);
+ if (!wl->scan_req_int) {
+ WL_ERR(("Scan req alloc failed\n"));
+ goto init_priv_mem_out;
+ }
+ wl->ioctl_buf = (void *)kzalloc(WL_IOCTL_LEN_MAX, GFP_KERNEL);
+ if (!wl->ioctl_buf) {
+ WL_ERR(("Ioctl buf alloc failed\n"));
+ goto init_priv_mem_out;
+ }
+ wl->extra_buf = (void *)kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
+ if (!wl->extra_buf) {
+ WL_ERR(("Extra buf alloc failed\n"));
+ goto init_priv_mem_out;
+ }
+
+ wl->pmk_list = (void *)kzalloc(sizeof(*wl->pmk_list), GFP_KERNEL);
+ if (!wl->pmk_list) {
+ WL_ERR(("pmk list alloc failed\n"));
+ goto init_priv_mem_out;
+ }
+
+ return 0;
+
+init_priv_mem_out:
+ wl_deinit_priv_mem(wl);
+
+ return -ENOMEM;
+}
+
+static void wl_deinit_priv_mem(struct wl_cfg80211_priv *wl)
+{
+ kfree(wl->conf);
+ wl->conf = NULL;
+ kfree(wl->profile);
+ wl->profile = NULL;
+ kfree(wl->scan_req_int);
+ wl->scan_req_int = NULL;
+ kfree(wl->ioctl_buf);
+ wl->ioctl_buf = NULL;
+ kfree(wl->extra_buf);
+ wl->extra_buf = NULL;
+ kfree(wl->pmk_list);
+ wl->pmk_list = NULL;
+}
+
+static s32 wl_create_event_handler(struct wl_cfg80211_priv *wl)
+{
+ sema_init(&wl->event_sync, 0);
+ wl->event_tsk = kthread_run(wl_event_handler, wl, "wl_event_handler");
+ if (IS_ERR(wl->event_tsk)) {
+ wl->event_tsk = NULL;
+ WL_ERR(("failed to create event thread\n"));
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void wl_destroy_event_handler(struct wl_cfg80211_priv *wl)
+{
+ if (wl->event_tsk) {
+ send_sig(SIGTERM, wl->event_tsk, 1);
+ kthread_stop(wl->event_tsk);
+ wl->event_tsk = NULL;
+ }
+}
+
+static s32 wl_init_cfg80211_priv(struct wl_cfg80211_priv *wl, struct wireless_dev *wdev)
+{
+ s32 err = 0;
+
+ wl->wdev = wdev;
+
+ wl->scan_request = NULL;
+ wl->active_scan = true;
+ wl_init_eq(wl);
+ err = wl_init_priv_mem(wl);
+ if (err)
+ return err;
+
+ if (wl_create_event_handler(wl))
+ return -ENOMEM;
+
+ wl_init_eloop_handler(&wl->el);
+
+ if (err)
+ return err;
+
+ wl_init_conf(wl->conf);
+ wl_init_prof(wl->profile);
+ wl_link_down(wl);
+
+ return err;
+}
+
+static void wl_deinit_cfg80211_priv(struct wl_cfg80211_priv *wl)
+{
+ wl_destroy_event_handler(wl);
+ wl_flush_eq(wl);
+ wl_link_down(wl);
+ wl_deinit_priv_mem(wl);
+}
+
+s32 wl_cfg80211_attach(struct net_device *ndev, struct device *dev, int passive)
+{
+ struct wireless_dev *wdev;
+ struct wl_cfg80211_priv *wl;
+ s32 err = 0;
+
+ if (!ndev) {
+ WL_ERR(("ndev is invaild\n"));
+ return -ENODEV;
+ }
+
+ err = wl_alloc_wdev(dev, &wdev);
+ if (err < 0) {
+ return err;
+ }
+
+ wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS);
+ wl = wdev_to_wl(wdev);
+ ndev->ieee80211_ptr = wdev;
+ SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
+ wdev->netdev = ndev;
+ err = wl_init_cfg80211_priv(wl, wdev);
+ if (err) {
+ WL_ERR(("Failed to init iwm_priv (%d)\n", err));
+ goto cfg80211_attach_out;
+ }
+ wl->passive = !!passive;
+
+ if (!err) {
+ WL_INF(("Registered CFG80211 phy\n"));
+ }
+ return err;
+
+cfg80211_attach_out:
+ wl_free_wdev(wl);
+ return err;
+}
+
+void wl_cfg80211_detach(struct net_device *ndev)
+{
+ struct wl_cfg80211_priv *wl;
+
+ if (ndev->ieee80211_ptr == NULL) {
+ WL_ERR(( "NULL ndev->ieee80211ptr, unable to deref wl\n"));
+ return;
+ }
+ wl = ndev_to_wl(ndev);
+
+ wl_deinit_cfg80211_priv(wl);
+ wl_free_wdev(wl);
+}
+
+static void wl_wakeup_event(struct wl_cfg80211_priv *wl)
+{
+ up(&wl->event_sync);
+}
+
+static s32 wl_event_handler(void *data)
+{
+ struct wl_cfg80211_priv *wl = (struct wl_cfg80211_priv *)data;
+ struct wl_cfg80211_event_q *e;
+
+ allow_signal(SIGTERM);
+ while (!down_interruptible(&wl->event_sync)) {
+ if (kthread_should_stop())
+ break;
+ e = wl_deq_event(wl);
+ if (!e) {
+ WL_ERR(("eqeue empty..\n"));
+ BUG();
+ }
+ if (wl->el.handler[e->etype]) {
+ WL_DBG(("event type (%d)\n", e->etype));
+ wl->el.handler[e->etype] (wl, wl_to_ndev(wl), &e->emsg, e->edata);
+ } else {
+ WL_DBG(("Unknown Event (%d): ignoring\n", e->etype));
+ }
+ wl_put_event(e);
+ }
+ WL_DBG(("%s was terminated\n", __func__));
+ return 0;
+}
+
+void
+wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t * e, void *data)
+{
+
+ u32 event_type = EVENT_TYPE(e);
+
+ struct wl_cfg80211_priv *wl = ndev_to_wl(ndev);
+#if defined(WL_DBGMSG_ENABLE)
+ s8 *estr = (event_type <= sizeof(wl_dbg_estr) / WL_DBG_ESTR_MAX - 1) ?
+ wl_dbg_estr[event_type] : (s8 *) "Unknown";
+ WL_DBG(("event_type (%d):" "WLC_E_" "%s\n", event_type, estr));
+#endif
+ if (!wl_enq_event(wl, event_type, e, data))
+ wl_wakeup_event(wl);
+}
+
+static void wl_init_eq(struct wl_cfg80211_priv *wl)
+{
+ wl_init_eq_lock(wl);
+ INIT_LIST_HEAD(&wl->eq_list);
+}
+
+static void wl_flush_eq(struct wl_cfg80211_priv *wl)
+{
+ struct wl_cfg80211_event_q *e;
+
+ wl_lock_eq(wl);
+ while (!list_empty(&wl->eq_list)) {
+ e = list_first_entry(&wl->eq_list, struct wl_cfg80211_event_q, eq_list);
+ list_del(&e->eq_list);
+ kfree(e);
+ }
+ wl_unlock_eq(wl);
+}
+
+static struct wl_cfg80211_event_q *wl_deq_event(struct wl_cfg80211_priv *wl)
+{
+ struct wl_cfg80211_event_q *e = NULL;
+
+ wl_lock_eq(wl);
+ if (!list_empty(&wl->eq_list)) {
+ e = list_first_entry(&wl->eq_list, struct wl_cfg80211_event_q, eq_list);
+ list_del(&e->eq_list);
+ }
+ wl_unlock_eq(wl);
+
+ return e;
+}
+
+static s32
+wl_enq_event(struct wl_cfg80211_priv *wl, u32 event, const wl_event_msg_t *msg, void *data)
+{
+ struct wl_cfg80211_event_q *e;
+ s32 err = 0;
+
+ e = kzalloc(sizeof(struct wl_cfg80211_event_q), GFP_ATOMIC);
+ if (!e) {
+ WL_ERR(("event alloc failed\n"));
+ return -ENOMEM;
+ }
+
+ e->etype = event;
+ memcpy(&e->emsg, msg, sizeof(wl_event_msg_t));
+ if (data) {
+ }
+
+ spin_lock(&wl->eq_lock);
+ list_add_tail(&e->eq_list, &wl->eq_list);
+ spin_unlock(&wl->eq_lock);
+
+ return err;
+}
+
+static void wl_put_event(struct wl_cfg80211_event_q *e)
+{
+ kfree(e);
+}
+
+static s32 wl_set_mode(struct net_device *ndev, s32 iftype)
+{
+ s32 infra = 0;
+ s32 ap = 0;
+ s32 err = 0;
+
+ switch (iftype) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_WDS:
+ WL_ERR(("type (%d) : currently we do not support this mode\n",
+ iftype));
+ err = -EINVAL;
+ return err;
+ case NL80211_IFTYPE_ADHOC:
+ break;
+ case NL80211_IFTYPE_STATION:
+ infra = 1;
+ break;
+ default:
+ err = -EINVAL;
+ WL_ERR(("invalid type (%d)\n", iftype));
+ return err;
+ }
+ infra = htod32(infra);
+ ap = htod32(ap);
+ WL_DBG(("%s ap (%d), infra (%d)\n", ndev->name, ap, infra));
+ err = wl_dev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra));
+ if (err) {
+ WL_ERR(("WLC_SET_INFRA error (%d)\n", err));
+ return err;
+ }
+ err = wl_dev_ioctl(ndev, WLC_SET_AP, &ap, sizeof(ap));
+ if (err) {
+ WL_ERR(("WLC_SET_AP error (%d)\n", err));
+ return err;
+ }
+
+ return 0;
+}
+
+static void wl_update_wowl(struct net_device *ndev)
+{
+#ifdef CONFIG_PM
+ struct wl_cfg80211_priv *wl = ndev_to_wl(ndev);
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
+ s32 offloads = 0;
+ s32 err = 0;
+ err = wl_dev_bufvar_get(wl_to_ndev(wl), "offloads",
+ (s8 *)&offloads, sizeof(offloads));
+ if (err == 0 && offloads == 1) {
+ WL_INF(("Supports offloads\n"));
+ wl->offloads = true;
+ } else {
+ WL_INF(("No offloads supported\n"));
+ wl->offloads = false;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ wdev->wiphy->wowlan = NULL;
+#else
+ memset(&wdev->wiphy->wowlan, 0, sizeof(struct wiphy_wowlan_support));
+#endif
+#endif
+ }
+#endif
+}
+
+static s32 wl_update_wiphybands(struct wl_cfg80211_priv *wl)
+{
+ struct wiphy *wiphy;
+ s32 phy_list;
+ s8 phy;
+ s32 err = 0;
+
+ err = wl_dev_ioctl(wl_to_ndev(wl), WLC_GET_PHYLIST, &phy_list, sizeof(phy_list));
+ if (err) {
+ WL_ERR(("error (%d)\n", err));
+ return err;
+ }
+
+ phy = ((char *)&phy_list)[0];
+ WL_DBG(("%c phy\n", phy));
+
+ if (phy == 'n' || phy == 'a' || phy == 'v') {
+ wiphy = wl_to_wiphy(wl);
+ wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
+ }
+
+ return err;
+}
+
+s32 wl_cfg80211_up(struct net_device *ndev)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(ndev);
+ s32 err = 0;
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
+
+ wl_set_mode(ndev, wdev->iftype);
+ err = wl_update_wiphybands(wl);
+ if (err) {
+ return err;
+ }
+
+ wl_update_wowl(ndev);
+ return 0;
+}
+
+s32 wl_cfg80211_down(struct net_device *ndev)
+{
+ struct wl_cfg80211_priv *wl = ndev_to_wl(ndev);
+ s32 err = 0;
+
+ if (wl->scan_request) {
+ cfg80211_scan_done(wl->scan_request, true);
+ wl->scan_request = NULL;
+ }
+
+ return err;
+}
+
+static bool wl_is_ibssmode(struct wl_cfg80211_priv *wl)
+{
+ return wl->conf->mode == WL_MODE_IBSS;
+}
+
+static void wl_rst_ie(struct wl_cfg80211_priv *wl)
+{
+ struct wl_cfg80211_ie *ie = wl_to_ie(wl);
+
+ ie->offset = 0;
+}
+
+static __used s32 wl_add_ie(struct wl_cfg80211_priv *wl, u8 t, u8 l, u8 *v)
+{
+ struct wl_cfg80211_ie *ie = wl_to_ie(wl);
+ s32 err = 0;
+
+ if (ie->offset + l + 2 > WL_TLV_INFO_MAX) {
+ WL_ERR(("ei crosses buffer boundary\n"));
+ return -ENOSPC;
+ }
+ ie->buf[ie->offset] = t;
+ ie->buf[ie->offset + 1] = l;
+ memcpy(&ie->buf[ie->offset + 2], v, l);
+ ie->offset += l + 2;
+
+ return err;
+}
+
+static s32 wl_mrg_ie(struct wl_cfg80211_priv *wl, u8 *ie_stream, u16 ie_size)
+{
+ struct wl_cfg80211_ie *ie = wl_to_ie(wl);
+ s32 err = 0;
+
+ if (ie->offset + ie_size > WL_TLV_INFO_MAX) {
+ WL_ERR(("ei_stream crosses buffer boundary\n"));
+ return -ENOSPC;
+ }
+ memcpy(&ie->buf[ie->offset], ie_stream, ie_size);
+ ie->offset += ie_size;
+
+ return err;
+}
+
+static s32 wl_cp_ie(struct wl_cfg80211_priv *wl, u8 *dst, u16 dst_size)
+{
+ struct wl_cfg80211_ie *ie = wl_to_ie(wl);
+ s32 err = 0;
+
+ if (ie->offset > dst_size) {
+ WL_ERR(("dst_size is not enough\n"));
+ return -ENOSPC;
+ }
+ memcpy(dst, &ie->buf[0], ie->offset);
+
+ return err;
+}
+
+static u32 wl_get_ielen(struct wl_cfg80211_priv *wl)
+{
+ struct wl_cfg80211_ie *ie = wl_to_ie(wl);
+
+ return ie->offset;
+}
+
+static void wl_link_up(struct wl_cfg80211_priv *wl)
+{
+ WL_DBG(("\n"));
+}
+
+static void wl_link_down(struct wl_cfg80211_priv *wl)
+{
+ struct wl_cfg80211_connect_info *conn_info = wl_to_conn(wl);
+
+ WL_DBG(("\n"));
+
+ kfree(conn_info->req_ie);
+ conn_info->req_ie = NULL;
+ conn_info->req_ie_len = 0;
+ kfree(conn_info->resp_ie);
+ conn_info->resp_ie = NULL;
+ conn_info->resp_ie_len = 0;
+}
+
+static void wl_lock_eq(struct wl_cfg80211_priv *wl)
+{
+ spin_lock_irq(&wl->eq_lock);
+}
+
+static void wl_unlock_eq(struct wl_cfg80211_priv *wl)
+{
+ spin_unlock_irq(&wl->eq_lock);
+}
+
+static void wl_init_eq_lock(struct wl_cfg80211_priv *wl)
+{
+ spin_lock_init(&wl->eq_lock);
+}
+
+#endif
diff --git a/src/wl/sys/wl_cfg80211_hybrid.h b/src/wl/sys/wl_cfg80211_hybrid.h
new file mode 100644
index 0000000..bc6f3ad
--- /dev/null
+++ b/src/wl/sys/wl_cfg80211_hybrid.h
@@ -0,0 +1,231 @@
+/*
+ * Linux-specific portion of Broadcom 802.11abg Networking Device Driver
+ * cfg80211 interface
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wl_cfg80211.h,v 1.1.8.1 2011-01-26 00:57:46 $
+ */
+
+#ifndef _wl_cfg80211_h_
+#define _wl_cfg80211_h_
+
+#include <net/cfg80211.h>
+#include <wlioctl.h>
+
+struct wl_cfg80211_conf;
+struct wl_cfg80211_priv;
+struct wl_cfg80211_security;
+
+#define htod32(i) i
+#define htod16(i) i
+#define dtoh32(i) i
+#define dtoh16(i) i
+#define htodchanspec(i) i
+#define dtohchanspec(i) i
+#define dtoh32(i) i
+#define dtoh16(i) i
+
+#define WL_DBGMSG_ENABLE
+
+#define WL_DBG_NONE 0
+#define WL_DBG_DBG (1 << 2)
+#define WL_DBG_INFO (1 << 1)
+#define WL_DBG_ERR (1 << 0)
+#define WL_DBG_MASK ((WL_DBG_DBG | WL_DBG_INFO | WL_DBG_ERR) << 1)
+
+#if defined(WL_DBGMSG_ENABLE)
+#define WL_DBG(args) \
+do { \
+ if (wl_dbg_level & WL_DBG_DBG) { \
+ printk(KERN_ERR "DEBUG @%s :", __func__); \
+ printk args; \
+ } \
+} while (0)
+#else
+#define WL_DBG(args)
+#endif
+
+#define WL_ERR(args) \
+do { \
+ if (wl_dbg_level & WL_DBG_ERR) { \
+ if (net_ratelimit()) { \
+ printk(KERN_ERR "ERROR @%s : ", __func__); \
+ printk args; \
+ } \
+ } \
+} while (0)
+
+#define WL_INF(args) \
+do { \
+ if (wl_dbg_level & WL_DBG_INFO) { \
+ if (net_ratelimit()) { \
+ printk(KERN_ERR "INFO @%s : ", __func__); \
+ printk args; \
+ } \
+ } \
+} while (0)
+
+#define WL_NUM_SCAN_MAX 1
+#define WL_NUM_PMKIDS_MAX MAXPMKID
+#define WL_SCAN_BUF_BASE (16*1024)
+#define WL_TLV_INFO_MAX 1024
+#define WL_BSS_INFO_MAX 2048
+#define WL_ASSOC_INFO_MAX 512
+#define WL_IOCTL_LEN_MAX 2048
+#define WL_EXTRA_BUF_MAX 2048
+#define WL_AP_MAX 256
+
+enum wl_cfg80211_status {
+ WL_STATUS_CONNECTING,
+ WL_STATUS_CONNECTED
+};
+
+enum wl_cfg80211_mode {
+ WL_MODE_BSS,
+ WL_MODE_IBSS,
+ WL_MODE_AP
+};
+
+struct beacon_proberesp {
+ __le64 timestamp;
+ __le16 beacon_int;
+ __le16 capab_info;
+ u8 variable[0];
+} __attribute__ ((packed));
+
+struct wl_cfg80211_conf {
+ u32 mode;
+ u32 frag_threshold;
+ u32 rts_threshold;
+ u32 retry_short;
+ u32 retry_long;
+ s32 tx_power;
+ struct ieee80211_channel channel;
+};
+
+struct wl_cfg80211_event_loop {
+ s32(*handler[WLC_E_LAST]) (struct wl_cfg80211_priv *wl, struct net_device *ndev,
+ const wl_event_msg_t *e, void *data);
+};
+
+struct wl_cfg80211_bss_info {
+ u16 band;
+ u16 channel;
+ s16 rssi;
+ u16 frame_len;
+ u8 frame_buf[1];
+};
+
+struct wl_cfg80211_scan_req {
+ struct wlc_ssid ssid;
+};
+
+struct wl_cfg80211_ie {
+ u16 offset;
+ u8 buf[WL_TLV_INFO_MAX];
+};
+
+struct wl_cfg80211_event_q {
+ struct list_head eq_list;
+ u32 etype;
+ wl_event_msg_t emsg;
+ s8 edata[1];
+};
+
+struct wl_cfg80211_security {
+ u32 wpa_versions;
+ u32 auth_type;
+ u32 cipher_pairwise;
+ u32 cipher_group;
+ u32 wpa_auth;
+};
+
+struct wl_cfg80211_profile {
+ struct wlc_ssid ssid;
+ u8 bssid[ETHER_ADDR_LEN];
+ struct wl_cfg80211_security sec;
+ bool active;
+};
+
+struct wl_cfg80211_connect_info {
+ u8 *req_ie;
+ s32 req_ie_len;
+ u8 *resp_ie;
+ s32 resp_ie_len;
+};
+
+struct wl_cfg80211_assoc_ielen {
+ u32 req_len;
+ u32 resp_len;
+};
+
+struct wl_cfg80211_pmk_list {
+ pmkid_list_t pmkids;
+ pmkid_t foo[MAXPMKID - 1];
+};
+
+struct wl_cfg80211_priv {
+ struct wireless_dev *wdev;
+ struct wl_cfg80211_conf *conf;
+ struct cfg80211_scan_request *scan_request;
+ struct wl_cfg80211_event_loop el;
+ struct list_head eq_list;
+ spinlock_t eq_lock;
+ struct wl_cfg80211_scan_req *scan_req_int;
+ struct wl_cfg80211_ie ie;
+ struct ether_addr bssid;
+ struct semaphore event_sync;
+ struct wl_cfg80211_profile *profile;
+ struct wl_cfg80211_connect_info conn_info;
+ struct wl_cfg80211_pmk_list *pmk_list;
+ struct task_struct *event_tsk;
+ unsigned long status;
+ bool active_scan;
+ bool passive;
+ bool offloads;
+ u8 *ioctl_buf;
+ u8 *extra_buf;
+ u8 ci[0] __attribute__ ((__aligned__(NETDEV_ALIGN)));
+};
+
+#define wl_to_dev(w) (wiphy_dev(wl->wdev->wiphy))
+#define wl_to_wiphy(w) (w->wdev->wiphy)
+#define wiphy_to_wl(w) ((struct wl_cfg80211_priv *)(wiphy_priv(w)))
+#define wl_to_wdev(w) (w->wdev)
+#define wdev_to_wl(w) ((struct wl_cfg80211_priv *)(wdev_priv(w)))
+#define wl_to_ndev(w) (w->wdev->netdev)
+#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr))
+#define wl_to_sr(w) (w->scan_req_int)
+#define wl_to_ie(w) (&w->ie)
+#define wl_to_conn(w) (&w->conn_info)
+
+static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss)
+{
+ return bss = bss ? (struct wl_bss_info *)((unsigned long)bss +
+ dtoh32(bss->length)) : list->bss_info;
+}
+
+#define for_each_bss(list, bss, __i) \
+ for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss))
+
+extern s32 wl_cfg80211_attach(struct net_device *ndev, struct device *dev, int flags);
+extern void wl_cfg80211_detach(struct net_device *ndev);
+
+extern void wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e, void *data);
+extern s32 wl_cfg80211_up(struct net_device *ndev);
+extern s32 wl_cfg80211_down(struct net_device *ndev);
+
+#endif
diff --git a/src/wl/sys/wl_dbg.h b/src/wl/sys/wl_dbg.h
new file mode 100644
index 0000000..dc8bf26
--- /dev/null
+++ b/src/wl/sys/wl_dbg.h
@@ -0,0 +1,74 @@
+/*
+ * Minimal debug/trace/assert driver definitions for
+ * Broadcom 802.11 Networking Adapter.
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wl_dbg.h 405851 2013-06-05 00:56:21Z $
+ */
+
+#ifndef _wl_dbg_h_
+#define _wl_dbg_h_
+
+extern uint32 wl_msg_level;
+extern uint32 wl_msg_level2;
+
+#if defined(BCMDBG) && !defined(BCMDBG_EXCLUDE_HW_TIMESTAMP)
+extern char* wlc_dbg_get_hw_timestamp(void);
+
+#define WL_TIMESTAMP() do { if (wl_msg_level2 & WL_TIMESTAMP_VAL) {\
+ printf(wlc_dbg_get_hw_timestamp()); }\
+ } while (0)
+#else
+#define WL_TIMESTAMP()
+#endif
+
+#if 0 && (VERSION_MAJOR > 9)
+extern int osl_printf(const char *fmt, ...);
+#include <IOKit/apple80211/IO8Log.h>
+#define WL_PRINT(args) do { osl_printf args; } while (0)
+#define RELEASE_PRINT(args) do { WL_PRINT(args); IO8Log args; } while (0)
+#else
+#define WL_PRINT(args) do { WL_TIMESTAMP(); printf args; } while (0)
+#endif
+
+#ifdef BCMDBG
+
+#define WL_NONE(args) do {if (wl_msg_level & 0) WL_PRINT(args);} while (0)
+
+#define WL_ERROR(args) do {if (wl_msg_level & WL_ERROR_VAL) WL_PRINT(args);} while (0)
+#define WL_TRACE(args) do {if (wl_msg_level & WL_TRACE_VAL) WL_PRINT(args);} while (0)
+
+#else
+
+#define WL_NONE(args)
+
+#ifdef BCMDBG_ERR
+#define WL_ERROR(args) WL_PRINT(args)
+#else
+#define WL_ERROR(args)
+#endif
+#define WL_TRACE(args)
+#define WL_APSTA_UPDN(args)
+#define WL_APSTA_RX(args)
+#define WL_WSEC(args)
+#define WL_WSEC_DUMP(args)
+#define WL_PCIE(args) do {if (wl_msg_level2 & WL_PCIE_VAL) WL_PRINT(args);} while (0)
+#define WL_PCIE_ON() (wl_msg_level2 & WL_PCIE_VAL)
+#endif
+
+extern uint32 wl_msg_level;
+extern uint32 wl_msg_level2;
+#endif
diff --git a/src/wl/sys/wl_export.h b/src/wl/sys/wl_export.h
new file mode 100644
index 0000000..41d4251
--- /dev/null
+++ b/src/wl/sys/wl_export.h
@@ -0,0 +1,81 @@
+/*
+ * Required functions exported by the port-specific (os-dependent) driver
+ * to common (os-independent) driver code.
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wl_export.h 579954 2015-08-17 18:08:53Z $
+ */
+
+#ifndef _wl_export_h_
+#define _wl_export_h_
+
+struct wl_info;
+struct wl_if;
+struct wlc_if;
+extern void wl_init(struct wl_info *wl);
+extern uint wl_reset(struct wl_info *wl);
+extern void wl_intrson(struct wl_info *wl);
+extern uint32 wl_intrsoff(struct wl_info *wl);
+extern void wl_intrsrestore(struct wl_info *wl, uint32 macintmask);
+extern void wl_event(struct wl_info *wl, char *ifname, wlc_event_t *e);
+extern void wl_event_sync(struct wl_info *wl, char *ifname, wlc_event_t *e);
+extern void wl_event_sendup(struct wl_info *wl, const wlc_event_t *e, uint8 *data, uint32 len);
+extern int wl_up(struct wl_info *wl);
+extern void wl_down(struct wl_info *wl);
+extern void wl_dump_ver(struct wl_info *wl, struct bcmstrbuf *b);
+extern void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state, int prio);
+extern bool wl_alloc_dma_resources(struct wl_info *wl, uint dmaddrwidth);
+extern void wl_reclaim(void);
+extern void wl_nocard_timer(void *arg);
+extern void wl_recover_nocard(struct wl_info *wl);
+extern void wl_devicerecovery(struct wl_info *wl );
+
+extern uint32 wl_pcie_bar1(struct wl_info *wl, uchar** addr);
+
+struct wl_timer;
+extern struct wl_timer *wl_init_timer(struct wl_info *wl, void (*fn)(void* arg), void *arg,
+ const char *name);
+extern void wl_free_timer(struct wl_info *wl, struct wl_timer *timer);
+extern void wl_add_timer(struct wl_info *wl, struct wl_timer *timer, uint ms, int periodic);
+extern bool wl_del_timer(struct wl_info *wl, struct wl_timer *timer);
+
+extern void wl_sendup(struct wl_info *wl, struct wl_if *wlif, void *p, int numpkt);
+extern char *wl_ifname(struct wl_info *wl, struct wl_if *wlif);
+extern struct wl_if *wl_add_if(struct wl_info *wl, struct wlc_if* wlcif, uint unit,
+ struct ether_addr *remote);
+extern void wl_del_if(struct wl_info *wl, struct wl_if *wlif);
+#ifdef DWDS
+extern void wl_dwds_del_if(struct wl_info *wl, struct wl_if *wlif, bool force);
+#endif
+
+extern int wl_osl_pcie_rc(struct wl_info *wl, uint op, int param);
+
+extern void wl_monitor(struct wl_info *wl, wl_rxsts_t *rxsts, void *p);
+extern void wl_set_monitor(struct wl_info *wl, int val);
+
+extern uint wl_buf_to_pktcopy(osl_t *osh, void *p, uchar *buf, int len, uint offset);
+extern void * wl_get_pktbuffer(osl_t *osh, int len);
+extern int wl_set_pktlen(osl_t *osh, void *p, int len);
+
+#define IFCTX_ARPI (1)
+#define IFCTX_NDI (2)
+#define IFCTX_NETDEV (3)
+extern void *wl_get_ifctx(struct wl_info *wl, int ctx_id, wl_if_t *wlif);
+
+#define wl_sort_bsslist(a, b) FALSE
+
+#define wl_outputpacket_complete(a, b, c) do { } while (0)
+#endif
diff --git a/src/wl/sys/wl_iw.c b/src/wl/sys/wl_iw.c
new file mode 100644
index 0000000..c4c610b
--- /dev/null
+++ b/src/wl/sys/wl_iw.c
@@ -0,0 +1,2830 @@
+/*
+ * Linux Wireless Extensions support
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wl_iw.c 458427 2014-02-26 23:12:38Z $
+ */
+
+#if defined(USE_IW)
+#define LINUX_PORT
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <proto/ethernet.h>
+
+#include <linux/if_arp.h>
+#include <asm/uaccess.h>
+
+typedef const struct si_pub si_t;
+#include <wlioctl.h>
+
+#include <wl_dbg.h>
+#include <wl_iw.h>
+
+extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status,
+ uint32 reason, char* stringBuf, uint buflen);
+
+#define MAX_WLIW_IOCTL_LEN 1024
+
+#define htod32(i) i
+#define htod16(i) i
+#define dtoh32(i) i
+#define dtoh16(i) i
+#define htodchanspec(i) i
+#define dtohchanspec(i) i
+
+extern struct iw_statistics *wl_get_wireless_stats(struct net_device *dev);
+
+#if WIRELESS_EXT < 19
+#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST)
+#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST)
+#endif
+
+typedef struct priv_link {
+ wl_iw_t *wliw;
+} priv_link_t;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
+#define WL_DEV_LINK(dev) (priv_link_t*)(dev->priv)
+#else
+#define WL_DEV_LINK(dev) (priv_link_t*)netdev_priv(dev)
+#endif
+
+#define IW_DEV_IF(dev) ((wl_iw_t*)(WL_DEV_LINK(dev))->wliw)
+
+static void swap_key_from_BE(
+ wl_wsec_key_t *key
+)
+{
+ key->index = htod32(key->index);
+ key->len = htod32(key->len);
+ key->algo = htod32(key->algo);
+ key->flags = htod32(key->flags);
+ key->rxiv.hi = htod32(key->rxiv.hi);
+ key->rxiv.lo = htod16(key->rxiv.lo);
+ key->iv_initialized = htod32(key->iv_initialized);
+}
+
+static void swap_key_to_BE(
+ wl_wsec_key_t *key
+)
+{
+ key->index = dtoh32(key->index);
+ key->len = dtoh32(key->len);
+ key->algo = dtoh32(key->algo);
+ key->flags = dtoh32(key->flags);
+ key->rxiv.hi = dtoh32(key->rxiv.hi);
+ key->rxiv.lo = dtoh16(key->rxiv.lo);
+ key->iv_initialized = dtoh32(key->iv_initialized);
+}
+
+static int
+dev_wlc_ioctl(
+ struct net_device *dev,
+ int cmd,
+ void *arg,
+ int len
+)
+{
+ struct ifreq ifr;
+ wl_ioctl_t ioc;
+ mm_segment_t fs;
+ int ret;
+
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.cmd = cmd;
+ ioc.buf = arg;
+ ioc.len = len;
+
+ strcpy(ifr.ifr_name, dev->name);
+ ifr.ifr_data = (caddr_t) &ioc;
+
+ fs = get_fs();
+ set_fs(get_ds());
+#if defined(WL_USE_NETDEV_OPS)
+ ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+#else
+ ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+#endif
+ set_fs(fs);
+
+ return ret;
+}
+
+static int
+dev_wlc_intvar_set(
+ struct net_device *dev,
+ char *name,
+ int val)
+{
+ char buf[WLC_IOCTL_SMLEN];
+ uint len;
+
+ val = htod32(val);
+ len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf));
+ ASSERT(len);
+
+ return (dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len));
+}
+
+#if WIRELESS_EXT > 17
+static int
+dev_wlc_bufvar_set(
+ struct net_device *dev,
+ char *name,
+ char *buf, int len)
+{
+ char *ioctlbuf;
+ uint buflen;
+ int error;
+
+ ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL);
+ if (!ioctlbuf)
+ return -ENOMEM;
+
+ buflen = bcm_mkiovar(name, buf, len, ioctlbuf, MAX_WLIW_IOCTL_LEN);
+ ASSERT(buflen);
+ error = dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen);
+
+ kfree(ioctlbuf);
+ return error;
+}
+#endif
+
+static int
+dev_wlc_bufvar_get(
+ struct net_device *dev,
+ char *name,
+ char *buf, int buflen)
+{
+ char *ioctlbuf;
+ int error;
+
+ uint len;
+
+ ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL);
+ if (!ioctlbuf)
+ return -ENOMEM;
+ len = bcm_mkiovar(name, NULL, 0, ioctlbuf, MAX_WLIW_IOCTL_LEN);
+ ASSERT(len);
+ BCM_REFERENCE(len);
+ error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf, MAX_WLIW_IOCTL_LEN);
+ if (!error)
+ bcopy(ioctlbuf, buf, buflen);
+
+ kfree(ioctlbuf);
+ return (error);
+}
+
+static int
+dev_wlc_intvar_get(
+ struct net_device *dev,
+ char *name,
+ int *retval)
+{
+ union {
+ char buf[WLC_IOCTL_SMLEN];
+ int val;
+ } var;
+ int error;
+
+ uint len;
+ uint data_null;
+
+ len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf));
+ ASSERT(len);
+ error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len);
+
+ *retval = dtoh32(var.val);
+
+ return (error);
+}
+
+#if WIRELESS_EXT < 13
+struct iw_request_info
+{
+ __u16 cmd;
+ __u16 flags;
+};
+
+typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info,
+ void *wrqu, char *extra);
+#endif
+
+#if WIRELESS_EXT > 12
+static int
+wl_iw_set_leddc(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int dc = *(int *)extra;
+ int error;
+
+ error = dev_wlc_intvar_set(dev, "leddc", dc);
+ return error;
+}
+
+static int
+wl_iw_set_vlanmode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int mode = *(int *)extra;
+ int error;
+
+ mode = htod32(mode);
+ error = dev_wlc_intvar_set(dev, "vlan_mode", mode);
+ return error;
+}
+
+static int
+wl_iw_set_pm(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int pm = *(int *)extra;
+ int error;
+
+ pm = htod32(pm);
+ error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
+ return error;
+}
+#endif
+
+int
+wl_iw_send_priv_event(
+ struct net_device *dev,
+ char *flag
+)
+{
+ union iwreq_data wrqu;
+ char extra[IW_CUSTOM_MAX + 1];
+ int cmd;
+
+ cmd = IWEVCUSTOM;
+ memset(&wrqu, 0, sizeof(wrqu));
+ if (strlen(flag) > sizeof(extra))
+ return -1;
+
+ strcpy(extra, flag);
+ wrqu.data.length = strlen(extra);
+ wireless_send_event(dev, cmd, &wrqu, extra);
+ WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra));
+
+ return 0;
+}
+
+static int
+wl_iw_config_commit(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ void *zwrq,
+ char *extra
+)
+{
+ wlc_ssid_t ssid;
+ int error;
+ struct sockaddr bssid;
+
+ WL_TRACE(("%s: SIOCSIWCOMMIT\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid))))
+ return error;
+
+ ssid.SSID_len = dtoh32(ssid.SSID_len);
+
+ if (!ssid.SSID_len)
+ return 0;
+
+ bzero(&bssid, sizeof(struct sockaddr));
+ if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETHER_ADDR_LEN))) {
+ WL_ERROR(("%s: WLC_REASSOC failed (%d)\n", __FUNCTION__, error));
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+wl_iw_get_name(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *cwrq,
+ char *extra
+)
+{
+ int phytype, err;
+ uint band[3];
+ char cap[5];
+
+ WL_TRACE(("%s: SIOCGIWNAME\n", dev->name));
+
+ cap[0] = 0;
+ if ((err = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype))) < 0)
+ goto done;
+ if ((err = dev_wlc_ioctl(dev, WLC_GET_BANDLIST, band, sizeof(band))) < 0)
+ goto done;
+
+ band[0] = dtoh32(band[0]);
+ switch (phytype) {
+ case WLC_PHY_TYPE_A:
+ strcpy(cap, "a");
+ break;
+ case WLC_PHY_TYPE_B:
+ strcpy(cap, "b");
+ break;
+ case WLC_PHY_TYPE_LP:
+ case WLC_PHY_TYPE_G:
+ if (band[0] >= 2)
+ strcpy(cap, "abg");
+ else
+ strcpy(cap, "bg");
+ break;
+ case WLC_PHY_TYPE_N:
+ if (band[0] >= 2)
+ strcpy(cap, "abgn");
+ else
+ strcpy(cap, "bgn");
+ break;
+ }
+done:
+ snprintf(cwrq->name, IFNAMSIZ, "IEEE 802.11%s", cap);
+ return 0;
+}
+
+static int
+wl_iw_set_freq(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *fwrq,
+ char *extra
+)
+{
+ int error, chan;
+ uint sf = 0;
+
+ WL_TRACE(("%s: SIOCSIWFREQ\n", dev->name));
+
+ if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) {
+ chan = fwrq->m;
+ }
+
+ else {
+
+ if (fwrq->e >= 6) {
+ fwrq->e -= 6;
+ while (fwrq->e--)
+ fwrq->m *= 10;
+ } else if (fwrq->e < 6) {
+ while (fwrq->e++ < 6)
+ fwrq->m /= 10;
+ }
+
+ if (fwrq->m > 4000 && fwrq->m < 5000)
+ sf = WF_CHAN_FACTOR_4_G;
+
+ chan = wf_mhz2channel(fwrq->m, sf);
+ }
+ chan = htod32(chan);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan))))
+ return error;
+
+ return -EINPROGRESS;
+}
+
+static int
+wl_iw_get_freq(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *fwrq,
+ char *extra
+)
+{
+ channel_info_t ci;
+ int error;
+
+ WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
+ return error;
+
+ fwrq->m = dtoh32(ci.hw_channel);
+ fwrq->e = dtoh32(0);
+ return 0;
+}
+
+static int
+wl_iw_set_mode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *uwrq,
+ char *extra
+)
+{
+ int infra = 0, ap = 0, error = 0;
+
+ WL_TRACE(("%s: SIOCSIWMODE\n", dev->name));
+
+ switch (*uwrq) {
+ case IW_MODE_MASTER:
+ infra = ap = 1;
+ break;
+ case IW_MODE_ADHOC:
+ case IW_MODE_AUTO:
+ break;
+ case IW_MODE_INFRA:
+ infra = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ infra = htod32(infra);
+ ap = htod32(ap);
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra))) ||
+ (error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap))))
+ return error;
+
+ return -EINPROGRESS;
+}
+
+static int
+wl_iw_get_mode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *uwrq,
+ char *extra
+)
+{
+ int error, infra = 0, ap = 0;
+
+ WL_TRACE(("%s: SIOCGIWMODE\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra))) ||
+ (error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap))))
+ return error;
+
+ infra = dtoh32(infra);
+ ap = dtoh32(ap);
+ *uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC;
+
+ return 0;
+}
+
+static int
+wl_iw_get_range(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ struct iw_range *range = (struct iw_range *) extra;
+ static int channels[MAXCHANNEL+1];
+ wl_uint32_list_t *list = (wl_uint32_list_t *) channels;
+ wl_rateset_t rateset;
+ int error, i, k;
+ uint sf, ch;
+
+ int phytype;
+ int bw_cap = 0, sgi_tx = 0, nmode = 0;
+ channel_info_t ci;
+ uint8 nrate_list2copy = 0;
+ uint16 nrate_list[4][8] = { {13, 26, 39, 52, 78, 104, 117, 130},
+ {14, 29, 43, 58, 87, 116, 130, 144},
+ {27, 54, 81, 108, 162, 216, 243, 270},
+ {30, 60, 90, 120, 180, 240, 270, 300}};
+
+ WL_TRACE(("%s: SIOCGIWRANGE\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ dwrq->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(*range));
+
+ range->min_nwid = range->max_nwid = 0;
+
+ list->count = htod32(MAXCHANNEL);
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, sizeof(channels))))
+ return error;
+ for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) {
+ range->freq[i].i = dtoh32(list->element[i]);
+
+ ch = dtoh32(list->element[i]);
+ if (ch <= CH_MAX_2G_CHANNEL)
+ sf = WF_CHAN_FACTOR_2_4_G;
+ else
+ sf = WF_CHAN_FACTOR_5_G;
+
+ range->freq[i].m = wf_channel2mhz(ch, sf);
+ range->freq[i].e = 6;
+ }
+ range->num_frequency = range->num_channels = i;
+
+ range->max_qual.qual = 5;
+
+ range->max_qual.level = 0x100 - 200;
+
+ range->max_qual.noise = 0x100 - 200;
+
+ range->sensitivity = 65535;
+
+#if WIRELESS_EXT > 11
+
+ range->avg_qual.qual = 3;
+
+ range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD;
+
+ range->avg_qual.noise = 0x100 - 75;
+#endif
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset))))
+ return error;
+ rateset.count = dtoh32(rateset.count);
+ range->num_bitrates = rateset.count;
+ for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++)
+ range->bitrate[i] = (rateset.rates[i] & 0x7f) * 500000;
+ dev_wlc_intvar_get(dev, "nmode", &nmode);
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype))))
+ return error;
+
+ if (nmode == 1 && ((phytype == WLC_PHY_TYPE_SSN) || (phytype == WLC_PHY_TYPE_LCN) ||
+ (phytype == WLC_PHY_TYPE_LCN40))) {
+ dev_wlc_intvar_get(dev, "mimo_bw_cap", &bw_cap);
+ dev_wlc_intvar_get(dev, "sgi_tx", &sgi_tx);
+ dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t));
+ ci.hw_channel = dtoh32(ci.hw_channel);
+
+ if (bw_cap == 0 ||
+ (bw_cap == 2 && ci.hw_channel <= 14)) {
+ if (sgi_tx == 0)
+ nrate_list2copy = 0;
+ else
+ nrate_list2copy = 1;
+ }
+ if (bw_cap == 1 ||
+ (bw_cap == 2 && ci.hw_channel >= 36)) {
+ if (sgi_tx == 0)
+ nrate_list2copy = 2;
+ else
+ nrate_list2copy = 3;
+ }
+ range->num_bitrates += 8;
+ for (k = 0; i < range->num_bitrates; k++, i++) {
+
+ range->bitrate[i] = (nrate_list[nrate_list2copy][k]) * 500000;
+ }
+ }
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i))))
+ return error;
+ i = dtoh32(i);
+ if (i == WLC_PHY_TYPE_A)
+ range->throughput = 24000000;
+ else
+ range->throughput = 1500000;
+
+ range->min_rts = 0;
+ range->max_rts = 2347;
+ range->min_frag = 256;
+ range->max_frag = 2346;
+
+ range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS;
+ range->num_encoding_sizes = 4;
+ range->encoding_size[0] = WEP1_KEY_SIZE;
+ range->encoding_size[1] = WEP128_KEY_SIZE;
+#if WIRELESS_EXT > 17
+ range->encoding_size[2] = TKIP_KEY_SIZE;
+#else
+ range->encoding_size[2] = 0;
+#endif
+ range->encoding_size[3] = AES_KEY_SIZE;
+
+ range->min_pmp = 0;
+ range->max_pmp = 0;
+ range->min_pmt = 0;
+ range->max_pmt = 0;
+ range->pmp_flags = 0;
+ range->pm_capa = 0;
+
+ range->num_txpower = 2;
+ range->txpower[0] = 1;
+ range->txpower[1] = 255;
+ range->txpower_capa = IW_TXPOW_MWATT;
+
+#if WIRELESS_EXT > 10
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 19;
+
+ range->retry_capa = IW_RETRY_LIMIT;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->r_time_flags = 0;
+
+ range->min_retry = 1;
+ range->max_retry = 255;
+
+ range->min_r_time = 0;
+ range->max_r_time = 0;
+#endif
+
+#if WIRELESS_EXT > 17
+ range->enc_capa = IW_ENC_CAPA_WPA;
+ range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
+ range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
+ range->enc_capa |= IW_ENC_CAPA_WPA2;
+
+ IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
+
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE);
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCREQIE);
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCRESPIE);
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND);
+
+#if WIRELESS_EXT >= 22 && defined(IW_SCAN_CAPA_ESSID)
+
+ range->scan_capa = IW_SCAN_CAPA_ESSID;
+#endif
+#endif
+
+ return 0;
+}
+
+static int
+rssi_to_qual(int rssi)
+{
+ if (rssi <= WL_IW_RSSI_NO_SIGNAL)
+ return 0;
+ else if (rssi <= WL_IW_RSSI_VERY_LOW)
+ return 1;
+ else if (rssi <= WL_IW_RSSI_LOW)
+ return 2;
+ else if (rssi <= WL_IW_RSSI_GOOD)
+ return 3;
+ else if (rssi <= WL_IW_RSSI_VERY_GOOD)
+ return 4;
+ else
+ return 5;
+}
+
+static int
+wl_iw_set_spy(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_iw_t *iw = IW_DEV_IF(dev);
+ struct sockaddr *addr = (struct sockaddr *) extra;
+ int i;
+
+ WL_TRACE(("%s: SIOCSIWSPY\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ iw->spy_num = MIN(ARRAYSIZE(iw->spy_addr), dwrq->length);
+ for (i = 0; i < iw->spy_num; i++)
+ memcpy(&iw->spy_addr[i], addr[i].sa_data, ETHER_ADDR_LEN);
+ memset(iw->spy_qual, 0, sizeof(iw->spy_qual));
+
+ return 0;
+}
+
+static int
+wl_iw_get_spy(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_iw_t *iw = IW_DEV_IF(dev);
+ struct sockaddr *addr = (struct sockaddr *) extra;
+ struct iw_quality *qual = (struct iw_quality *) &addr[iw->spy_num];
+ int i;
+
+ WL_TRACE(("%s: SIOCGIWSPY\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ dwrq->length = iw->spy_num;
+ for (i = 0; i < iw->spy_num; i++) {
+ memcpy(addr[i].sa_data, &iw->spy_addr[i], ETHER_ADDR_LEN);
+ addr[i].sa_family = AF_UNIX;
+ memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality));
+ iw->spy_qual[i].updated = 0;
+ }
+
+ return 0;
+}
+
+static int
+wl_iw_set_wap(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *awrq,
+ char *extra
+)
+{
+ int error = -EINVAL;
+#ifdef BCMDBG
+
+#endif
+
+ WL_TRACE(("%s: SIOCSIWAP\n", dev->name));
+
+ if (awrq->sa_family != ARPHRD_ETHER) {
+ WL_ERROR(("%s: Invalid Header...sa_family\n", __FUNCTION__));
+ return -EINVAL;
+ }
+
+ if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) {
+ scb_val_t scbval;
+ bzero(&scbval, sizeof(scb_val_t));
+ if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) {
+ WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error));
+ }
+ return 0;
+ }
+
+ if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, awrq->sa_data, ETHER_ADDR_LEN))) {
+ WL_ERROR(("%s: WLC_REASSOC failed (%d).\n", __FUNCTION__, error));
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+wl_iw_get_wap(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *awrq,
+ char *extra
+)
+{
+ WL_TRACE(("%s: SIOCGIWAP\n", dev->name));
+
+ awrq->sa_family = ARPHRD_ETHER;
+ memset(awrq->sa_data, 0, ETHER_ADDR_LEN);
+
+ (void) dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETHER_ADDR_LEN);
+
+ return 0;
+}
+
+#if WIRELESS_EXT > 17
+static int
+wl_iw_mlme(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *awrq,
+ char *extra
+)
+{
+ struct iw_mlme *mlme;
+ scb_val_t scbval;
+ int error = -EINVAL;
+
+ WL_TRACE(("%s: SIOCSIWMLME\n", dev->name));
+
+ mlme = (struct iw_mlme *)extra;
+ if (mlme == NULL) {
+ WL_ERROR(("Invalid ioctl data.\n"));
+ return error;
+ }
+
+ scbval.val = mlme->reason_code;
+ bcopy(&mlme->addr.sa_data, &scbval.ea, ETHER_ADDR_LEN);
+
+ if (mlme->cmd == IW_MLME_DISASSOC) {
+ scbval.val = htod32(scbval.val);
+ error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
+ }
+ else if (mlme->cmd == IW_MLME_DEAUTH) {
+ scbval.val = htod32(scbval.val);
+ error = dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval,
+ sizeof(scb_val_t));
+ }
+ else {
+ WL_ERROR(("%s: Invalid ioctl data.\n", __FUNCTION__));
+ return error;
+ }
+
+ return error;
+}
+#endif
+
+static int
+wl_iw_get_aplist(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_scan_results_t *list;
+ struct sockaddr *addr = (struct sockaddr *) extra;
+ struct iw_quality qual[IW_MAX_AP];
+ wl_bss_info_t *bi = NULL;
+ int error, i;
+ uint buflen = dwrq->length;
+
+ WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ list = kmalloc(buflen, GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+ memset(list, 0, buflen);
+ list->buflen = htod32(buflen);
+ if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) {
+ WL_ERROR(("%d: Scan results error %d\n", __LINE__, error));
+ kfree(list);
+ return error;
+ }
+ list->buflen = dtoh32(list->buflen);
+ list->version = dtoh32(list->version);
+ list->count = dtoh32(list->count);
+ ASSERT(list->version == WL_BSS_INFO_VERSION);
+
+ for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
+ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
+ ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
+ buflen));
+
+ if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
+ continue;
+
+ memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+ addr[dwrq->length].sa_family = ARPHRD_ETHER;
+ qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI));
+ qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
+ qual[dwrq->length].noise = 0x100 + bi->phy_noise;
+
+#if WIRELESS_EXT > 18
+ qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+#else
+ qual[dwrq->length].updated = 7;
+#endif
+
+ dwrq->length++;
+ }
+
+ kfree(list);
+
+ if (dwrq->length) {
+ memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
+
+ dwrq->flags = 1;
+ }
+
+ return 0;
+}
+
+#if WIRELESS_EXT > 13
+static int
+wl_iw_set_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ wlc_ssid_t ssid;
+
+ WL_TRACE(("%s: SIOCSIWSCAN\n", dev->name));
+
+ memset(&ssid, 0, sizeof(ssid));
+
+#if WIRELESS_EXT > 17
+
+ if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ struct iw_scan_req *req = (struct iw_scan_req *)extra;
+ ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
+ memcpy(ssid.SSID, req->essid, ssid.SSID_len);
+ ssid.SSID_len = htod32(ssid.SSID_len);
+ }
+ }
+#endif
+
+ (void) dev_wlc_ioctl(dev, WLC_SCAN, &ssid, sizeof(ssid));
+
+ return 0;
+}
+
+#if WIRELESS_EXT > 17
+static bool
+ie_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, int *tlvs_len)
+{
+
+ uint8 *ie = *wpaie;
+
+ if ((ie[1] >= 6) &&
+ !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) {
+ return TRUE;
+ }
+
+ ie += ie[1] + 2;
+
+ *tlvs_len -= (int)(ie - *tlvs);
+
+ *tlvs = ie;
+ return FALSE;
+}
+
+static bool
+ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len)
+{
+
+ uint8 *ie = *wpsie;
+
+ if ((ie[1] >= 4) &&
+ !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) {
+ return TRUE;
+ }
+
+ ie += ie[1] + 2;
+
+ *tlvs_len -= (int)(ie - *tlvs);
+
+ *tlvs = ie;
+ return FALSE;
+}
+#endif
+
+static int
+wl_iw_handle_scanresults_ies(char **event_p, char *end,
+ struct iw_request_info *info, wl_bss_info_t *bi)
+{
+#if WIRELESS_EXT > 17
+ struct iw_event iwe;
+ char *event;
+
+ event = *event_p;
+ if (bi->ie_length) {
+
+ bcm_tlv_t *ie;
+ uint8 *ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+ int ptr_len = bi->ie_length;
+
+ if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID))) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie->len + 2;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+ }
+ ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+
+ while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
+
+ if (ie_is_wps_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie->len + 2;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+ break;
+ }
+ }
+
+ ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+ ptr_len = bi->ie_length;
+ while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
+ if (ie_is_wpa_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie->len + 2;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+ break;
+ }
+ }
+
+ *event_p = event;
+ }
+
+#endif
+ return 0;
+}
+static int
+wl_iw_get_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ channel_info_t ci;
+ wl_scan_results_t *list;
+ struct iw_event iwe;
+ wl_bss_info_t *bi = NULL;
+ int error, i, j;
+ char *event = extra, *end = extra + dwrq->length, *value;
+ uint buflen = dwrq->length;
+
+ WL_TRACE(("%s: SIOCGIWSCAN\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
+ return error;
+ ci.scan_channel = dtoh32(ci.scan_channel);
+ if (ci.scan_channel)
+ return -EAGAIN;
+
+ list = kmalloc(buflen, GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+ memset(list, 0, buflen);
+ list->buflen = htod32(buflen);
+ if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) {
+ kfree(list);
+ return error;
+ }
+ list->buflen = dtoh32(list->buflen);
+ list->version = dtoh32(list->version);
+ list->count = dtoh32(list->count);
+
+ ASSERT(list->version == WL_BSS_INFO_VERSION);
+
+ for (i = 0; i < list->count && i < IW_MAX_AP; i++) {
+ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
+ ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
+ buflen));
+
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
+
+ iwe.u.data.length = dtoh32(bi->SSID_len);
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
+
+ if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
+ iwe.cmd = SIOCGIWMODE;
+ if (dtoh16(bi->capability) & DOT11_CAP_ESS)
+ iwe.u.mode = IW_MODE_INFRA;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
+ }
+
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec),
+ CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ?
+ WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
+ iwe.u.freq.e = 6;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
+
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
+ iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
+ iwe.u.qual.noise = 0x100 + bi->phy_noise;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
+
+ wl_iw_handle_scanresults_ies(&event, end, info, bi);
+
+ iwe.cmd = SIOCGIWENCODE;
+ if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
+
+ if (bi->rateset.count) {
+ value = event + IW_EV_LCP_LEN;
+ iwe.cmd = SIOCGIWRATE;
+
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+ for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
+ iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
+ value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ event = value;
+ }
+ }
+
+ kfree(list);
+
+ dwrq->length = event - extra;
+ dwrq->flags = 0;
+
+ return 0;
+}
+
+#endif
+
+static int
+wl_iw_set_essid(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wlc_ssid_t ssid;
+ int error;
+
+ WL_TRACE(("%s: SIOCSIWESSID\n", dev->name));
+
+ memset(&ssid, 0, sizeof(ssid));
+ if (dwrq->length && extra) {
+#if WIRELESS_EXT > 20
+ ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length);
+#else
+ ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length-1);
+#endif
+ memcpy(ssid.SSID, extra, ssid.SSID_len);
+ ssid.SSID_len = htod32(ssid.SSID_len);
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid))))
+ return error;
+ }
+
+ else {
+ scb_val_t scbval;
+ bzero(&scbval, sizeof(scb_val_t));
+ if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t))))
+ return error;
+ }
+ return 0;
+}
+
+static int
+wl_iw_get_essid(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wlc_ssid_t ssid;
+ int error;
+
+ WL_TRACE(("%s: SIOCGIWESSID\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) {
+ WL_ERROR(("Error getting the SSID\n"));
+ return error;
+ }
+
+ ssid.SSID_len = dtoh32(ssid.SSID_len);
+
+ memcpy(extra, ssid.SSID, ssid.SSID_len);
+
+ dwrq->length = ssid.SSID_len;
+
+ dwrq->flags = 1;
+
+ return 0;
+}
+
+static int
+wl_iw_set_nick(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_iw_t *iw = IW_DEV_IF(dev);
+ WL_TRACE(("%s: SIOCSIWNICKN\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ if (dwrq->length > sizeof(iw->nickname))
+ return -E2BIG;
+
+ memcpy(iw->nickname, extra, dwrq->length);
+ iw->nickname[dwrq->length - 1] = '\0';
+
+ return 0;
+}
+
+static int
+wl_iw_get_nick(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_iw_t *iw = IW_DEV_IF(dev);
+ WL_TRACE(("%s: SIOCGIWNICKN\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ strcpy(extra, iw->nickname);
+ dwrq->length = strlen(extra) + 1;
+
+ return 0;
+}
+
+static int wl_iw_set_rate(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ wl_rateset_t rateset;
+ int error, rate, i, error_bg, error_a;
+
+ WL_TRACE(("%s: SIOCSIWRATE\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset))))
+ return error;
+
+ rateset.count = dtoh32(rateset.count);
+
+ if (vwrq->value < 0) {
+
+ rate = rateset.rates[rateset.count - 1] & 0x7f;
+ } else if (vwrq->value < rateset.count) {
+
+ rate = rateset.rates[vwrq->value] & 0x7f;
+ } else {
+
+ rate = vwrq->value / 500000;
+ }
+
+ if (vwrq->fixed) {
+
+ error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate);
+ error_a = dev_wlc_intvar_set(dev, "a_rate", rate);
+
+ if (error_bg && error_a)
+ return (error_bg | error_a);
+ } else {
+
+ error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0);
+
+ error_a = dev_wlc_intvar_set(dev, "a_rate", 0);
+
+ if (error_bg && error_a)
+ return (error_bg | error_a);
+
+ for (i = 0; i < rateset.count; i++)
+ if ((rateset.rates[i] & 0x7f) > rate)
+ break;
+ rateset.count = htod32(i);
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset, sizeof(rateset))))
+ return error;
+ }
+
+ return 0;
+}
+
+static int wl_iw_get_rate(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, rate;
+
+ WL_TRACE(("%s: SIOCGIWRATE\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate))))
+ return error;
+ rate = dtoh32(rate);
+ vwrq->value = rate * 500000;
+
+ return 0;
+}
+
+static int
+wl_iw_set_rts(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, rts;
+
+ WL_TRACE(("%s: SIOCSIWRTS\n", dev->name));
+
+ if (vwrq->disabled)
+ rts = DOT11_DEFAULT_RTS_LEN;
+ else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN)
+ return -EINVAL;
+ else
+ rts = vwrq->value;
+
+ if ((error = dev_wlc_intvar_set(dev, "rtsthresh", rts)))
+ return error;
+
+ return 0;
+}
+
+static int
+wl_iw_get_rts(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, rts;
+
+ WL_TRACE(("%s: SIOCGIWRTS\n", dev->name));
+
+ if ((error = dev_wlc_intvar_get(dev, "rtsthresh", &rts)))
+ return error;
+
+ vwrq->value = rts;
+ vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN);
+ vwrq->fixed = 1;
+
+ return 0;
+}
+
+static int
+wl_iw_set_frag(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, frag;
+
+ WL_TRACE(("%s: SIOCSIWFRAG\n", dev->name));
+
+ if (vwrq->disabled)
+ frag = DOT11_DEFAULT_FRAG_LEN;
+ else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN)
+ return -EINVAL;
+ else
+ frag = vwrq->value;
+
+ if ((error = dev_wlc_intvar_set(dev, "fragthresh", frag)))
+ return error;
+
+ return 0;
+}
+
+static int
+wl_iw_get_frag(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, fragthreshold;
+
+ WL_TRACE(("%s: SIOCGIWFRAG\n", dev->name));
+
+ if ((error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold)))
+ return error;
+
+ vwrq->value = fragthreshold;
+ vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN);
+ vwrq->fixed = 1;
+
+ return 0;
+}
+
+static int
+wl_iw_set_txpow(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, disable;
+ uint16 txpwrmw;
+ WL_TRACE(("%s: SIOCSIWTXPOW\n", dev->name));
+
+ disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0;
+ disable += WL_RADIO_SW_DISABLE << 16;
+
+ disable = htod32(disable);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable))))
+ return error;
+
+ if (disable & WL_RADIO_SW_DISABLE)
+ return 0;
+
+ if (!(vwrq->flags & IW_TXPOW_MWATT))
+ return -EINVAL;
+
+ if (vwrq->value < 0)
+ return 0;
+
+ if (vwrq->value > 0xffff) txpwrmw = 0xffff;
+ else txpwrmw = (uint16)vwrq->value;
+
+ error = dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw)));
+ return error;
+}
+
+static int
+wl_iw_get_txpow(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, disable, txpwrdbm;
+ uint8 result;
+
+ WL_TRACE(("%s: SIOCGIWTXPOW\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable))) ||
+ (error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm)))
+ return error;
+
+ disable = dtoh32(disable);
+ result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE);
+ vwrq->value = (int32)bcm_qdbm_to_mw(result);
+ vwrq->fixed = 0;
+ vwrq->disabled = (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0;
+ vwrq->flags = IW_TXPOW_MWATT;
+
+ return 0;
+}
+
+#if WIRELESS_EXT > 10
+static int
+wl_iw_set_retry(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, lrl, srl;
+
+ WL_TRACE(("%s: SIOCSIWRETRY\n", dev->name));
+
+ if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME))
+ return -EINVAL;
+
+ if (vwrq->flags & IW_RETRY_LIMIT) {
+
+#if WIRELESS_EXT > 20
+ if ((vwrq->flags & IW_RETRY_LONG) ||(vwrq->flags & IW_RETRY_MAX) ||
+ !((vwrq->flags & IW_RETRY_SHORT) || (vwrq->flags & IW_RETRY_MIN))) {
+#else
+ if ((vwrq->flags & IW_RETRY_MAX) || !(vwrq->flags & IW_RETRY_MIN)) {
+#endif
+
+ lrl = htod32(vwrq->value);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl, sizeof(lrl))))
+ return error;
+ }
+
+#if WIRELESS_EXT > 20
+ if ((vwrq->flags & IW_RETRY_SHORT) ||(vwrq->flags & IW_RETRY_MIN) ||
+ !((vwrq->flags & IW_RETRY_LONG) || (vwrq->flags & IW_RETRY_MAX))) {
+#else
+ if ((vwrq->flags & IW_RETRY_MIN) || !(vwrq->flags & IW_RETRY_MAX)) {
+#endif
+
+ srl = htod32(vwrq->value);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl, sizeof(srl))))
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+static int
+wl_iw_get_retry(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, lrl, srl;
+
+ WL_TRACE(("%s: SIOCGIWRETRY\n", dev->name));
+
+ vwrq->disabled = 0;
+
+ if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
+ return -EINVAL;
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl))) ||
+ (error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl))))
+ return error;
+
+ lrl = dtoh32(lrl);
+ srl = dtoh32(srl);
+
+ if (vwrq->flags & IW_RETRY_MAX) {
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ vwrq->value = lrl;
+ } else {
+ vwrq->flags = IW_RETRY_LIMIT;
+ vwrq->value = srl;
+ if (srl != lrl)
+ vwrq->flags |= IW_RETRY_MIN;
+ }
+
+ return 0;
+}
+#endif
+
+static int
+wl_iw_set_encode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_wsec_key_t key;
+ int error, val, wsec;
+
+ WL_TRACE(("%s: SIOCSIWENCODE\n", dev->name));
+
+ memset(&key, 0, sizeof(key));
+
+ if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
+
+ for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
+ val = htod32(key.index);
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
+ return error;
+ val = dtoh32(val);
+ if (val)
+ break;
+ }
+
+ if (key.index == DOT11_MAX_DEFAULT_KEYS)
+ key.index = 0;
+ } else {
+ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ if (key.index >= DOT11_MAX_DEFAULT_KEYS)
+ return -EINVAL;
+ }
+
+ wsec = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED;
+
+ if ((error = dev_wlc_intvar_set(dev, "wsec", wsec)))
+ return error;
+
+ if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) {
+
+ val = htod32(key.index);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val, sizeof(val))))
+ return error;
+ } else {
+ key.len = dwrq->length;
+
+ if (dwrq->length > sizeof(key.data))
+ return -EINVAL;
+
+ memcpy(key.data, extra, dwrq->length);
+
+ key.flags = WL_PRIMARY_KEY;
+ switch (key.len) {
+ case WEP1_KEY_SIZE:
+ key.algo = CRYPTO_ALGO_WEP1;
+ break;
+ case WEP128_KEY_SIZE:
+ key.algo = CRYPTO_ALGO_WEP128;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
+ case TKIP_KEY_SIZE:
+ key.algo = CRYPTO_ALGO_TKIP;
+ break;
+#endif
+ case AES_KEY_SIZE:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ swap_key_from_BE(&key);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))
+ return error;
+ }
+
+ val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0;
+ val = htod32(val);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val))))
+ return error;
+
+ return 0;
+}
+
+static int
+wl_iw_get_encode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_wsec_key_t key;
+ int error, val, wsec, auth;
+
+ WL_TRACE(("%s: SIOCGIWENCODE\n", dev->name));
+
+ bzero(&key, sizeof(wl_wsec_key_t));
+
+ if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
+
+ for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
+ val = key.index;
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
+ return error;
+ val = dtoh32(val);
+ if (val)
+ break;
+ }
+ } else
+ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+ if (key.index >= DOT11_MAX_DEFAULT_KEYS)
+ key.index = 0;
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec))) ||
+ (error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth))))
+ return error;
+
+ swap_key_to_BE(&key);
+
+ wsec = dtoh32(wsec);
+ auth = dtoh32(auth);
+
+ dwrq->length = MIN(IW_ENCODING_TOKEN_MAX, key.len);
+
+ dwrq->flags = key.index + 1;
+ if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))) {
+
+ dwrq->flags |= IW_ENCODE_DISABLED;
+ }
+ if (auth) {
+
+ dwrq->flags |= IW_ENCODE_RESTRICTED;
+ }
+
+ if (dwrq->length && extra)
+ memcpy(extra, key.data, dwrq->length);
+
+ return 0;
+}
+
+static int
+wl_iw_set_power(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, pm;
+
+ WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name));
+
+ pm = vwrq->disabled ? PM_OFF : PM_MAX;
+
+ pm = htod32(pm);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm))))
+ return error;
+
+ return 0;
+}
+
+static int
+wl_iw_get_power(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, pm;
+
+ WL_TRACE(("%s: SIOCGIWPOWER\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))))
+ return error;
+
+ pm = dtoh32(pm);
+ vwrq->disabled = pm ? 0 : 1;
+ vwrq->flags = IW_POWER_ALL_R;
+
+ return 0;
+}
+
+#if WIRELESS_EXT > 17
+static int
+wl_iw_set_wpaie(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *iwp,
+ char *extra
+)
+{
+ dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length);
+
+ return 0;
+}
+
+static int
+wl_iw_get_wpaie(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *iwp,
+ char *extra
+)
+{
+ WL_TRACE(("%s: SIOCGIWGENIE\n", dev->name));
+ iwp->length = 64;
+ dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length);
+ return 0;
+}
+
+static int
+wl_iw_set_encodeext(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_wsec_key_t key;
+ int error;
+ struct iw_encode_ext *iwe;
+
+ WL_TRACE(("%s: SIOCSIWENCODEEXT\n", dev->name));
+
+ memset(&key, 0, sizeof(key));
+ iwe = (struct iw_encode_ext *)extra;
+
+ if (dwrq->flags & IW_ENCODE_DISABLED) {
+
+ }
+
+ key.index = 0;
+ if (dwrq->flags & IW_ENCODE_INDEX)
+ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+ key.len = iwe->key_len;
+
+ if (!ETHER_ISMULTI(iwe->addr.sa_data))
+ bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea, ETHER_ADDR_LEN);
+
+ if (key.len == 0) {
+ if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ WL_WSEC(("Changing the the primary Key to %d\n", key.index));
+
+ key.index = htod32(key.index);
+ error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY,
+ &key.index, sizeof(key.index));
+ if (error)
+ return error;
+ }
+
+ else {
+ swap_key_from_BE(&key);
+ dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+ }
+ }
+
+ else {
+ if (iwe->key_len > sizeof(key.data))
+ return -EINVAL;
+
+ WL_WSEC(("Setting the key index %d\n", key.index));
+ if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ WL_WSEC(("key is a Primary Key\n"));
+ key.flags = WL_PRIMARY_KEY;
+ }
+
+ bcopy((void *)iwe->key, key.data, iwe->key_len);
+
+ if (iwe->alg == IW_ENCODE_ALG_TKIP) {
+ uint8 keybuf[8];
+ bcopy(&key.data[24], keybuf, sizeof(keybuf));
+ bcopy(&key.data[16], &key.data[24], sizeof(keybuf));
+ bcopy(keybuf, &key.data[16], sizeof(keybuf));
+ }
+
+ if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
+ uchar *ivptr;
+ ivptr = (uchar *)iwe->rx_seq;
+ key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
+ (ivptr[3] << 8) | ivptr[2];
+ key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
+ key.iv_initialized = TRUE;
+ }
+
+ switch (iwe->alg) {
+ case IW_ENCODE_ALG_NONE:
+ key.algo = CRYPTO_ALGO_OFF;
+ break;
+ case IW_ENCODE_ALG_WEP:
+ if (iwe->key_len == WEP1_KEY_SIZE)
+ key.algo = CRYPTO_ALGO_WEP1;
+ else
+ key.algo = CRYPTO_ALGO_WEP128;
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ key.algo = CRYPTO_ALGO_TKIP;
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ break;
+ default:
+ break;
+ }
+ swap_key_from_BE(&key);
+
+ error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+ if (error)
+ return error;
+ }
+ return 0;
+}
+
+#if WIRELESS_EXT > 17
+struct {
+ pmkid_list_t pmkids;
+ pmkid_t foo[MAXPMKID-1];
+} pmkid_list;
+static int
+wl_iw_set_pmksa(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ struct iw_pmksa *iwpmksa;
+ uint i;
+ char eabuf[ETHER_ADDR_STR_LEN];
+ pmkid_t * pmkid_array = pmkid_list.pmkids.pmkid;
+
+ WL_TRACE(("%s: SIOCSIWPMKSA\n", dev->name));
+ iwpmksa = (struct iw_pmksa *)extra;
+ bzero((char *)eabuf, ETHER_ADDR_STR_LEN);
+ if (iwpmksa->cmd == IW_PMKSA_FLUSH) {
+ WL_TRACE(("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n"));
+ bzero((char *)&pmkid_list, sizeof(pmkid_list));
+ }
+ if (iwpmksa->cmd == IW_PMKSA_REMOVE) {
+ pmkid_list_t pmkid, *pmkidptr;
+ pmkidptr = &pmkid;
+ bcopy(&iwpmksa->bssid.sa_data[0], &pmkidptr->pmkid[0].BSSID, ETHER_ADDR_LEN);
+ bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID, WPA2_PMKID_LEN);
+ {
+ uint j;
+ WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_REMOVE - PMKID: %s = ",
+ bcm_ether_ntoa(&pmkidptr->pmkid[0].BSSID,
+ eabuf)));
+ for (j = 0; j < WPA2_PMKID_LEN; j++)
+ WL_TRACE(("%02x ", pmkidptr->pmkid[0].PMKID[j]));
+ WL_TRACE(("\n"));
+ }
+ for (i = 0; i < pmkid_list.pmkids.npmkid; i++)
+ if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID,
+ ETHER_ADDR_LEN))
+ break;
+ for (; i < pmkid_list.pmkids.npmkid; i++) {
+ bcopy(&pmkid_array[i+1].BSSID,
+ &pmkid_array[i].BSSID,
+ ETHER_ADDR_LEN);
+ bcopy(&pmkid_array[i+1].PMKID,
+ &pmkid_array[i].PMKID,
+ WPA2_PMKID_LEN);
+ }
+ pmkid_list.pmkids.npmkid--;
+ }
+ if (iwpmksa->cmd == IW_PMKSA_ADD) {
+ bcopy(&iwpmksa->bssid.sa_data[0],
+ &pmkid_array[pmkid_list.pmkids.npmkid].BSSID,
+ ETHER_ADDR_LEN);
+ bcopy(&iwpmksa->pmkid[0], &pmkid_array[pmkid_list.pmkids.npmkid].PMKID,
+ WPA2_PMKID_LEN);
+ {
+ uint j;
+ uint k;
+ k = pmkid_list.pmkids.npmkid;
+ BCM_REFERENCE(k);
+ WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ",
+ bcm_ether_ntoa(&pmkid_array[k].BSSID,
+ eabuf)));
+ for (j = 0; j < WPA2_PMKID_LEN; j++)
+ WL_TRACE(("%02x ", pmkid_array[k].PMKID[j]));
+ WL_TRACE(("\n"));
+ }
+ pmkid_list.pmkids.npmkid++;
+ }
+ WL_TRACE(("PRINTING pmkid LIST - No of elements %d\n", pmkid_list.pmkids.npmkid));
+ for (i = 0; i < pmkid_list.pmkids.npmkid; i++) {
+ uint j;
+ WL_TRACE(("PMKID[%d]: %s = ", i,
+ bcm_ether_ntoa(&pmkid_array[i].BSSID,
+ eabuf)));
+ for (j = 0; j < WPA2_PMKID_LEN; j++)
+ WL_TRACE(("%02x ", pmkid_array[i].PMKID[j]));
+ printf("\n");
+ }
+ WL_TRACE(("\n"));
+ dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list, sizeof(pmkid_list));
+ return 0;
+}
+#endif
+
+static int
+wl_iw_get_encodeext(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ WL_TRACE(("%s: SIOCGIWENCODEEXT\n", dev->name));
+ return 0;
+}
+
+static int
+wl_iw_set_wpaauth(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error = 0;
+ int paramid;
+ int paramval;
+ uint32 cipher_combined;
+ int val = 0;
+ wl_iw_t *iw = IW_DEV_IF(dev);
+
+ WL_TRACE(("%s: SIOCSIWAUTH\n", dev->name));
+
+ paramid = vwrq->flags & IW_AUTH_INDEX;
+ paramval = vwrq->value;
+
+ WL_TRACE(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n",
+ dev->name, paramid, paramval));
+
+ switch (paramid) {
+
+ case IW_AUTH_WPA_VERSION:
+
+ if (paramval & IW_AUTH_WPA_VERSION_DISABLED)
+ val = WPA_AUTH_DISABLED;
+ else if (paramval & (IW_AUTH_WPA_VERSION_WPA))
+ val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
+ else if (paramval & IW_AUTH_WPA_VERSION_WPA2)
+ val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+ WL_TRACE(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val));
+ if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val)))
+ return error;
+ break;
+
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+
+ if (paramid == IW_AUTH_CIPHER_PAIRWISE) {
+ iw->pwsec = paramval;
+ }
+ else {
+ iw->gwsec = paramval;
+ }
+
+ if ((error = dev_wlc_intvar_get(dev, "wsec", &val)))
+ return error;
+
+ cipher_combined = iw->gwsec | iw->pwsec;
+ val &= ~(WEP_ENABLED | TKIP_ENABLED | AES_ENABLED);
+ if (cipher_combined & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
+ val |= WEP_ENABLED;
+ if (cipher_combined & IW_AUTH_CIPHER_TKIP)
+ val |= TKIP_ENABLED;
+ if (cipher_combined & IW_AUTH_CIPHER_CCMP)
+ val |= AES_ENABLED;
+
+ if (iw->privacy_invoked && !val) {
+ WL_WSEC(("%s: %s: 'Privacy invoked' TRUE but clearing wsec, assuming "
+ "we're a WPS enrollee\n", dev->name, __FUNCTION__));
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
+ WL_WSEC(("Failed to set iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ } else if (val) {
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+ WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ }
+
+ if ((error = dev_wlc_intvar_set(dev, "wsec", val)))
+ return error;
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+ return error;
+
+ if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+ if (paramval & IW_AUTH_KEY_MGMT_PSK)
+ val = WPA_AUTH_PSK;
+ else
+ val = WPA_AUTH_UNSPECIFIED;
+ }
+ else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+ if (paramval & IW_AUTH_KEY_MGMT_PSK)
+ val = WPA2_AUTH_PSK;
+ else
+ val = WPA2_AUTH_UNSPECIFIED;
+ }
+ WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val));
+ if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val)))
+ return error;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ dev_wlc_bufvar_set(dev, "tkip_countermeasures", (char *)&paramval, 1);
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+
+ WL_ERROR(("Setting the D11auth %d\n", paramval));
+ if (paramval & IW_AUTH_ALG_OPEN_SYSTEM)
+ val = 0;
+ else if (paramval & IW_AUTH_ALG_SHARED_KEY)
+ val = 1;
+ else
+ error = 1;
+ if (!error && (error = dev_wlc_intvar_set(dev, "auth", val)))
+ return error;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ if (paramval == 0) {
+ val = 0;
+ WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val));
+ error = dev_wlc_intvar_set(dev, "wpa_auth", val);
+ return error;
+ }
+ else {
+
+ }
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ dev_wlc_bufvar_set(dev, "wsec_restrict", (char *)&paramval, 1);
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", (char *)&paramval, 1);
+ break;
+
+#if WIRELESS_EXT > 17
+
+ case IW_AUTH_ROAMING_CONTROL:
+ WL_TRACE(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
+
+ break;
+
+ case IW_AUTH_PRIVACY_INVOKED: {
+ int wsec;
+
+ if (paramval == 0) {
+ iw->privacy_invoked = FALSE;
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+ WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ } else {
+ iw->privacy_invoked = TRUE;
+ if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec)))
+ return error;
+
+ if (!WSEC_ENABLED(wsec)) {
+
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
+ WL_WSEC(("Failed to set iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ } else {
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+ WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ }
+ }
+ break;
+ }
+
+#endif
+
+ default:
+ break;
+ }
+ return 0;
+}
+#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK))
+
+static int
+wl_iw_get_wpaauth(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error;
+ int paramid;
+ int paramval = 0;
+ int val;
+ wl_iw_t *iw = IW_DEV_IF(dev);
+
+ WL_TRACE(("%s: SIOCGIWAUTH\n", dev->name));
+
+ paramid = vwrq->flags & IW_AUTH_INDEX;
+
+ switch (paramid) {
+ case IW_AUTH_WPA_VERSION:
+
+ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+ return error;
+ if (val & (WPA_AUTH_NONE | WPA_AUTH_DISABLED))
+ paramval = IW_AUTH_WPA_VERSION_DISABLED;
+ else if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED))
+ paramval = IW_AUTH_WPA_VERSION_WPA;
+ else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED))
+ paramval = IW_AUTH_WPA_VERSION_WPA2;
+ break;
+
+ case IW_AUTH_CIPHER_PAIRWISE:
+ paramval = iw->pwsec;
+ break;
+
+ case IW_AUTH_CIPHER_GROUP:
+ paramval = iw->gwsec;
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+
+ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+ return error;
+ if (VAL_PSK(val))
+ paramval = IW_AUTH_KEY_MGMT_PSK;
+ else
+ paramval = IW_AUTH_KEY_MGMT_802_1X;
+
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ dev_wlc_bufvar_get(dev, "tkip_countermeasures", (char *)&paramval, 1);
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ dev_wlc_bufvar_get(dev, "wsec_restrict", (char *)&paramval, 1);
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", (char *)&paramval, 1);
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+
+ if ((error = dev_wlc_intvar_get(dev, "auth", &val)))
+ return error;
+ if (!val)
+ paramval = IW_AUTH_ALG_OPEN_SYSTEM;
+ else
+ paramval = IW_AUTH_ALG_SHARED_KEY;
+ break;
+ case IW_AUTH_WPA_ENABLED:
+ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+ return error;
+ if (val)
+ paramval = TRUE;
+ else
+ paramval = FALSE;
+ break;
+
+#if WIRELESS_EXT > 17
+
+ case IW_AUTH_ROAMING_CONTROL:
+ WL_ERROR(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
+
+ break;
+
+ case IW_AUTH_PRIVACY_INVOKED:
+ paramval = iw->privacy_invoked;
+ break;
+
+#endif
+ }
+ vwrq->value = paramval;
+ return 0;
+}
+#endif
+
+static const iw_handler wl_iw_handler[] =
+{
+ (iw_handler) wl_iw_config_commit,
+ (iw_handler) wl_iw_get_name,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_freq,
+ (iw_handler) wl_iw_get_freq,
+ (iw_handler) wl_iw_set_mode,
+ (iw_handler) wl_iw_get_mode,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_get_range,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_spy,
+ (iw_handler) wl_iw_get_spy,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_wap,
+ (iw_handler) wl_iw_get_wap,
+#if WIRELESS_EXT > 17
+ (iw_handler) wl_iw_mlme,
+#else
+ (iw_handler) NULL,
+#endif
+ (iw_handler) wl_iw_get_aplist,
+#if WIRELESS_EXT > 13
+ (iw_handler) wl_iw_set_scan,
+ (iw_handler) wl_iw_get_scan,
+#else
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+#endif
+ (iw_handler) wl_iw_set_essid,
+ (iw_handler) wl_iw_get_essid,
+ (iw_handler) wl_iw_set_nick,
+ (iw_handler) wl_iw_get_nick,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_rate,
+ (iw_handler) wl_iw_get_rate,
+ (iw_handler) wl_iw_set_rts,
+ (iw_handler) wl_iw_get_rts,
+ (iw_handler) wl_iw_set_frag,
+ (iw_handler) wl_iw_get_frag,
+ (iw_handler) wl_iw_set_txpow,
+ (iw_handler) wl_iw_get_txpow,
+#if WIRELESS_EXT > 10
+ (iw_handler) wl_iw_set_retry,
+ (iw_handler) wl_iw_get_retry,
+#endif
+ (iw_handler) wl_iw_set_encode,
+ (iw_handler) wl_iw_get_encode,
+ (iw_handler) wl_iw_set_power,
+ (iw_handler) wl_iw_get_power,
+#if WIRELESS_EXT > 17
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_wpaie,
+ (iw_handler) wl_iw_get_wpaie,
+ (iw_handler) wl_iw_set_wpaauth,
+ (iw_handler) wl_iw_get_wpaauth,
+ (iw_handler) wl_iw_set_encodeext,
+ (iw_handler) wl_iw_get_encodeext,
+ (iw_handler) wl_iw_set_pmksa,
+#endif
+};
+
+#if WIRELESS_EXT > 12
+enum {
+ WL_IW_SET_LEDDC = SIOCIWFIRSTPRIV,
+ WL_IW_SET_VLANMODE,
+ WL_IW_SET_PM
+};
+
+static iw_handler wl_iw_priv_handler[] = {
+ wl_iw_set_leddc,
+ wl_iw_set_vlanmode,
+ wl_iw_set_pm
+};
+
+static struct iw_priv_args wl_iw_priv_args[] = {
+ {
+ WL_IW_SET_LEDDC,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ 0,
+ "set_leddc"
+ },
+ {
+ WL_IW_SET_VLANMODE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ 0,
+ "set_vlanmode"
+ },
+ {
+ WL_IW_SET_PM,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ 0,
+ "set_pm"
+ }
+};
+
+const struct iw_handler_def wl_iw_handler_def =
+{
+ .num_standard = ARRAYSIZE(wl_iw_handler),
+ .num_private = ARRAY_SIZE(wl_iw_priv_handler),
+ .num_private_args = ARRAY_SIZE(wl_iw_priv_args),
+ .standard = (iw_handler *) wl_iw_handler,
+ .private = wl_iw_priv_handler,
+ .private_args = wl_iw_priv_args,
+#if WIRELESS_EXT >= 19
+ get_wireless_stats: wl_get_wireless_stats,
+#endif
+ };
+#endif
+
+int
+wl_iw_ioctl(
+ struct net_device *dev,
+ struct ifreq *rq,
+ int cmd
+)
+{
+ struct iwreq *wrq = (struct iwreq *) rq;
+ struct iw_request_info info;
+ iw_handler handler;
+ char *extra = NULL;
+ size_t token_size = 1;
+ int max_tokens = 0, ret = 0;
+
+ if (cmd < SIOCIWFIRST ||
+ IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) ||
+ !(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)]))
+ return -EOPNOTSUPP;
+
+ switch (cmd) {
+
+ case SIOCSIWESSID:
+ case SIOCGIWESSID:
+ case SIOCSIWNICKN:
+ case SIOCGIWNICKN:
+ max_tokens = IW_ESSID_MAX_SIZE + 1;
+ break;
+
+ case SIOCSIWENCODE:
+ case SIOCGIWENCODE:
+#if WIRELESS_EXT > 17
+ case SIOCSIWENCODEEXT:
+ case SIOCGIWENCODEEXT:
+#endif
+ max_tokens = IW_ENCODING_TOKEN_MAX;
+ break;
+
+ case SIOCGIWRANGE:
+ max_tokens = sizeof(struct iw_range);
+ break;
+
+ case SIOCGIWAPLIST:
+ token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
+ max_tokens = IW_MAX_AP;
+ break;
+
+#if WIRELESS_EXT > 13
+ case SIOCGIWSCAN:
+ max_tokens = IW_SCAN_MAX_DATA;
+ break;
+#endif
+
+ case SIOCSIWSPY:
+ token_size = sizeof(struct sockaddr);
+ max_tokens = IW_MAX_SPY;
+ break;
+
+ case SIOCGIWSPY:
+ token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
+ max_tokens = IW_MAX_SPY;
+ break;
+ default:
+ break;
+ }
+
+ if (max_tokens && wrq->u.data.pointer) {
+ if (wrq->u.data.length > max_tokens)
+ return -E2BIG;
+
+ if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) {
+ kfree(extra);
+ return -EFAULT;
+ }
+ }
+
+ info.cmd = cmd;
+ info.flags = 0;
+
+ ret = handler(dev, &info, &wrq->u, extra);
+
+ if (extra) {
+ if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) {
+ kfree(extra);
+ return -EFAULT;
+ }
+
+ kfree(extra);
+ }
+
+ return ret;
+}
+
+bool
+wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason,
+ char* stringBuf, uint buflen)
+{
+ typedef struct conn_fail_event_map_t {
+ uint32 inEvent;
+ uint32 inStatus;
+ uint32 inReason;
+ const char* outName;
+ const char* outCause;
+ } conn_fail_event_map_t;
+
+# define WL_IW_DONT_CARE 9999
+ const conn_fail_event_map_t event_map [] = {
+
+ {WLC_E_SET_SSID, WLC_E_STATUS_SUCCESS, WL_IW_DONT_CARE,
+ "Conn", "Success"},
+ {WLC_E_SET_SSID, WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE,
+ "Conn", "NoNetworks"},
+ {WLC_E_SET_SSID, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
+ "Conn", "ConfigMismatch"},
+ {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_PRUNE_ENCR_MISMATCH,
+ "Conn", "EncrypMismatch"},
+ {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_RSN_MISMATCH,
+ "Conn", "RsnMismatch"},
+ {WLC_E_AUTH, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE,
+ "Conn", "AuthTimeout"},
+ {WLC_E_AUTH, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
+ "Conn", "AuthFail"},
+ {WLC_E_AUTH, WLC_E_STATUS_NO_ACK, WL_IW_DONT_CARE,
+ "Conn", "AuthNoAck"},
+ {WLC_E_REASSOC, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
+ "Conn", "ReassocFail"},
+ {WLC_E_REASSOC, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE,
+ "Conn", "ReassocTimeout"},
+ {WLC_E_REASSOC, WLC_E_STATUS_ABORT, WL_IW_DONT_CARE,
+ "Conn", "ReassocAbort"},
+ {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
+ "Conn", "Deauth"},
+ {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
+ "Conn", "DisassocInd"},
+ {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
+ "Conn", "Disassoc"}
+ };
+
+ const char* name = "";
+ const char* cause = NULL;
+ int i;
+
+ for (i = 0; i < sizeof(event_map)/sizeof(event_map[0]); i++) {
+ const conn_fail_event_map_t* row = &event_map[i];
+ if (row->inEvent == event_type &&
+ (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) &&
+ (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) {
+ name = row->outName;
+ cause = row->outCause;
+ break;
+ }
+ }
+
+ if (cause) {
+ memset(stringBuf, 0, buflen);
+ snprintf(stringBuf, buflen, "%s %s %02d %02d",
+ name, cause, status, reason);
+ WL_TRACE(("Connection status: %s\n", stringBuf));
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+#if (WIRELESS_EXT > 14)
+
+static bool
+wl_iw_check_conn_fail(wl_event_msg_t *e, char* stringBuf, uint buflen)
+{
+ uint32 event = e->event_type;
+ uint32 status = e->status;
+ uint32 reason = e->reason;
+
+ if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) {
+ return TRUE;
+ } else
+ {
+ return FALSE;
+ }
+}
+#endif
+
+#ifndef IW_CUSTOM_MAX
+#define IW_CUSTOM_MAX 256
+#endif
+
+void
+wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data)
+{
+#if WIRELESS_EXT > 13
+ union iwreq_data wrqu;
+ char extra[IW_CUSTOM_MAX + 1];
+ int cmd = 0;
+ uint32 event_type = e->event_type;
+ uint16 flags = e->flags;
+ uint32 datalen = e->datalen;
+ uint32 status = e->status;
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ memset(extra, 0, sizeof(extra));
+
+ memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+
+ switch (event_type) {
+ case WLC_E_TXFAIL:
+ cmd = IWEVTXDROP;
+ break;
+#if WIRELESS_EXT > 14
+ case WLC_E_JOIN:
+ case WLC_E_ASSOC_IND:
+ case WLC_E_REASSOC_IND:
+ cmd = IWEVREGISTERED;
+ break;
+ case WLC_E_DEAUTH_IND:
+ case WLC_E_DISASSOC_IND:
+ cmd = SIOCGIWAP;
+ wrqu.data.length = strlen(extra);
+ bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+ bzero(&extra, ETHER_ADDR_LEN);
+ break;
+
+ case WLC_E_LINK:
+ case WLC_E_NDIS_LINK:
+ cmd = SIOCGIWAP;
+ wrqu.data.length = strlen(extra);
+ if (!(flags & WLC_EVENT_MSG_LINK)) {
+ bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+ bzero(&extra, ETHER_ADDR_LEN);
+ }
+ break;
+ case WLC_E_ACTION_FRAME:
+ cmd = IWEVCUSTOM;
+ if (datalen + 1 <= sizeof(extra)) {
+ wrqu.data.length = datalen + 1;
+ extra[0] = WLC_E_ACTION_FRAME;
+ memcpy(&extra[1], data, datalen);
+ WL_TRACE(("WLC_E_ACTION_FRAME len %d \n", wrqu.data.length));
+ }
+ break;
+
+ case WLC_E_ACTION_FRAME_COMPLETE:
+ cmd = IWEVCUSTOM;
+ if (sizeof(status) + 1 <= sizeof(extra)) {
+ wrqu.data.length = sizeof(status) + 1;
+ extra[0] = WLC_E_ACTION_FRAME_COMPLETE;
+ memcpy(&extra[1], &status, sizeof(status));
+ WL_TRACE(("wl_iw_event status %d \n", status));
+ }
+ break;
+#endif
+#if WIRELESS_EXT > 17
+ case WLC_E_MIC_ERROR: {
+ struct iw_michaelmicfailure *micerrevt = (struct iw_michaelmicfailure *)&extra;
+ cmd = IWEVMICHAELMICFAILURE;
+ wrqu.data.length = sizeof(struct iw_michaelmicfailure);
+ if (flags & WLC_EVENT_MSG_GROUP)
+ micerrevt->flags |= IW_MICFAILURE_GROUP;
+ else
+ micerrevt->flags |= IW_MICFAILURE_PAIRWISE;
+ memcpy(micerrevt->src_addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+ micerrevt->src_addr.sa_family = ARPHRD_ETHER;
+
+ break;
+ }
+
+ case WLC_E_ASSOC_REQ_IE:
+ cmd = IWEVASSOCREQIE;
+ wrqu.data.length = datalen;
+ if (datalen < sizeof(extra))
+ memcpy(extra, data, datalen);
+ break;
+
+ case WLC_E_ASSOC_RESP_IE:
+ cmd = IWEVASSOCRESPIE;
+ wrqu.data.length = datalen;
+ if (datalen < sizeof(extra))
+ memcpy(extra, data, datalen);
+ break;
+
+ case WLC_E_PMKID_CACHE: {
+ struct iw_pmkid_cand *iwpmkidcand = (struct iw_pmkid_cand *)&extra;
+ pmkid_cand_list_t *pmkcandlist;
+ pmkid_cand_t *pmkidcand;
+ int count;
+
+ if (data == NULL)
+ break;
+
+ cmd = IWEVPMKIDCAND;
+ pmkcandlist = data;
+ count = ntoh32_ua((uint8 *)&pmkcandlist->npmkid_cand);
+ wrqu.data.length = sizeof(struct iw_pmkid_cand);
+ pmkidcand = pmkcandlist->pmkid_cand;
+ while (count) {
+ bzero(iwpmkidcand, sizeof(struct iw_pmkid_cand));
+ if (pmkidcand->preauth)
+ iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH;
+ bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data,
+ ETHER_ADDR_LEN);
+ wireless_send_event(dev, cmd, &wrqu, extra);
+ pmkidcand++;
+ count--;
+ }
+ break;
+ }
+#endif
+
+ case WLC_E_SCAN_COMPLETE:
+#if WIRELESS_EXT > 14
+ cmd = SIOCGIWSCAN;
+#endif
+ break;
+
+ default:
+
+ break;
+ }
+
+ if (cmd) {
+ if (cmd == SIOCGIWSCAN)
+ wireless_send_event(dev, cmd, &wrqu, NULL);
+ else
+ wireless_send_event(dev, cmd, &wrqu, extra);
+ }
+
+#if WIRELESS_EXT > 14
+
+ memset(extra, 0, sizeof(extra));
+ if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) {
+ cmd = IWEVCUSTOM;
+ wrqu.data.length = strlen(extra);
+ wireless_send_event(dev, cmd, &wrqu, extra);
+ }
+#endif
+
+#endif
+}
+
+int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats)
+{
+ int res = 0;
+ wl_cnt_t cnt;
+ int phy_noise;
+ int rssi;
+ scb_val_t scb_val;
+
+ phy_noise = 0;
+ if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise))))
+ goto done;
+
+ phy_noise = dtoh32(phy_noise);
+ WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n *****", phy_noise));
+
+ scb_val.val = 0;
+ if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t))))
+ goto done;
+
+ rssi = dtoh32(scb_val.val);
+ WL_TRACE(("wl_iw_get_wireless_stats rssi=%d ****** \n", rssi));
+ if (rssi <= WL_IW_RSSI_NO_SIGNAL)
+ wstats->qual.qual = 0;
+ else if (rssi <= WL_IW_RSSI_VERY_LOW)
+ wstats->qual.qual = 1;
+ else if (rssi <= WL_IW_RSSI_LOW)
+ wstats->qual.qual = 2;
+ else if (rssi <= WL_IW_RSSI_GOOD)
+ wstats->qual.qual = 3;
+ else if (rssi <= WL_IW_RSSI_VERY_GOOD)
+ wstats->qual.qual = 4;
+ else
+ wstats->qual.qual = 5;
+
+ wstats->qual.level = 0x100 + rssi;
+ wstats->qual.noise = 0x100 + phy_noise;
+#if WIRELESS_EXT > 18
+ wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM);
+#else
+ wstats->qual.updated |= 7;
+#endif
+
+#if WIRELESS_EXT > 11
+ WL_TRACE(("wl_iw_get_wireless_stats counters=%d\n *****", (int)sizeof(wl_cnt_t)));
+
+ memset(&cnt, 0, sizeof(wl_cnt_t));
+ res = dev_wlc_bufvar_get(dev, "counters", (char *)&cnt, sizeof(wl_cnt_t));
+ if (res)
+ {
+ WL_ERROR(("wl_iw_get_wireless_stats counters failed error=%d ****** \n", res));
+ goto done;
+ }
+
+ cnt.version = dtoh16(cnt.version);
+ if (cnt.version != WL_CNT_T_VERSION) {
+ WL_TRACE(("\tIncorrect version of counters struct: expected %d; got %d\n",
+ WL_CNT_T_VERSION, cnt.version));
+ goto done;
+ }
+
+ wstats->discard.nwid = 0;
+ wstats->discard.code = dtoh32(cnt.rxundec);
+ wstats->discard.fragment = dtoh32(cnt.rxfragerr);
+ wstats->discard.retries = dtoh32(cnt.txfail);
+ wstats->discard.misc = dtoh32(cnt.rxrunt) + dtoh32(cnt.rxgiant);
+ wstats->miss.beacon = 0;
+
+ WL_TRACE(("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n",
+ dtoh32(cnt.txframe), dtoh32(cnt.txbyte)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n", dtoh32(cnt.rxfrmtoolong)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxbadplcp=%d\n", dtoh32(cnt.rxbadplcp)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxundec=%d\n", dtoh32(cnt.rxundec)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxfragerr=%d\n", dtoh32(cnt.rxfragerr)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters txfail=%d\n", dtoh32(cnt.txfail)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxrunt=%d\n", dtoh32(cnt.rxrunt)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxgiant=%d\n", dtoh32(cnt.rxgiant)));
+
+#endif
+
+done:
+ return res;
+}
+
+int
+wl_iw_attach(struct net_device *dev, void * dhdp)
+{
+ return 0;
+}
+
+void wl_iw_detach(void)
+{
+}
+
+#endif
diff --git a/src/wl/sys/wl_iw.h b/src/wl/sys/wl_iw.h
new file mode 100644
index 0000000..3ab084f
--- /dev/null
+++ b/src/wl/sys/wl_iw.h
@@ -0,0 +1,150 @@
+/*
+ * Linux Wireless Extensions support
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wl_iw.h 291086 2011-10-21 01:17:24Z $
+ */
+
+#ifndef _wl_iw_h_
+#define _wl_iw_h_
+
+#include <linux/wireless.h>
+
+#include <typedefs.h>
+#include <proto/ethernet.h>
+#include <wlioctl.h>
+
+#define WL_SCAN_PARAMS_SSID_MAX 10
+#define GET_SSID "SSID="
+#define GET_CHANNEL "CH="
+#define GET_NPROBE "NPROBE="
+#define GET_ACTIVE_ASSOC_DWELL "ACTIVE="
+#define GET_PASSIVE_ASSOC_DWELL "PASSIVE="
+#define GET_HOME_DWELL "HOME="
+#define GET_SCAN_TYPE "TYPE="
+
+#define BAND_GET_CMD "GETBAND"
+#define BAND_SET_CMD "SETBAND"
+#define DTIM_SKIP_GET_CMD "DTIMSKIPGET"
+#define DTIM_SKIP_SET_CMD "DTIMSKIPSET"
+#define SETSUSPEND_CMD "SETSUSPENDOPT"
+#define PNOSSIDCLR_SET_CMD "PNOSSIDCLR"
+
+#define PNOSETUP_SET_CMD "PNOSETUP "
+#define PNOENABLE_SET_CMD "PNOFORCE"
+#define PNODEBUG_SET_CMD "PNODEBUG"
+#define TXPOWER_SET_CMD "TXPOWER"
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+typedef struct wl_iw_extra_params {
+ int target_channel;
+} wl_iw_extra_params_t;
+
+struct cntry_locales_custom {
+ char iso_abbrev[WLC_CNTRY_BUF_SZ];
+ char custom_locale[WLC_CNTRY_BUF_SZ];
+ int32 custom_locale_rev;
+};
+
+#define WL_IW_RSSI_MINVAL -200
+#define WL_IW_RSSI_NO_SIGNAL -91
+#define WL_IW_RSSI_VERY_LOW -80
+#define WL_IW_RSSI_LOW -70
+#define WL_IW_RSSI_GOOD -68
+#define WL_IW_RSSI_VERY_GOOD -58
+#define WL_IW_RSSI_EXCELLENT -57
+#define WL_IW_RSSI_INVALID 0
+#define MAX_WX_STRING 80
+#define isprint(c) bcm_isprint(c)
+#define WL_IW_SET_ACTIVE_SCAN (SIOCIWFIRSTPRIV+1)
+#define WL_IW_GET_RSSI (SIOCIWFIRSTPRIV+3)
+#define WL_IW_SET_PASSIVE_SCAN (SIOCIWFIRSTPRIV+5)
+#define WL_IW_GET_LINK_SPEED (SIOCIWFIRSTPRIV+7)
+#define WL_IW_GET_CURR_MACADDR (SIOCIWFIRSTPRIV+9)
+#define WL_IW_SET_STOP (SIOCIWFIRSTPRIV+11)
+#define WL_IW_SET_START (SIOCIWFIRSTPRIV+13)
+
+#define G_SCAN_RESULTS 8*1024
+#define WE_ADD_EVENT_FIX 0x80
+#define G_WLAN_SET_ON 0
+#define G_WLAN_SET_OFF 1
+
+typedef struct wl_iw {
+ char nickname[IW_ESSID_MAX_SIZE];
+
+ struct iw_statistics wstats;
+
+ int spy_num;
+ uint32 pwsec;
+ uint32 gwsec;
+ bool privacy_invoked;
+ struct ether_addr spy_addr[IW_MAX_SPY];
+ struct iw_quality spy_qual[IW_MAX_SPY];
+ void *wlinfo;
+} wl_iw_t;
+
+struct wl_ctrl {
+ struct timer_list *timer;
+ struct net_device *dev;
+ long sysioc_pid;
+ struct semaphore sysioc_sem;
+ struct completion sysioc_exited;
+};
+
+#if WIRELESS_EXT > 12
+#include <net/iw_handler.h>
+extern const struct iw_handler_def wl_iw_handler_def;
+#endif
+
+extern int wl_iw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+extern void wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data);
+extern int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats);
+int wl_iw_attach(struct net_device *dev, void * dhdp);
+int wl_iw_send_priv_event(struct net_device *dev, char *flag);
+
+void wl_iw_detach(void);
+
+#define CSCAN_COMMAND "CSCAN "
+#define CSCAN_TLV_PREFIX 'S'
+#define CSCAN_TLV_VERSION 1
+#define CSCAN_TLV_SUBVERSION 0
+#define CSCAN_TLV_TYPE_SSID_IE 'S'
+#define CSCAN_TLV_TYPE_CHANNEL_IE 'C'
+#define CSCAN_TLV_TYPE_NPROBE_IE 'N'
+#define CSCAN_TLV_TYPE_ACTIVE_IE 'A'
+#define CSCAN_TLV_TYPE_PASSIVE_IE 'P'
+#define CSCAN_TLV_TYPE_HOME_IE 'H'
+#define CSCAN_TLV_TYPE_STYPE_IE 'T'
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \
+ iwe_stream_add_event(info, stream, ends, iwe, extra)
+#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \
+ iwe_stream_add_value(info, event, value, ends, iwe, event_len)
+#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \
+ iwe_stream_add_point(info, stream, ends, iwe, extra)
+#else
+#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \
+ iwe_stream_add_event(stream, ends, iwe, extra)
+#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \
+ iwe_stream_add_value(event, value, ends, iwe, event_len)
+#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \
+ iwe_stream_add_point(stream, ends, iwe, extra)
+#endif
+
+#endif
diff --git a/src/wl/sys/wl_linux.c b/src/wl/sys/wl_linux.c
new file mode 100644
index 0000000..0d05100
--- /dev/null
+++ b/src/wl/sys/wl_linux.c
@@ -0,0 +1,3371 @@
+/*
+ * Linux-specific portion of
+ * Broadcom 802.11abg Networking Device Driver
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wl_linux.c 580354 2015-08-18 23:42:37Z $
+ */
+
+#define LINUX_PORT
+
+#define __UNDEF_NO_VERSION__
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
+#include <linux/module.h>
+#endif
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ethtool.h>
+#include <linux/completion.h>
+#include <linux/usb.h>
+#include <linux/pci_ids.h>
+#define WLC_MAXBSSCFG 1
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+#include <asm/switch_to.h>
+#else
+#include <asm/system.h>
+#endif
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <proto/802.1d.h>
+
+#include <epivers.h>
+#include <bcmendian.h>
+#include <proto/ethernet.h>
+#include <bcmutils.h>
+#include <pcicfg.h>
+#include <wlioctl.h>
+#include <wlc_key.h>
+#include <siutils.h>
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 5)
+#error "No support for Kernel Rev <= 2.4.5, As the older kernel revs doesn't support Tasklets"
+#endif
+
+#include <wlc_pub.h>
+#include <wl_dbg.h>
+#include <wlc_ethereal.h>
+#include <proto/ieee80211_radiotap.h>
+
+#include <wl_iw.h>
+#ifdef USE_IW
+struct iw_statistics *wl_get_wireless_stats(struct net_device *dev);
+#endif
+
+#include <wl_export.h>
+
+#include <wl_linux.h>
+
+#if defined(USE_CFG80211)
+#include <wl_cfg80211_hybrid.h>
+#endif
+
+#include <wlc_wowl.h>
+
+static void wl_timer(ulong data);
+static void _wl_timer(wl_timer_t *t);
+static struct net_device *wl_alloc_linux_if(wl_if_t *wlif);
+
+static int wl_monitor_start(struct sk_buff *skb, struct net_device *dev);
+
+static void wl_start_txqwork(wl_task_t *task);
+static void wl_txq_free(wl_info_t *wl);
+#define TXQ_LOCK(_wl) spin_lock_bh(&(_wl)->txq_lock)
+#define TXQ_UNLOCK(_wl) spin_unlock_bh(&(_wl)->txq_lock)
+
+static void wl_set_multicast_list_workitem(struct work_struct *work);
+
+static void wl_timer_task(wl_task_t *task);
+static void wl_dpc_rxwork(struct wl_task *task);
+
+static int wl_reg_proc_entry(wl_info_t *wl);
+
+static int wl_linux_watchdog(void *ctx);
+static
+int wl_found = 0;
+
+typedef struct priv_link {
+ wl_if_t *wlif;
+} priv_link_t;
+
+#define WL_DEV_IF(dev) ((wl_if_t*)((priv_link_t*)DEV_PRIV(dev))->wlif)
+
+#ifdef WL_INFO
+#undef WL_INFO
+#endif
+#define WL_INFO(dev) ((wl_info_t*)(WL_DEV_IF(dev)->wl))
+
+static int wl_open(struct net_device *dev);
+static int wl_close(struct net_device *dev);
+static int BCMFASTPATH wl_start(struct sk_buff *skb, struct net_device *dev);
+static int wl_start_int(wl_info_t *wl, wl_if_t *wlif, struct sk_buff *skb);
+
+static struct net_device_stats *wl_get_stats(struct net_device *dev);
+static int wl_set_mac_address(struct net_device *dev, void *addr);
+static void wl_set_multicast_list(struct net_device *dev);
+static void _wl_set_multicast_list(struct net_device *dev);
+static int wl_ethtool(wl_info_t *wl, void *uaddr, wl_if_t *wlif);
+static void wl_dpc(ulong data);
+static void wl_tx_tasklet(ulong data);
+static void wl_link_up(wl_info_t *wl, char * ifname);
+static void wl_link_down(wl_info_t *wl, char *ifname);
+static int wl_schedule_task(wl_info_t *wl, void (*fn)(struct wl_task *), void *context);
+#if defined(BCMDBG)
+static int wl_dump(wl_info_t *wl, struct bcmstrbuf *b);
+#endif
+static struct wl_if *wl_alloc_if(wl_info_t *wl, int iftype, uint unit, struct wlc_if* wlc_if);
+static void wl_free_if(wl_info_t *wl, wl_if_t *wlif);
+static void wl_get_driver_info(struct net_device *dev, struct ethtool_drvinfo *info);
+
+#if defined(WL_CONFIG_RFKILL)
+#include <linux/rfkill.h>
+static int wl_init_rfkill(wl_info_t *wl);
+static void wl_uninit_rfkill(wl_info_t *wl);
+static int wl_set_radio_block(void *data, bool blocked);
+static void wl_report_radio_state(wl_info_t *wl);
+#endif
+
+MODULE_LICENSE("MIXED/Proprietary");
+
+static struct pci_device_id wl_id_table[] =
+{
+ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, wl_id_table);
+
+static unsigned int online_cpus = 1;
+
+#ifdef BCMDBG
+static int msglevel = 0xdeadbeef;
+module_param(msglevel, int, 0);
+static int msglevel2 = 0xdeadbeef;
+module_param(msglevel2, int, 0);
+static int phymsglevel = 0xdeadbeef;
+module_param(phymsglevel, int, 0);
+#endif
+
+#ifdef BCMDBG_ASSERT
+static int assert_type = 0xdeadbeef;
+module_param(assert_type, int, 0);
+#endif
+
+static int passivemode = 0;
+module_param(passivemode, int, 0);
+
+#define WL_TXQ_THRESH 0
+static int wl_txq_thresh = WL_TXQ_THRESH;
+module_param(wl_txq_thresh, int, 0);
+
+static int oneonly = 0;
+module_param(oneonly, int, 0);
+
+static int piomode = 0;
+module_param(piomode, int, 0);
+
+static int instance_base = 0;
+module_param(instance_base, int, 0);
+
+#if defined(BCMDBG)
+static struct ether_addr local_ea;
+static char *macaddr = NULL;
+module_param(macaddr, charp, S_IRUGO);
+#endif
+
+static int nompc = 0;
+module_param(nompc, int, 0);
+
+#ifdef quote_str
+#undef quote_str
+#endif
+#ifdef to_str
+#undef to_str
+#endif
+#define to_str(s) #s
+#define quote_str(s) to_str(s)
+
+#define BRCM_WLAN_IFNAME eth%d
+
+static char intf_name[IFNAMSIZ] = quote_str(BRCM_WLAN_IFNAME);
+
+module_param_string(intf_name, intf_name, IFNAMSIZ, 0);
+
+static const u_int8_t brcm_oui[] = {0x00, 0x10, 0x18};
+
+#define WL_RADIOTAP_BRCM2_HT_SNS 0x01
+#define WL_RADIOTAP_BRCM2_HT_MCS 0x00000001
+
+#define WL_RADIOTAP_LEGACY_SNS 0x02
+#define WL_RADIOTAP_LEGACY_VHT 0x00000001
+
+#define IEEE80211_RADIOTAP_HTMOD_40 0x01
+#define IEEE80211_RADIOTAP_HTMOD_SGI 0x02
+#define IEEE80211_RADIOTAP_HTMOD_GF 0x04
+#define IEEE80211_RADIOTAP_HTMOD_LDPC 0x08
+#define IEEE80211_RADIOTAP_HTMOD_STBC_MASK 0x30
+#define IEEE80211_RADIOTAP_HTMOD_STBC_SHIFT 4
+
+#define WL_RADIOTAP_F_NONHT_VHT_DYN_BW 0x01
+
+#define WL_RADIOTAP_F_NONHT_VHT_BW 0x02
+
+struct wl_radiotap_nonht_vht {
+ u_int8_t len;
+ u_int8_t flags;
+ u_int8_t bw;
+} __attribute__ ((packed));
+
+typedef struct wl_radiotap_nonht_vht wl_radiotap_nonht_vht_t;
+
+struct wl_radiotap_legacy {
+ struct ieee80211_radiotap_header ieee_radiotap;
+ u_int32_t it_present_ext;
+ u_int32_t pad1;
+ uint32 tsft_l;
+ uint32 tsft_h;
+ uint8 flags;
+ uint8 rate;
+ uint16 channel_freq;
+ uint16 channel_flags;
+ uint8 signal;
+ uint8 noise;
+ int8 antenna;
+ uint8 pad2;
+ u_int8_t vend_oui[3];
+ u_int8_t vend_sns;
+ u_int16_t vend_skip_len;
+ wl_radiotap_nonht_vht_t nonht_vht;
+} __attribute__ ((__packed__));
+
+typedef struct wl_radiotap_legacy wl_radiotap_legacy_t;
+
+#define WL_RADIOTAP_LEGACY_SKIP_LEN htol16(sizeof(struct wl_radiotap_legacy) - \
+ offsetof(struct wl_radiotap_legacy, nonht_vht))
+
+#define WL_RADIOTAP_NONHT_VHT_LEN (sizeof(wl_radiotap_nonht_vht_t) - 1)
+
+struct wl_radiotap_ht_brcm_2 {
+ struct ieee80211_radiotap_header ieee_radiotap;
+ u_int32_t it_present_ext;
+ u_int32_t pad1;
+ uint32 tsft_l;
+ uint32 tsft_h;
+ u_int8_t flags;
+ u_int8_t pad2;
+ u_int16_t channel_freq;
+ u_int16_t channel_flags;
+ u_int8_t signal;
+ u_int8_t noise;
+ u_int8_t antenna;
+ u_int8_t pad3;
+ u_int8_t vend_oui[3];
+ u_int8_t vend_sns;
+ u_int16_t vend_skip_len;
+ u_int8_t mcs;
+ u_int8_t htflags;
+} __attribute__ ((packed));
+
+typedef struct wl_radiotap_ht_brcm_2 wl_radiotap_ht_brcm_2_t;
+
+#define WL_RADIOTAP_HT_BRCM2_SKIP_LEN htol16(sizeof(struct wl_radiotap_ht_brcm_2) - \
+ offsetof(struct wl_radiotap_ht_brcm_2, mcs))
+
+struct wl_radiotap_ht_brcm_3 {
+ struct ieee80211_radiotap_header ieee_radiotap;
+ u_int32_t it_present_ext;
+ u_int32_t pad1;
+ uint32 tsft_l;
+ uint32 tsft_h;
+ u_int8_t flags;
+ u_int8_t pad2;
+ u_int16_t channel_freq;
+ u_int16_t channel_flags;
+ u_int8_t signal;
+ u_int8_t noise;
+ u_int8_t antenna;
+ u_int8_t mcs_known;
+ u_int8_t mcs_flags;
+ u_int8_t mcs_index;
+ u_int8_t vend_oui[3];
+ u_int8_t vend_sns;
+ u_int16_t vend_skip_len;
+ wl_radiotap_nonht_vht_t nonht_vht;
+} __attribute__ ((packed));
+
+typedef struct wl_radiotap_ht_brcm_3 wl_radiotap_ht_brcm_3_t;
+
+struct wl_radiotap_ht {
+ struct ieee80211_radiotap_header ieee_radiotap;
+ uint32 tsft_l;
+ uint32 tsft_h;
+ u_int8_t flags;
+ u_int8_t pad1;
+ u_int16_t channel_freq;
+ u_int16_t channel_flags;
+ u_int8_t signal;
+ u_int8_t noise;
+ u_int8_t antenna;
+ u_int8_t mcs_known;
+ u_int8_t mcs_flags;
+ u_int8_t mcs_index;
+} __attribute__ ((packed));
+
+typedef struct wl_radiotap_ht wl_radiotap_ht_t;
+
+struct wl_radiotap_vht {
+ struct ieee80211_radiotap_header ieee_radiotap;
+ uint32 tsft_l;
+ uint32 tsft_h;
+ u_int8_t flags;
+ u_int8_t pad1;
+ u_int16_t channel_freq;
+ u_int16_t channel_flags;
+ u_int8_t signal;
+ u_int8_t noise;
+ u_int8_t antenna;
+ u_int8_t pad2;
+ u_int16_t pad3;
+ uint32 ampdu_ref_num;
+ u_int16_t ampdu_flags;
+ u_int8_t ampdu_delim_crc;
+ u_int8_t ampdu_reserved;
+ u_int16_t vht_known;
+ u_int8_t vht_flags;
+ u_int8_t vht_bw;
+ u_int8_t vht_mcs_nss[4];
+ u_int8_t vht_coding;
+ u_int8_t vht_group_id;
+ u_int16_t vht_partial_aid;
+} __attribute__ ((packed));
+
+typedef struct wl_radiotap_vht wl_radiotap_vht_t;
+
+#define WL_RADIOTAP_PRESENT_LEGACY \
+ ((1 << IEEE80211_RADIOTAP_TSFT) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE) | \
+ (1 << IEEE80211_RADIOTAP_EXT))
+
+#define WL_RADIOTAP_PRESENT_HT_BRCM2 \
+ ((1 << IEEE80211_RADIOTAP_TSFT) | \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE) | \
+ (1 << IEEE80211_RADIOTAP_EXT))
+
+#define WL_RADIOTAP_PRESENT_HT \
+ ((1 << IEEE80211_RADIOTAP_TSFT) | \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_MCS))
+
+#define WL_RADIOTAP_PRESENT_VHT \
+ ((1 << IEEE80211_RADIOTAP_TSFT) | \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_AMPDU) | \
+ (1 << IEEE80211_RADIOTAP_VHT))
+
+#ifndef ARPHRD_IEEE80211_RADIOTAP
+#define ARPHRD_IEEE80211_RADIOTAP 803
+#endif
+
+#ifndef SRCBASE
+#define SRCBASE "."
+#endif
+
+#if WIRELESS_EXT >= 19 || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static struct ethtool_ops wl_ethtool_ops =
+#else
+static const struct ethtool_ops wl_ethtool_ops =
+#endif
+{
+ .get_drvinfo = wl_get_driver_info,
+};
+#endif
+
+#if defined(WL_USE_NETDEV_OPS)
+
+static const struct net_device_ops wl_netdev_ops =
+{
+ .ndo_open = wl_open,
+ .ndo_stop = wl_close,
+ .ndo_start_xmit = wl_start,
+ .ndo_get_stats = wl_get_stats,
+ .ndo_set_mac_address = wl_set_mac_address,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ .ndo_set_rx_mode = wl_set_multicast_list,
+#else
+ .ndo_set_multicast_list = wl_set_multicast_list,
+#endif
+ .ndo_do_ioctl = wl_ioctl
+};
+
+static const struct net_device_ops wl_netdev_monitor_ops =
+{
+ .ndo_start_xmit = wl_monitor_start,
+ .ndo_get_stats = wl_get_stats,
+ .ndo_do_ioctl = wl_ioctl
+};
+#endif
+
+static void
+wl_if_setup(struct net_device *dev)
+{
+#if defined(WL_USE_NETDEV_OPS)
+ dev->netdev_ops = &wl_netdev_ops;
+#else
+ dev->open = wl_open;
+ dev->stop = wl_close;
+ dev->hard_start_xmit = wl_start;
+ dev->get_stats = wl_get_stats;
+ dev->set_mac_address = wl_set_mac_address;
+ dev->set_multicast_list = wl_set_multicast_list;
+ dev->do_ioctl = wl_ioctl;
+#endif
+
+#ifdef USE_IW
+#if WIRELESS_EXT < 19
+ dev->get_wireless_stats = wl_get_wireless_stats;
+#endif
+#if WIRELESS_EXT > 12
+ dev->wireless_handlers = (struct iw_handler_def *) &wl_iw_handler_def;
+#endif
+#endif
+
+#if WIRELESS_EXT >= 19 || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+ dev->ethtool_ops = &wl_ethtool_ops;
+#endif
+}
+
+static wl_info_t *
+wl_attach(uint16 vendor, uint16 device, ulong regs,
+ uint bustype, void *btparam, uint irq, uchar* bar1_addr, uint32 bar1_size)
+{
+ struct net_device *dev;
+ wl_if_t *wlif;
+ wl_info_t *wl;
+ osl_t *osh;
+ int unit, err;
+#if defined(USE_CFG80211)
+ struct device *parentdev;
+#endif
+
+ unit = wl_found + instance_base;
+ err = 0;
+
+ if (unit < 0) {
+ WL_ERROR(("wl%d: unit number overflow, exiting\n", unit));
+ return NULL;
+ }
+
+ if (oneonly && (unit != instance_base)) {
+ WL_ERROR(("wl%d: wl_attach: oneonly is set, exiting\n", unit));
+ return NULL;
+ }
+
+ osh = osl_attach(btparam, bustype, TRUE);
+ ASSERT(osh);
+
+ if ((wl = (wl_info_t*) MALLOC(osh, sizeof(wl_info_t))) == NULL) {
+ WL_ERROR(("wl%d: malloc wl_info_t, out of memory, malloced %d bytes\n", unit,
+ MALLOCED(osh)));
+ osl_detach(osh);
+ return NULL;
+ }
+ bzero(wl, sizeof(wl_info_t));
+
+ wl->osh = osh;
+ wl->unit = unit;
+ atomic_set(&wl->callbacks, 0);
+
+ wl->all_dispatch_mode = (passivemode == 0) ? TRUE : FALSE;
+ if (WL_ALL_PASSIVE_ENAB(wl)) {
+
+ MY_INIT_WORK(&wl->txq_task.work, (work_func_t)wl_start_txqwork);
+ wl->txq_task.context = wl;
+
+ MY_INIT_WORK(&wl->multicast_task.work, (work_func_t)wl_set_multicast_list_workitem);
+
+ MY_INIT_WORK(&wl->wl_dpc_task.work, (work_func_t)wl_dpc_rxwork);
+ wl->wl_dpc_task.context = wl;
+ }
+
+ wl->txq_dispatched = FALSE;
+ wl->txq_head = wl->txq_tail = NULL;
+ wl->txq_cnt = 0;
+
+ wlif = wl_alloc_if(wl, WL_IFTYPE_BSS, unit, NULL);
+ if (!wlif) {
+ WL_ERROR(("wl%d: %s: wl_alloc_if failed\n", unit, __FUNCTION__));
+ MFREE(osh, wl, sizeof(wl_info_t));
+ osl_detach(osh);
+ return NULL;
+ }
+
+ if (wl_alloc_linux_if(wlif) == NULL) {
+ WL_ERROR(("wl%d: %s: wl_alloc_linux_if failed\n", unit, __FUNCTION__));
+ MFREE(osh, wl, sizeof(wl_info_t));
+ osl_detach(osh);
+ return NULL;
+ }
+
+ dev = wlif->dev;
+ wl->dev = dev;
+ wl_if_setup(dev);
+
+ dev->base_addr = regs;
+
+ WL_TRACE(("wl%d: Bus: ", unit));
+ if (bustype == PCMCIA_BUS) {
+
+ wl->piomode = TRUE;
+ WL_TRACE(("PCMCIA\n"));
+ } else if (bustype == PCI_BUS) {
+
+ wl->piomode = piomode;
+ WL_TRACE(("PCI/%s\n", wl->piomode ? "PIO" : "DMA"));
+ }
+ else if (bustype == RPC_BUS) {
+
+ } else {
+ bustype = PCI_BUS;
+ WL_TRACE(("force to PCI\n"));
+ }
+ wl->bcm_bustype = bustype;
+
+ if ((wl->regsva = ioremap_nocache(dev->base_addr, PCI_BAR0_WINSZ)) == NULL) {
+ WL_ERROR(("wl%d: ioremap() failed\n", unit));
+ goto fail;
+ }
+
+ wl->bar1_addr = bar1_addr;
+ wl->bar1_size = bar1_size;
+
+ spin_lock_init(&wl->lock);
+ spin_lock_init(&wl->isr_lock);
+
+ if (WL_ALL_PASSIVE_ENAB(wl))
+ sema_init(&wl->sem, 1);
+
+ spin_lock_init(&wl->txq_lock);
+
+ if (!(wl->wlc = wlc_attach((void *) wl, vendor, device, unit, wl->piomode,
+ osh, wl->regsva, wl->bcm_bustype, btparam, &err))) {
+ printf("wl driver %s failed with code %d\n", EPI_VERSION_STR, err);
+ goto fail;
+ }
+ wl->pub = wlc_pub(wl->wlc);
+
+ wlif->wlcif = wlc_wlcif_get_by_index(wl->wlc, 0);
+
+ if (nompc) {
+ if (wlc_iovar_setint(wl->wlc, "mpc", 0)) {
+ WL_ERROR(("wl%d: Error setting MPC variable to 0\n", unit));
+ }
+ }
+
+ wlc_iovar_setint(wl->wlc, "scan_passive_time", 170);
+
+ wlc_iovar_setint(wl->wlc, "qtxpower", 23 * 4);
+
+#ifdef BCMDBG
+ if (macaddr != NULL) {
+ int dbg_err;
+
+ WL_ERROR(("wl%d: setting MAC ADDRESS %s\n", unit, macaddr));
+ bcm_ether_atoe(macaddr, &local_ea);
+
+ dbg_err = wlc_iovar_op(wl->wlc, "cur_etheraddr", NULL, 0, &local_ea,
+ ETHER_ADDR_LEN, IOV_SET, NULL);
+ if (dbg_err)
+ WL_ERROR(("wl%d: Error setting MAC ADDRESS\n", unit));
+ }
+#endif
+ bcopy(&wl->pub->cur_etheraddr, dev->dev_addr, ETHER_ADDR_LEN);
+
+ online_cpus = 1;
+
+ WL_ERROR(("wl%d: online cpus %d\n", unit, online_cpus));
+
+ tasklet_init(&wl->tasklet, wl_dpc, (ulong)wl);
+
+ tasklet_init(&wl->tx_tasklet, wl_tx_tasklet, (ulong)wl);
+
+ {
+ if (request_irq(irq, wl_isr, IRQF_SHARED, dev->name, wl)) {
+ WL_ERROR(("wl%d: request_irq() failed\n", unit));
+ goto fail;
+ }
+ dev->irq = irq;
+ }
+
+#if defined(USE_IW)
+ WL_ERROR(("Using Wireless Extension\n"));
+#endif
+
+#if defined(USE_CFG80211)
+ parentdev = NULL;
+ if (wl->bcm_bustype == PCI_BUS) {
+ parentdev = &((struct pci_dev *)btparam)->dev;
+ }
+ if (parentdev) {
+ if (wl_cfg80211_attach(dev, parentdev, WL_ALL_PASSIVE_ENAB(wl))) {
+ goto fail;
+ }
+ }
+ else {
+ WL_ERROR(("unsupported bus type\n"));
+ goto fail;
+ }
+#else
+
+ if (wl->bcm_bustype == PCI_BUS) {
+ struct pci_dev *pci_dev = (struct pci_dev *)btparam;
+ if (pci_dev != NULL)
+ SET_NETDEV_DEV(dev, &pci_dev->dev);
+ }
+#endif
+
+ if (register_netdev(dev)) {
+ WL_ERROR(("wl%d: register_netdev() failed\n", unit));
+ goto fail;
+ }
+ wlif->dev_registed = TRUE;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
+#endif
+#ifdef USE_IW
+ wlif->iw.wlinfo = (void *)wl;
+#endif
+
+#if defined(WL_CONFIG_RFKILL)
+ if (wl_init_rfkill(wl) < 0)
+ WL_ERROR(("%s: init_rfkill_failure\n", __FUNCTION__));
+#endif
+
+ if (wlc_iovar_setint(wl->wlc, "leddc", 0xa0000)) {
+ WL_ERROR(("wl%d: Error setting led duty-cycle\n", unit));
+ }
+ if (wlc_set(wl->wlc, WLC_SET_PM, PM_FAST)) {
+ WL_ERROR(("wl%d: Error setting PM variable to FAST PS\n", unit));
+ }
+
+ if (wlc_iovar_setint(wl->wlc, "vlan_mode", OFF)) {
+ WL_ERROR(("wl%d: Error setting vlan mode OFF\n", unit));
+ }
+
+ if (wlc_set(wl->wlc, WLC_SET_INFRA, 1)) {
+ WL_ERROR(("wl%d: Error setting infra_mode to infrastructure\n", unit));
+ }
+
+ if (wlc_module_register(wl->pub, NULL, "linux", wl, NULL, wl_linux_watchdog, NULL, NULL)) {
+ WL_ERROR(("wl%d: %s wlc_module_register() failed\n",
+ wl->pub->unit, __FUNCTION__));
+ goto fail;
+ }
+
+#ifdef BCMDBG
+ wlc_dump_register(wl->pub, "wl", (dump_fn_t)wl_dump, (void *)wl);
+#endif
+
+ wl_reg_proc_entry(wl);
+
+ printf("%s: Broadcom BCM%04x 802.11 Hybrid Wireless Controller%s %s",
+ dev->name, device,
+ WL_ALL_PASSIVE_ENAB(wl) ? ", Passive Mode" : "", EPI_VERSION_STR);
+
+#ifdef BCMDBG
+ printf(" (Compiled in " SRCBASE " at " __TIME__ " on " __DATE__ ")");
+#endif
+ printf("\n");
+
+ wl_found++;
+ return wl;
+
+fail:
+ wl_free(wl);
+ return NULL;
+}
+
+static void __devexit wl_remove(struct pci_dev *pdev);
+
+int __devinit
+wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int rc;
+ wl_info_t *wl;
+ uint32 val;
+ uint32 bar1_size = 0;
+ void* bar1_addr = NULL;
+
+ WL_TRACE(("%s: bus %d slot %d func %d irq %d\n", __FUNCTION__,
+ pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq));
+
+ if ((pdev->vendor != PCI_VENDOR_ID_BROADCOM) ||
+ (((pdev->device & 0xff00) != 0x4300) &&
+ (pdev->device != 0x576) &&
+ ((pdev->device & 0xff00) != 0x4700) &&
+ ((pdev->device < 43000) || (pdev->device > 43999)))) {
+ WL_TRACE(("%s: unsupported vendor %x device %x\n", __FUNCTION__,
+ pdev->vendor, pdev->device));
+ return (-ENODEV);
+ }
+
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ WL_ERROR(("%s: Cannot enable device %d-%d_%d\n", __FUNCTION__,
+ pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)));
+ return (-ENODEV);
+ }
+ pci_set_master(pdev);
+
+ pci_read_config_dword(pdev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+ bar1_size = pci_resource_len(pdev, 2);
+ bar1_addr = (uchar *)ioremap_nocache(pci_resource_start(pdev, 2),
+ bar1_size);
+ wl = wl_attach(pdev->vendor, pdev->device, pci_resource_start(pdev, 0), PCI_BUS, pdev,
+ pdev->irq, bar1_addr, bar1_size);
+
+ if (!wl)
+ return -ENODEV;
+
+ pci_set_drvdata(pdev, wl);
+
+ return 0;
+}
+
+static int
+#if !defined(SIMPLE_DEV_PM_OPS)
+wl_suspend(struct pci_dev *pdev, DRV_SUSPEND_STATE_TYPE state)
+{
+#else
+wl_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+#endif
+ wl_info_t *wl = (wl_info_t *) pci_get_drvdata(pdev);
+ if (!wl) {
+ WL_ERROR(("wl: wl_suspend: pci_get_drvdata failed\n"));
+ return -ENODEV;
+ }
+ WL_ERROR(("%s: PCI Suspend handler\n", __FUNCTION__));
+
+ WL_LOCK(wl);
+ if (WLOFFLD_ENAB(wl->pub) && wlc_iovar_setint(wl->wlc, "wowl_activate", 1) == 0) {
+ WL_TRACE(("%s: Enabled WOWL OFFLOAD\n", __FUNCTION__));
+ } else {
+ WL_ERROR(("%s: Not WOWL capable\n", __FUNCTION__));
+ wl_down(wl);
+ wl->pub->hw_up = FALSE;
+ }
+ WL_UNLOCK(wl);
+
+ if (BUSTYPE(wl->pub->sih->bustype) == PCI_BUS)
+ si_pci_sleep(wl->pub->sih);
+
+ return 0;
+}
+
+static int
+#if !defined(SIMPLE_DEV_PM_OPS)
+wl_resume(struct pci_dev *pdev)
+{
+#else
+wl_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+#endif
+ int err = 0;
+ wl_info_t *wl = (wl_info_t *) pci_get_drvdata(pdev);
+ if (!wl) {
+ WL_ERROR(("wl: wl_resume: pci_get_drvdata failed\n"));
+ return -ENODEV;
+ }
+
+ WL_ERROR(("%s: PCI Resume handler\n", __FUNCTION__));
+ if (WLOFFLD_ENAB(wl->pub)) {
+ wlc_iovar_setint(wl->wlc, "wowl_activate", 0);
+ wlc_wowl_wake_reason_process(wl->wlc);
+
+ if (WOWL_ACTIVE(wl->pub)) {
+ if (BUSTYPE(wl->pub->sih->bustype) == PCI_BUS) {
+ si_pci_pmeclr(wl->pub->sih);
+ }
+ }
+ }
+
+ WL_LOCK(wl);
+ err = wl_up(wl);
+ WL_UNLOCK(wl);
+
+ return (err);
+}
+
+static void __devexit
+wl_remove(struct pci_dev *pdev)
+{
+ wl_info_t *wl = (wl_info_t *) pci_get_drvdata(pdev);
+
+ if (!wl) {
+ WL_ERROR(("wl: wl_remove: pci_get_drvdata failed\n"));
+ return;
+ }
+ if (!wlc_chipmatch(pdev->vendor, pdev->device)) {
+ WL_ERROR(("wl: wl_remove: wlc_chipmatch failed\n"));
+ return;
+ }
+
+ WL_LOCK(wl);
+ WL_APSTA_UPDN(("wl%d (%s): wl_remove() -> wl_down()\n", wl->pub->unit, wl->dev->name));
+ wl_down(wl);
+ WL_UNLOCK(wl);
+
+ wl_free(wl);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+#if defined(SIMPLE_DEV_PM_OPS)
+static SIMPLE_DEV_PM_OPS(wl_pm_ops, wl_suspend, wl_resume);
+#endif
+
+static struct pci_driver wl_pci_driver __refdata = {
+ .name = "wl",
+ .probe = wl_pci_probe,
+ .remove = __devexit_p(wl_remove),
+ .id_table = wl_id_table,
+#ifdef SIMPLE_DEV_PM_OPS
+ .driver.pm = &wl_pm_ops,
+#else
+ .suspend = wl_suspend,
+ .resume = wl_resume,
+#endif
+};
+
+static int __init
+wl_module_init(void)
+{
+ int error = -ENODEV;
+
+#ifdef BCMDBG
+ if (msglevel != 0xdeadbeef)
+ wl_msg_level = msglevel;
+ else {
+ const char *var = getvar(NULL, "wl_msglevel");
+ if (var)
+ wl_msg_level = bcm_strtoul(var, NULL, 0);
+ }
+ printf("%s: msglevel set to 0x%x\n", __FUNCTION__, wl_msg_level);
+ if (msglevel2 != 0xdeadbeef)
+ wl_msg_level2 = msglevel2;
+ else {
+ const char *var = getvar(NULL, "wl_msglevel2");
+ if (var)
+ wl_msg_level2 = bcm_strtoul(var, NULL, 0);
+ }
+ printf("%s: msglevel2 set to 0x%x\n", __FUNCTION__, wl_msg_level2);
+ {
+ extern uint32 phyhal_msg_level;
+
+ if (phymsglevel != 0xdeadbeef)
+ phyhal_msg_level = phymsglevel;
+ else {
+ const char *var = getvar(NULL, "phy_msglevel");
+ if (var)
+ phyhal_msg_level = bcm_strtoul(var, NULL, 0);
+ }
+ printf("%s: phymsglevel set to 0x%x\n", __FUNCTION__, phyhal_msg_level);
+ }
+#endif
+
+ {
+ const char *var = getvar(NULL, "wl_dispatch_mode");
+ if (var)
+ passivemode = bcm_strtoul(var, NULL, 0);
+ if (passivemode)
+ printf("%s: passivemode enabled\n", __FUNCTION__);
+ }
+
+#ifdef BCMDBG_ASSERT
+
+ if (assert_type != 0xdeadbeef)
+ g_assert_type = assert_type;
+#endif
+
+ {
+ char *var = getvar(NULL, "wl_txq_thresh");
+ if (var)
+ wl_txq_thresh = bcm_strtoul(var, NULL, 0);
+#ifdef BCMDBG
+ WL_INFORM(("%s: wl_txq_thresh set to 0x%x\n",
+ __FUNCTION__, wl_txq_thresh));
+#endif
+ }
+
+ if (!(error = pci_module_init(&wl_pci_driver)))
+ return (0);
+
+ return (error);
+}
+
+static void __exit
+wl_module_exit(void)
+{
+
+ pci_unregister_driver(&wl_pci_driver);
+
+}
+
+module_init(wl_module_init);
+module_exit(wl_module_exit);
+
+void
+wl_free(wl_info_t *wl)
+{
+ wl_timer_t *t, *next;
+ osl_t *osh;
+
+ WL_TRACE(("wl: wl_free\n"));
+ {
+ if (wl->dev && wl->dev->irq)
+ free_irq(wl->dev->irq, wl);
+ }
+
+#if defined(WL_CONFIG_RFKILL)
+ wl_uninit_rfkill(wl);
+#endif
+
+ if (wl->dev) {
+ wl_free_if(wl, WL_DEV_IF(wl->dev));
+ wl->dev = NULL;
+ }
+
+ tasklet_kill(&wl->tasklet);
+
+ tasklet_kill(&wl->tx_tasklet);
+
+ if (wl->pub) {
+ wlc_module_unregister(wl->pub, "linux", wl);
+ }
+
+ if (wl->wlc) {
+ {
+ char tmp1[128];
+ sprintf(tmp1, "%s%d", HYBRID_PROC, wl->pub->unit);
+ remove_proc_entry(tmp1, 0);
+ }
+ wlc_detach(wl->wlc);
+ wl->wlc = NULL;
+ wl->pub = NULL;
+ }
+
+ while (atomic_read(&wl->callbacks) > 0)
+ schedule();
+
+ for (t = wl->timers; t; t = next) {
+ next = t->next;
+#ifdef BCMDBG
+ if (t->name)
+ MFREE(wl->osh, t->name, strlen(t->name) + 1);
+#endif
+ MFREE(wl->osh, t, sizeof(wl_timer_t));
+ }
+
+ osh = wl->osh;
+
+ if (wl->regsva && BUSTYPE(wl->bcm_bustype) != SDIO_BUS &&
+ BUSTYPE(wl->bcm_bustype) != JTAG_BUS) {
+ iounmap((void*)wl->regsva);
+ }
+ wl->regsva = NULL;
+
+ if (wl->bar1_addr) {
+ iounmap(wl->bar1_addr);
+ wl->bar1_addr = NULL;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
+#endif
+
+ wl_txq_free(wl);
+
+ MFREE(osh, wl, sizeof(wl_info_t));
+
+ if (MALLOCED(osh)) {
+ printf("Memory leak of bytes %d\n", MALLOCED(osh));
+#ifndef BCMDBG_MEM
+ ASSERT(0);
+#endif
+ }
+
+#ifdef BCMDBG_MEM
+
+ MALLOC_DUMP(osh, NULL);
+#endif
+
+ osl_detach(osh);
+}
+
+static int
+wl_open(struct net_device *dev)
+{
+ wl_info_t *wl;
+ int error = 0;
+
+ if (!dev)
+ return -ENETDOWN;
+
+ wl = WL_INFO(dev);
+
+ WL_TRACE(("wl%d: wl_open\n", wl->pub->unit));
+
+ WL_LOCK(wl);
+ WL_APSTA_UPDN(("wl%d: (%s): wl_open() -> wl_up()\n",
+ wl->pub->unit, wl->dev->name));
+
+ error = wl_up(wl);
+ if (!error) {
+ error = wlc_set(wl->wlc, WLC_SET_PROMISC, (dev->flags & IFF_PROMISC));
+ }
+ WL_UNLOCK(wl);
+
+ if (!error)
+ OLD_MOD_INC_USE_COUNT;
+
+#if defined(USE_CFG80211)
+ if (wl_cfg80211_up(dev)) {
+ WL_ERROR(("%s: failed to bring up cfg80211\n", __func__));
+ return -1;
+ }
+#endif
+ return (error? -ENODEV : 0);
+}
+
+static int
+wl_close(struct net_device *dev)
+{
+ wl_info_t *wl;
+
+ if (!dev)
+ return -ENETDOWN;
+
+#if defined(USE_CFG80211)
+ wl_cfg80211_down(dev);
+#endif
+ wl = WL_INFO(dev);
+
+ WL_TRACE(("wl%d: wl_close\n", wl->pub->unit));
+
+ WL_LOCK(wl);
+ WL_APSTA_UPDN(("wl%d (%s): wl_close() -> wl_down()\n",
+ wl->pub->unit, wl->dev->name));
+
+ if (wl->if_list == NULL) {
+ wl_down(wl);
+ }
+ WL_UNLOCK(wl);
+
+ OLD_MOD_DEC_USE_COUNT;
+
+ return (0);
+}
+
+void * BCMFASTPATH
+wl_get_ifctx(struct wl_info *wl, int ctx_id, wl_if_t *wlif)
+{
+ void *ifctx = NULL;
+
+ switch (ctx_id) {
+ case IFCTX_NETDEV:
+ ifctx = (void *)((wlif == NULL) ? wl->dev : wlif->dev);
+ break;
+
+ default:
+ break;
+ }
+
+ return ifctx;
+}
+
+static int BCMFASTPATH
+wl_start_int(wl_info_t *wl, wl_if_t *wlif, struct sk_buff *skb)
+{
+ void *pkt;
+
+ WL_TRACE(("wl%d: wl_start: len %d data_len %d summed %d csum: 0x%x\n",
+ wl->pub->unit, skb->len, skb->data_len, skb->ip_summed, (uint32)skb->csum));
+
+ WL_LOCK(wl);
+
+ pkt = PKTFRMNATIVE(wl->osh, skb);
+ ASSERT(pkt != NULL);
+
+ if (WME_ENAB(wl->pub) && (PKTPRIO(pkt) == 0))
+ pktsetprio(pkt, FALSE);
+
+ wlc_sendpkt(wl->wlc, pkt, wlif->wlcif);
+
+ WL_UNLOCK(wl);
+
+ return (0);
+}
+
+void
+wl_txflowcontrol(wl_info_t *wl, struct wl_if *wlif, bool state, int prio)
+{
+ struct net_device *dev;
+
+ ASSERT(prio == ALLPRIO);
+
+ if (wlif == NULL)
+ dev = wl->dev;
+ else if (!wlif->dev_registed)
+ return;
+ else
+ dev = wlif->dev;
+
+ if (state == ON)
+ netif_stop_queue(dev);
+ else
+ netif_wake_queue(dev);
+}
+
+static int
+wl_schedule_task(wl_info_t *wl, void (*fn)(struct wl_task *task), void *context)
+{
+ wl_task_t *task;
+
+ WL_TRACE(("wl%d: wl_schedule_task\n", wl->pub->unit));
+
+ if (!(task = MALLOC(wl->osh, sizeof(wl_task_t)))) {
+ WL_ERROR(("wl%d: wl_schedule_task: out of memory, malloced %d bytes\n",
+ wl->pub->unit, MALLOCED(wl->osh)));
+ return -ENOMEM;
+ }
+
+ MY_INIT_WORK(&task->work, (work_func_t)fn);
+ task->context = context;
+
+ if (!schedule_work(&task->work)) {
+ WL_ERROR(("wl%d: schedule_work() failed\n", wl->pub->unit));
+ MFREE(wl->osh, task, sizeof(wl_task_t));
+ return -ENOMEM;
+ }
+
+ atomic_inc(&wl->callbacks);
+
+ return 0;
+}
+
+static struct wl_if *
+wl_alloc_if(wl_info_t *wl, int iftype, uint subunit, struct wlc_if *wlcif)
+{
+ wl_if_t *wlif;
+ wl_if_t *p;
+
+ if (!(wlif = MALLOC(wl->osh, sizeof(wl_if_t)))) {
+ WL_ERROR(("wl%d: wl_alloc_if: out of memory, malloced %d bytes\n",
+ (wl->pub)?wl->pub->unit:subunit, MALLOCED(wl->osh)));
+ return NULL;
+ }
+ bzero(wlif, sizeof(wl_if_t));
+ wlif->wl = wl;
+ wlif->wlcif = wlcif;
+ wlif->subunit = subunit;
+ wlif->if_type = iftype;
+
+ if (wl->if_list == NULL)
+ wl->if_list = wlif;
+ else {
+ p = wl->if_list;
+ while (p->next != NULL)
+ p = p->next;
+ p->next = wlif;
+ }
+
+ return wlif;
+}
+
+static void
+wl_free_if(wl_info_t *wl, wl_if_t *wlif)
+{
+ wl_if_t *p;
+ ASSERT(wlif);
+ ASSERT(wl);
+
+ WL_TRACE(("%s\n", __FUNCTION__));
+
+ if (wlif->dev_registed) {
+ ASSERT(wlif->dev);
+ unregister_netdev(wlif->dev);
+ wlif->dev_registed = FALSE;
+ }
+
+#if defined(USE_CFG80211)
+ wl_cfg80211_detach(wlif->dev);
+#endif
+
+ p = wl->if_list;
+ if (p == wlif)
+ wl->if_list = p->next;
+ else {
+ while (p != NULL && p->next != wlif)
+ p = p->next;
+ if (p != NULL)
+ p->next = p->next->next;
+ }
+
+ if (wlif->dev) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
+ MFREE(wl->osh, wlif->dev->priv, sizeof(priv_link_t));
+ MFREE(wl->osh, wlif->dev, sizeof(struct net_device));
+#else
+ free_netdev(wlif->dev);
+ wlif->dev = NULL;
+#endif
+ }
+
+ MFREE(wl->osh, wlif, sizeof(wl_if_t));
+}
+
+static struct net_device *
+wl_alloc_linux_if(wl_if_t *wlif)
+{
+ wl_info_t *wl = wlif->wl;
+ struct net_device *dev;
+ priv_link_t *priv_link;
+
+ WL_TRACE(("%s\n", __FUNCTION__));
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
+ dev = MALLOC(wl->osh, sizeof(struct net_device));
+ if (!dev) {
+ WL_ERROR(("wl%d: %s: malloc of net_device failed\n",
+ (wl->pub)?wl->pub->unit:wlif->subunit, __FUNCTION__));
+ return NULL;
+ }
+ bzero(dev, sizeof(struct net_device));
+ ether_setup(dev);
+
+ strncpy(dev->name, intf_name, IFNAMSIZ-1);
+ dev->name[IFNAMSIZ-1] = '\0';
+
+ priv_link = MALLOC(wl->osh, sizeof(priv_link_t));
+ if (!priv_link) {
+ WL_ERROR(("wl%d: %s: malloc of priv_link failed\n",
+ (wl->pub)?wl->pub->unit:wlif->subunit, __FUNCTION__));
+ MFREE(wl->osh, dev, sizeof(struct net_device));
+ return NULL;
+ }
+ dev->priv = priv_link;
+#else
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
+ dev = alloc_netdev(sizeof(priv_link_t), intf_name, ether_setup);
+#else
+ dev = alloc_netdev(sizeof(priv_link_t), intf_name, NET_NAME_UNKNOWN, ether_setup);
+#endif
+
+ if (!dev) {
+ WL_ERROR(("wl%d: %s: alloc_netdev failed\n",
+ (wl->pub)?wl->pub->unit:wlif->subunit, __FUNCTION__));
+ return NULL;
+ }
+ priv_link = netdev_priv(dev);
+ if (!priv_link) {
+ WL_ERROR(("wl%d: %s: cannot get netdev_priv\n",
+ (wl->pub)?wl->pub->unit:wlif->subunit, __FUNCTION__));
+ return NULL;
+ }
+#endif
+
+ priv_link->wlif = wlif;
+ wlif->dev = dev;
+
+ if (wlif->if_type != WL_IFTYPE_MON && wl->dev && netif_queue_stopped(wl->dev))
+ netif_stop_queue(dev);
+
+ return dev;
+}
+
+char *
+wl_ifname(wl_info_t *wl, wl_if_t *wlif)
+{
+ if (wlif) {
+ return wlif->name;
+ } else {
+ return wl->dev->name;
+ }
+}
+
+void
+wl_init(wl_info_t *wl)
+{
+ WL_TRACE(("wl%d: wl_init\n", wl->pub->unit));
+
+ wl_reset(wl);
+
+ wlc_init(wl->wlc);
+}
+
+uint
+wl_reset(wl_info_t *wl)
+{
+ uint32 macintmask;
+
+ WL_TRACE(("wl%d: wl_reset\n", wl->pub->unit));
+
+ macintmask = wl_intrsoff(wl);
+
+ wlc_reset(wl->wlc);
+
+ wl_intrsrestore(wl, macintmask);
+
+ wl->resched = 0;
+
+ return (0);
+}
+
+void BCMFASTPATH
+wl_intrson(wl_info_t *wl)
+{
+ unsigned long flags = 0;
+
+ INT_LOCK(wl, flags);
+ wlc_intrson(wl->wlc);
+ INT_UNLOCK(wl, flags);
+}
+
+bool
+wl_alloc_dma_resources(wl_info_t *wl, uint addrwidth)
+{
+ return TRUE;
+}
+
+uint32 BCMFASTPATH
+wl_intrsoff(wl_info_t *wl)
+{
+ unsigned long flags = 0;
+ uint32 status;
+
+ INT_LOCK(wl, flags);
+ status = wlc_intrsoff(wl->wlc);
+ INT_UNLOCK(wl, flags);
+ return status;
+}
+
+void
+wl_intrsrestore(wl_info_t *wl, uint32 macintmask)
+{
+ unsigned long flags = 0;
+
+ INT_LOCK(wl, flags);
+ wlc_intrsrestore(wl->wlc, macintmask);
+ INT_UNLOCK(wl, flags);
+}
+
+int
+wl_up(wl_info_t *wl)
+{
+ int error = 0;
+ wl_if_t *wlif;
+
+ WL_TRACE(("wl%d: wl_up\n", wl->pub->unit));
+
+ if (wl->pub->up)
+ return (0);
+
+ error = wlc_up(wl->wlc);
+
+ if (!error) {
+ for (wlif = wl->if_list; wlif != NULL; wlif = wlif->next) {
+ wl_txflowcontrol(wl, wlif, OFF, ALLPRIO);
+ }
+ }
+
+ return (error);
+}
+
+void
+wl_down(wl_info_t *wl)
+{
+ wl_if_t *wlif;
+ int monitor = 0;
+ uint callbacks, ret_val = 0;
+
+ WL_TRACE(("wl%d: wl_down\n", wl->pub->unit));
+
+ for (wlif = wl->if_list; wlif != NULL; wlif = wlif->next) {
+ if (wlif->dev) {
+ netif_down(wlif->dev);
+ netif_stop_queue(wlif->dev);
+ }
+ }
+
+ if (wl->monitor_dev) {
+ ret_val = wlc_ioctl(wl->wlc, WLC_SET_MONITOR, &monitor, sizeof(int), NULL);
+ if (ret_val != BCME_OK) {
+ WL_ERROR(("%s: Disabling MONITOR failed %d\n", __FUNCTION__, ret_val));
+ }
+ }
+
+ if (wl->wlc)
+ ret_val = wlc_down(wl->wlc);
+
+ callbacks = atomic_read(&wl->callbacks) - ret_val;
+ BCM_REFERENCE(callbacks);
+
+ WL_UNLOCK(wl);
+
+ if (WL_ALL_PASSIVE_ENAB(wl)) {
+ int i = 0;
+ for (i = 0; (atomic_read(&wl->callbacks) > callbacks) && i < 10000; i++) {
+ schedule();
+ flush_scheduled_work();
+ }
+ }
+ else
+ {
+
+ SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000);
+ }
+
+ WL_LOCK(wl);
+}
+
+static int
+wl_toe_get(wl_info_t *wl, uint32 *toe_ol)
+{
+ if (wlc_iovar_getint(wl->wlc, "toe_ol", toe_ol) != 0)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int
+wl_toe_set(wl_info_t *wl, uint32 toe_ol)
+{
+ if (wlc_iovar_setint(wl->wlc, "toe_ol", toe_ol) != 0)
+ return -EOPNOTSUPP;
+
+ if (wlc_iovar_setint(wl->wlc, "toe", (toe_ol != 0)) != 0)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static void
+wl_get_driver_info(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ wl_info_t *wl = WL_INFO(dev);
+
+#if WIRELESS_EXT >= 19 || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+ if (!wl || !wl->pub || !wl->wlc || !wl->dev)
+ return;
+#endif
+ bzero(info, sizeof(struct ethtool_drvinfo));
+ snprintf(info->driver, sizeof(info->driver), "wl%d", wl->pub->unit);
+ strncpy(info->version, EPI_VERSION_STR, sizeof(info->version));
+ info->version[(sizeof(info->version))-1] = '\0';
+}
+
+static int
+wl_ethtool(wl_info_t *wl, void *uaddr, wl_if_t *wlif)
+{
+ struct ethtool_drvinfo info;
+ struct ethtool_value edata;
+ uint32 cmd;
+ uint32 toe_cmpnt = 0, csum_dir;
+ int ret;
+
+ if (!wl || !wl->pub || !wl->wlc)
+ return -ENODEV;
+
+ WL_TRACE(("wl%d: %s\n", wl->pub->unit, __FUNCTION__));
+
+ if (copy_from_user(&cmd, uaddr, sizeof(uint32)))
+ return (-EFAULT);
+
+ switch (cmd) {
+ case ETHTOOL_GDRVINFO:
+ if (!wl->dev)
+ return -ENETDOWN;
+
+ wl_get_driver_info(wl->dev, &info);
+ info.cmd = cmd;
+ if (copy_to_user(uaddr, &info, sizeof(info)))
+ return (-EFAULT);
+ break;
+
+ case ETHTOOL_GRXCSUM:
+ case ETHTOOL_GTXCSUM:
+ if ((ret = wl_toe_get(wl, &toe_cmpnt)) < 0)
+ return ret;
+
+ csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+ edata.cmd = cmd;
+ edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
+
+ if (copy_to_user(uaddr, &edata, sizeof(edata)))
+ return (-EFAULT);
+ break;
+
+ case ETHTOOL_SRXCSUM:
+ case ETHTOOL_STXCSUM:
+ if (copy_from_user(&edata, uaddr, sizeof(edata)))
+ return (-EFAULT);
+
+ if ((ret = wl_toe_get(wl, &toe_cmpnt)) < 0)
+ return ret;
+
+ csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+ if (edata.data != 0)
+ toe_cmpnt |= csum_dir;
+ else
+ toe_cmpnt &= ~csum_dir;
+
+ if ((ret = wl_toe_set(wl, toe_cmpnt)) < 0)
+ return ret;
+
+ if (cmd == ETHTOOL_STXCSUM) {
+ if (!wl->dev)
+ return -ENETDOWN;
+ if (edata.data)
+ wl->dev->features |= NETIF_F_IP_CSUM;
+ else
+ wl->dev->features &= ~NETIF_F_IP_CSUM;
+ }
+
+ break;
+
+ default:
+ return (-EOPNOTSUPP);
+
+ }
+
+ return (0);
+}
+
+int
+wl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ wl_info_t *wl;
+ wl_if_t *wlif;
+ void *buf = NULL;
+ wl_ioctl_t ioc;
+ int bcmerror;
+
+ if (!dev)
+ return -ENETDOWN;
+
+ wl = WL_INFO(dev);
+ wlif = WL_DEV_IF(dev);
+ if (wlif == NULL || wl == NULL || wl->dev == NULL)
+ return -ENETDOWN;
+
+ bcmerror = 0;
+
+ WL_TRACE(("wl%d: wl_ioctl: cmd 0x%x\n", wl->pub->unit, cmd));
+
+#ifdef USE_IW
+
+ if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) {
+
+ return wl_iw_ioctl(dev, ifr, cmd);
+ }
+#endif
+
+ if (cmd == SIOCETHTOOL)
+ return (wl_ethtool(wl, (void*)ifr->ifr_data, wlif));
+
+ switch (cmd) {
+ case SIOCDEVPRIVATE :
+ break;
+ default:
+ bcmerror = BCME_UNSUPPORTED;
+ goto done2;
+ }
+
+ if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) {
+ bcmerror = BCME_BADADDR;
+ goto done2;
+ }
+
+ if (segment_eq(get_fs(), KERNEL_DS))
+ buf = ioc.buf;
+
+ else if (ioc.buf) {
+ if (!(buf = (void *) MALLOC(wl->osh, MAX(ioc.len, WLC_IOCTL_MAXLEN)))) {
+ bcmerror = BCME_NORESOURCE;
+ goto done2;
+ }
+
+ if (copy_from_user(buf, ioc.buf, ioc.len)) {
+ bcmerror = BCME_BADADDR;
+ goto done1;
+ }
+ }
+
+ WL_LOCK(wl);
+ if (!capable(CAP_NET_ADMIN)) {
+ bcmerror = BCME_EPERM;
+ } else {
+ bcmerror = wlc_ioctl(wl->wlc, ioc.cmd, buf, ioc.len, wlif->wlcif);
+ }
+ WL_UNLOCK(wl);
+
+done1:
+ if (ioc.buf && (ioc.buf != buf)) {
+ if (copy_to_user(ioc.buf, buf, ioc.len))
+ bcmerror = BCME_BADADDR;
+ MFREE(wl->osh, buf, MAX(ioc.len, WLC_IOCTL_MAXLEN));
+ }
+
+done2:
+ ASSERT(VALID_BCMERROR(bcmerror));
+ if (bcmerror != 0)
+ wl->pub->bcmerror = bcmerror;
+ return (OSL_ERROR(bcmerror));
+}
+
+static struct net_device_stats*
+wl_get_stats(struct net_device *dev)
+{
+ struct net_device_stats *stats_watchdog = NULL;
+ struct net_device_stats *stats = NULL;
+ wl_info_t *wl;
+ wl_if_t *wlif;
+
+ if (!dev)
+ return NULL;
+
+ if ((wl = WL_INFO(dev)) == NULL)
+ return NULL;
+
+ if ((wlif = WL_DEV_IF(dev)) == NULL)
+ return NULL;
+
+ if ((stats = &wlif->stats) == NULL)
+ return NULL;
+
+ WL_TRACE(("wl%d: wl_get_stats\n", wl->pub->unit));
+
+ ASSERT(wlif->stats_id < 2);
+ stats_watchdog = &wlif->stats_watchdog[wlif->stats_id];
+ memcpy(stats, stats_watchdog, sizeof(struct net_device_stats));
+ return (stats);
+}
+
+#ifdef USE_IW
+struct iw_statistics *
+wl_get_wireless_stats(struct net_device *dev)
+{
+ int res = 0;
+ wl_info_t *wl;
+ wl_if_t *wlif;
+ struct iw_statistics *wstats = NULL;
+ struct iw_statistics *wstats_watchdog = NULL;
+ int phy_noise, rssi;
+
+ if (!dev)
+ return NULL;
+
+ if ((wl = WL_INFO(dev)) == NULL)
+ return NULL;
+
+ if ((wlif = WL_DEV_IF(dev)) == NULL)
+ return NULL;
+
+ if ((wstats = &wlif->wstats) == NULL)
+ return NULL;
+
+ WL_TRACE(("wl%d: wl_get_wireless_stats\n", wl->pub->unit));
+
+ ASSERT(wlif->stats_id < 2);
+ wstats_watchdog = &wlif->wstats_watchdog[wlif->stats_id];
+
+ phy_noise = wlif->phy_noise;
+#if WIRELESS_EXT > 11
+ wstats->discard.nwid = 0;
+ wstats->discard.code = wstats_watchdog->discard.code;
+ wstats->discard.fragment = wstats_watchdog->discard.fragment;
+ wstats->discard.retries = wstats_watchdog->discard.retries;
+ wstats->discard.misc = wstats_watchdog->discard.misc;
+
+ wstats->miss.beacon = 0;
+#endif
+
+ if (AP_ENAB(wl->pub))
+ rssi = 0;
+ else {
+ scb_val_t scb;
+ res = wlc_ioctl(wl->wlc, WLC_GET_RSSI, &scb, sizeof(int), wlif->wlcif);
+ if (res) {
+ WL_ERROR(("wl%d: %s: WLC_GET_RSSI failed (%d)\n",
+ wl->pub->unit, __FUNCTION__, res));
+ return NULL;
+ }
+ rssi = scb.val;
+ }
+
+ if (rssi <= WLC_RSSI_NO_SIGNAL)
+ wstats->qual.qual = 0;
+ else if (rssi <= WLC_RSSI_VERY_LOW)
+ wstats->qual.qual = 1;
+ else if (rssi <= WLC_RSSI_LOW)
+ wstats->qual.qual = 2;
+ else if (rssi <= WLC_RSSI_GOOD)
+ wstats->qual.qual = 3;
+ else if (rssi <= WLC_RSSI_VERY_GOOD)
+ wstats->qual.qual = 4;
+ else
+ wstats->qual.qual = 5;
+
+ wstats->qual.level = 0x100 + rssi;
+ wstats->qual.noise = 0x100 + phy_noise;
+#if WIRELESS_EXT > 18
+ wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM);
+#else
+ wstats->qual.updated |= 7;
+#endif
+
+ return wstats;
+}
+#endif
+
+static int
+wl_set_mac_address(struct net_device *dev, void *addr)
+{
+ int err = 0;
+ wl_info_t *wl;
+ struct sockaddr *sa = (struct sockaddr *) addr;
+
+ if (!dev)
+ return -ENETDOWN;
+
+ wl = WL_INFO(dev);
+
+ WL_TRACE(("wl%d: wl_set_mac_address\n", wl->pub->unit));
+
+ WL_LOCK(wl);
+
+ bcopy(sa->sa_data, dev->dev_addr, ETHER_ADDR_LEN);
+ err = wlc_iovar_op(wl->wlc, "cur_etheraddr", NULL, 0, sa->sa_data, ETHER_ADDR_LEN,
+ IOV_SET, (WL_DEV_IF(dev))->wlcif);
+ WL_UNLOCK(wl);
+ if (err)
+ WL_ERROR(("wl%d: wl_set_mac_address: error setting MAC addr override\n",
+ wl->pub->unit));
+ return err;
+}
+
+static void
+wl_set_multicast_list(struct net_device *dev)
+{
+ if (!WL_ALL_PASSIVE_ENAB((wl_info_t *)WL_INFO(dev)))
+ _wl_set_multicast_list(dev);
+ else {
+ wl_info_t *wl = WL_INFO(dev);
+ wl->multicast_task.context = dev;
+
+ if (schedule_work(&wl->multicast_task.work)) {
+
+ atomic_inc(&wl->callbacks);
+ }
+ }
+}
+
+static void
+_wl_set_multicast_list(struct net_device *dev)
+{
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34)
+ struct dev_mc_list *mclist;
+#else
+ struct netdev_hw_addr *ha;
+#endif
+ wl_info_t *wl;
+ int i, buflen;
+ struct maclist *maclist;
+ int allmulti;
+
+ if (!dev)
+ return;
+ wl = WL_INFO(dev);
+
+ WL_TRACE(("wl%d: wl_set_multicast_list\n", wl->pub->unit));
+
+ if (wl->pub->up) {
+ allmulti = (dev->flags & IFF_ALLMULTI)? TRUE: FALSE;
+
+ buflen = sizeof(struct maclist) + (MAXMULTILIST * ETHER_ADDR_LEN);
+
+ if ((maclist = MALLOC(wl->pub->osh, buflen)) == NULL) {
+ return;
+ }
+
+ i = 0;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34)
+ for (mclist = dev->mc_list; mclist && (i < dev->mc_count); mclist = mclist->next) {
+ if (i >= MAXMULTILIST) {
+ allmulti = TRUE;
+ i = 0;
+ break;
+ }
+ bcopy(mclist->dmi_addr, &maclist->ea[i++], ETHER_ADDR_LEN);
+ }
+#else
+ netdev_for_each_mc_addr(ha, dev) {
+ if (i >= MAXMULTILIST) {
+ allmulti = TRUE;
+ i = 0;
+ break;
+ }
+ bcopy(ha->addr, &maclist->ea[i++], ETHER_ADDR_LEN);
+ }
+#endif
+ maclist->count = i;
+
+ WL_LOCK(wl);
+
+ wlc_iovar_op(wl->wlc, "allmulti", NULL, 0, &allmulti, sizeof(allmulti), IOV_SET,
+ (WL_DEV_IF(dev))->wlcif);
+ wlc_set(wl->wlc, WLC_SET_PROMISC, (dev->flags & IFF_PROMISC));
+
+ wlc_iovar_op(wl->wlc, "mcast_list", NULL, 0, maclist, buflen, IOV_SET,
+ (WL_DEV_IF(dev))->wlcif);
+
+ WL_UNLOCK(wl);
+ MFREE(wl->pub->osh, maclist, buflen);
+ }
+
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+irqreturn_t BCMFASTPATH
+wl_isr(int irq, void *dev_id)
+#else
+irqreturn_t BCMFASTPATH
+wl_isr(int irq, void *dev_id, struct pt_regs *ptregs)
+#endif
+{
+ wl_info_t *wl;
+ bool ours, wantdpc;
+ unsigned long flags;
+
+ wl = (wl_info_t*) dev_id;
+
+ WL_ISRLOCK(wl, flags);
+
+ if ((ours = wlc_isr(wl->wlc, &wantdpc))) {
+
+ if (wantdpc) {
+
+ ASSERT(wl->resched == FALSE);
+ if (WL_ALL_PASSIVE_ENAB(wl)) {
+ if (schedule_work(&wl->wl_dpc_task.work))
+ atomic_inc(&wl->callbacks);
+ else
+ ASSERT(0);
+ } else
+ tasklet_schedule(&wl->tasklet);
+ }
+ }
+
+ WL_ISRUNLOCK(wl, flags);
+
+ return IRQ_RETVAL(ours);
+}
+
+static void BCMFASTPATH
+wl_dpc(ulong data)
+{
+ wl_info_t *wl;
+
+ wl = (wl_info_t *)data;
+
+ WL_LOCK(wl);
+
+ if (wl->pub->up) {
+ wlc_dpc_info_t dpci = {0};
+
+ if (wl->resched) {
+ unsigned long flags = 0;
+ INT_LOCK(wl, flags);
+ wlc_intrsupd(wl->wlc);
+ INT_UNLOCK(wl, flags);
+ }
+
+ wl->resched = wlc_dpc(wl->wlc, TRUE, &dpci);
+
+ wl->processed = dpci.processed;
+ }
+
+ if (!wl->pub->up) {
+
+ if ((WL_ALL_PASSIVE_ENAB(wl))) {
+ atomic_dec(&wl->callbacks);
+ }
+ goto done;
+ }
+
+ if (wl->resched) {
+ if (!(WL_ALL_PASSIVE_ENAB(wl)))
+ tasklet_schedule(&wl->tasklet);
+ else
+ if (!schedule_work(&wl->wl_dpc_task.work)) {
+
+ ASSERT(0);
+ }
+ }
+ else {
+
+ if (WL_ALL_PASSIVE_ENAB(wl))
+ atomic_dec(&wl->callbacks);
+ wl_intrson(wl);
+ }
+
+done:
+ WL_UNLOCK(wl);
+ return;
+}
+
+static void BCMFASTPATH
+wl_dpc_rxwork(struct wl_task *task)
+{
+ wl_info_t *wl = (wl_info_t *)task->context;
+ WL_TRACE(("wl%d: %s\n", wl->pub->unit, __FUNCTION__));
+
+ wl_dpc((unsigned long)wl);
+ return;
+}
+
+void BCMFASTPATH
+wl_sendup(wl_info_t *wl, wl_if_t *wlif, void *p, int numpkt)
+{
+ struct sk_buff *skb;
+ bool brcm_specialpkt;
+
+ WL_TRACE(("wl%d: wl_sendup: %d bytes\n", wl->pub->unit, PKTLEN(wl->osh, p)));
+
+ brcm_specialpkt =
+ (ntoh16_ua(PKTDATA(wl->pub->osh, p) + ETHER_TYPE_OFFSET) == ETHER_TYPE_BRCM);
+
+ if (!brcm_specialpkt) {
+
+ }
+
+ if (wlif) {
+
+ if (!wlif->dev || !netif_device_present(wlif->dev)) {
+ WL_ERROR(("wl%d: wl_sendup: interface not ready\n", wl->pub->unit));
+ PKTFREE(wl->osh, p, FALSE);
+ return;
+ }
+
+ skb = PKTTONATIVE(wl->osh, p);
+ skb->dev = wlif->dev;
+ } else {
+
+ skb = PKTTONATIVE(wl->osh, p);
+ skb->dev = wl->dev;
+
+ }
+
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ if (!brcm_specialpkt && !ISALIGNED(skb->data, 4)) {
+ WL_ERROR(("Unaligned assert. skb %p. skb->data %p.\n", skb, skb->data));
+ if (wlif) {
+ WL_ERROR(("wl_sendup: dev name is %s (wlif) \n", wlif->dev->name));
+ WL_ERROR(("wl_sendup: hard header len %d (wlif) \n",
+ wlif->dev->hard_header_len));
+ }
+ WL_ERROR(("wl_sendup: dev name is %s (wl) \n", wl->dev->name));
+ WL_ERROR(("wl_sendup: hard header len %d (wl) \n", wl->dev->hard_header_len));
+ ASSERT(ISALIGNED(skb->data, 4));
+ }
+
+ WL_APSTA_RX(("wl%d: wl_sendup(): pkt %p summed %d on interface %p (%s)\n",
+ wl->pub->unit, p, skb->ip_summed, wlif, skb->dev->name));
+
+ netif_rx(skb);
+
+}
+
+int
+wl_osl_pcie_rc(struct wl_info *wl, uint op, int param)
+{
+ return 0;
+}
+
+void
+wl_dump_ver(wl_info_t *wl, struct bcmstrbuf *b)
+{
+ bcm_bprintf(b, "wl%d: %s %s version %s\n", wl->pub->unit,
+ __DATE__, __TIME__, EPI_VERSION_STR);
+}
+
+#if defined(BCMDBG)
+static int
+wl_dump(wl_info_t *wl, struct bcmstrbuf *b)
+{
+ wl_if_t *p;
+ int i;
+
+ wl_dump_ver(wl, b);
+
+ bcm_bprintf(b, "name %s dev %p tbusy %d callbacks %d malloced %d\n",
+ wl->dev->name, wl->dev, (uint)netif_queue_stopped(wl->dev),
+ atomic_read(&wl->callbacks), MALLOCED(wl->osh));
+
+ p = wl->if_list;
+ if (p)
+ p = p->next;
+ for (i = 0; p != NULL; p = p->next, i++) {
+ if ((i % 4) == 0) {
+ if (i != 0)
+ bcm_bprintf(b, "\n");
+ bcm_bprintf(b, "Interfaces:");
+ }
+ bcm_bprintf(b, " name %s dev %p", p->dev->name, p->dev);
+ }
+ if (i)
+ bcm_bprintf(b, "\n");
+
+ return 0;
+}
+#endif
+
+static void
+wl_link_up(wl_info_t *wl, char *ifname)
+{
+ WL_ERROR(("wl%d: link up (%s)\n", wl->pub->unit, ifname));
+}
+
+static void
+wl_link_down(wl_info_t *wl, char *ifname)
+{
+ WL_ERROR(("wl%d: link down (%s)\n", wl->pub->unit, ifname));
+}
+
+void
+wl_event(wl_info_t *wl, char *ifname, wlc_event_t *e)
+{
+#ifdef USE_IW
+ wl_iw_event(wl->dev, &(e->event), e->data);
+#endif
+
+#if defined(USE_CFG80211)
+ wl_cfg80211_event(wl->dev, &(e->event), e->data);
+#endif
+ switch (e->event.event_type) {
+ case WLC_E_LINK:
+ case WLC_E_NDIS_LINK:
+ if (e->event.flags&WLC_EVENT_MSG_LINK)
+ wl_link_up(wl, ifname);
+ else
+ wl_link_down(wl, ifname);
+ break;
+#if defined(WL_CONFIG_RFKILL)
+ case WLC_E_RADIO: {
+ mbool i;
+ if (wlc_get(wl->wlc, WLC_GET_RADIO, &i) < 0)
+ WL_ERROR(("%s: WLC_GET_RADIO failed\n", __FUNCTION__));
+ if (wl->last_phyind == (mbool)(i & WL_RADIO_HW_DISABLE))
+ break;
+
+ wl->last_phyind = (mbool)(i & WL_RADIO_HW_DISABLE);
+
+ WL_ERROR(("wl%d: Radio hardware state changed to %d\n", wl->pub->unit, i));
+ wl_report_radio_state(wl);
+ break;
+ }
+#else
+ case WLC_E_RADIO:
+ break;
+#endif
+ }
+}
+
+void
+wl_event_sync(wl_info_t *wl, char *ifname, wlc_event_t *e)
+{
+}
+
+static void BCMFASTPATH
+wl_sched_tx_tasklet(void *t)
+{
+ wl_info_t *wl = (wl_info_t *)t;
+ tasklet_schedule(&wl->tx_tasklet);
+}
+
+#define WL_CONFIG_SMP() FALSE
+
+static int BCMFASTPATH
+wl_start(struct sk_buff *skb, struct net_device *dev)
+{
+ wl_if_t *wlif;
+ wl_info_t *wl;
+
+ if (!dev)
+ return -ENETDOWN;
+
+ wlif = WL_DEV_IF(dev);
+ wl = WL_INFO(dev);
+
+ if (WL_ALL_PASSIVE_ENAB(wl) || (WL_RTR() && WL_CONFIG_SMP())) {
+ skb->prev = NULL;
+
+ TXQ_LOCK(wl);
+
+ if ((wl_txq_thresh > 0) && (wl->txq_cnt >= wl_txq_thresh)) {
+ PKTFRMNATIVE(wl->osh, skb);
+ PKTCFREE(wl->osh, skb, TRUE);
+ TXQ_UNLOCK(wl);
+ return 0;
+ }
+
+ if (wl->txq_head == NULL)
+ wl->txq_head = skb;
+ else
+ wl->txq_tail->prev = skb;
+ wl->txq_tail = skb;
+ wl->txq_cnt++;
+
+ if (!wl->txq_dispatched) {
+ int32 err = 0;
+
+ if (!WL_ALL_PASSIVE_ENAB(wl))
+ wl_sched_tx_tasklet(wl);
+ else
+ err = (int32)(schedule_work(&wl->txq_task.work) == 0);
+
+ if (!err) {
+ atomic_inc(&wl->callbacks);
+ wl->txq_dispatched = TRUE;
+ } else
+ WL_ERROR(("wl%d: wl_start/schedule_work failed\n",
+ wl->pub->unit));
+ }
+
+ TXQ_UNLOCK(wl);
+ } else
+ return wl_start_int(wl, wlif, skb);
+
+ return (0);
+}
+
+static void BCMFASTPATH
+wl_start_txqwork(wl_task_t *task)
+{
+ wl_info_t *wl = (wl_info_t *)task->context;
+ struct sk_buff *skb;
+
+ WL_TRACE(("wl%d: %s txq_cnt %d\n", wl->pub->unit, __FUNCTION__, wl->txq_cnt));
+
+#ifdef BCMDBG
+ if (wl->txq_cnt >= 500)
+ WL_ERROR(("wl%d: WARNING dispatching over 500 packets in txqwork(%d)\n",
+ wl->pub->unit, wl->txq_cnt));
+#endif
+
+ TXQ_LOCK(wl);
+ while (wl->txq_head) {
+ skb = wl->txq_head;
+ wl->txq_head = skb->prev;
+ skb->prev = NULL;
+ if (wl->txq_head == NULL)
+ wl->txq_tail = NULL;
+ wl->txq_cnt--;
+ TXQ_UNLOCK(wl);
+
+ wl_start_int(wl, WL_DEV_IF(skb->dev), skb);
+
+ TXQ_LOCK(wl);
+ }
+
+ wl->txq_dispatched = FALSE;
+ atomic_dec(&wl->callbacks);
+ TXQ_UNLOCK(wl);
+
+ return;
+}
+
+static void BCMFASTPATH
+wl_tx_tasklet(ulong data)
+{
+ wl_task_t task;
+ task.context = (void *)data;
+ wl_start_txqwork(&task);
+}
+
+static void
+wl_txq_free(wl_info_t *wl)
+{
+ struct sk_buff *skb;
+
+ if (wl->txq_head == NULL) {
+ ASSERT(wl->txq_tail == NULL);
+ return;
+ }
+
+ while (wl->txq_head) {
+ skb = wl->txq_head;
+ wl->txq_head = skb->prev;
+ wl->txq_cnt--;
+ PKTFRMNATIVE(wl->osh, skb);
+ PKTCFREE(wl->osh, skb, TRUE);
+ }
+
+ wl->txq_tail = NULL;
+}
+
+static void
+wl_set_multicast_list_workitem(struct work_struct *work)
+{
+ wl_task_t *task = (wl_task_t *)work;
+ struct net_device *dev = (struct net_device*)task->context;
+ wl_info_t *wl;
+
+ wl = WL_INFO(dev);
+
+ atomic_dec(&wl->callbacks);
+
+ _wl_set_multicast_list(dev);
+}
+
+static void
+wl_timer_task(wl_task_t *task)
+{
+ wl_timer_t *t = (wl_timer_t *)task->context;
+
+ _wl_timer(t);
+ MFREE(t->wl->osh, task, sizeof(wl_task_t));
+
+ atomic_dec(&t->wl->callbacks);
+}
+
+static void
+wl_timer(ulong data)
+{
+ wl_timer_t *t = (wl_timer_t *)data;
+
+ if (!WL_ALL_PASSIVE_ENAB(t->wl))
+ _wl_timer(t);
+ else
+ wl_schedule_task(t->wl, wl_timer_task, t);
+}
+
+static void
+_wl_timer(wl_timer_t *t)
+{
+ wl_info_t *wl = t->wl;
+
+ WL_LOCK(wl);
+
+ if (t->set && (!timer_pending(&t->timer))) {
+ if (t->periodic) {
+ t->timer.expires = jiffies + t->ms*HZ/1000;
+ atomic_inc(&wl->callbacks);
+ add_timer(&t->timer);
+ t->set = TRUE;
+ } else
+ t->set = FALSE;
+
+ t->fn(t->arg);
+#ifdef BCMDBG
+ wlc_update_perf_stats(wl->wlc, WLC_PERF_STATS_TMR_DPC);
+ t->ticks++;
+#endif
+
+ }
+
+ atomic_dec(&wl->callbacks);
+
+ WL_UNLOCK(wl);
+}
+
+wl_timer_t *
+wl_init_timer(wl_info_t *wl, void (*fn)(void *arg), void *arg, const char *tname)
+{
+ wl_timer_t *t;
+
+ t = (wl_timer_t*)MALLOC(wl->osh, sizeof(wl_timer_t));
+
+ if (t == NULL) {
+ WL_ERROR(("wl%d: wl_init_timer: out of memory, malloced %d bytes\n",
+ wl->unit, MALLOCED(wl->osh)));
+ return 0;
+ }
+
+ bzero(t, sizeof(wl_timer_t));
+
+ init_timer(&t->timer);
+ t->timer.data = (ulong) t;
+ t->timer.function = wl_timer;
+ t->wl = wl;
+ t->fn = fn;
+ t->arg = arg;
+ t->next = wl->timers;
+ wl->timers = t;
+
+#ifdef BCMDBG
+ if ((t->name = MALLOC(wl->osh, strlen(tname) + 1)))
+ strcpy(t->name, tname);
+#endif
+
+ return t;
+}
+
+void
+wl_add_timer(wl_info_t *wl, wl_timer_t *t, uint ms, int periodic)
+{
+#ifdef BCMDBG
+ if (t->set) {
+ WL_ERROR(("%s: Already set. Name: %s, per %d\n",
+ __FUNCTION__, t->name, periodic));
+ }
+#endif
+
+ t->ms = ms;
+ t->periodic = (bool) periodic;
+
+ if (t->set)
+ return;
+
+ t->set = TRUE;
+ t->timer.expires = jiffies + ms*HZ/1000;
+
+ atomic_inc(&wl->callbacks);
+ add_timer(&t->timer);
+}
+
+bool
+wl_del_timer(wl_info_t *wl, wl_timer_t *t)
+{
+ ASSERT(t);
+ if (t->set) {
+ t->set = FALSE;
+ if (!del_timer(&t->timer)) {
+#ifdef BCMDBG
+ WL_INFORM(("wl%d: Failed to delete timer %s\n", wl->unit, t->name));
+#endif
+ return TRUE;
+ }
+ atomic_dec(&wl->callbacks);
+ }
+
+ return TRUE;
+}
+
+void
+wl_free_timer(wl_info_t *wl, wl_timer_t *t)
+{
+ wl_timer_t *tmp;
+
+ wl_del_timer(wl, t);
+
+ if (wl->timers == t) {
+ wl->timers = wl->timers->next;
+#ifdef BCMDBG
+ if (t->name)
+ MFREE(wl->osh, t->name, strlen(t->name) + 1);
+#endif
+ MFREE(wl->osh, t, sizeof(wl_timer_t));
+ return;
+
+ }
+
+ tmp = wl->timers;
+ while (tmp) {
+ if (tmp->next == t) {
+ tmp->next = t->next;
+#ifdef BCMDBG
+ if (t->name)
+ MFREE(wl->osh, t->name, strlen(t->name) + 1);
+#endif
+ MFREE(wl->osh, t, sizeof(wl_timer_t));
+ return;
+ }
+ tmp = tmp->next;
+ }
+
+}
+
+void
+wl_monitor(wl_info_t *wl, wl_rxsts_t *rxsts, void *p)
+{
+ struct sk_buff *oskb = (struct sk_buff *)p;
+ struct sk_buff *skb;
+ uchar *pdata;
+ uint len;
+
+ len = 0;
+ skb = NULL;
+ WL_TRACE(("wl%d: wl_monitor\n", wl->pub->unit));
+
+ if (!wl->monitor_dev)
+ return;
+
+ if (wl->monitor_type == 1) {
+ p80211msg_t *phdr;
+
+ len = sizeof(p80211msg_t) + oskb->len - D11_PHY_HDR_LEN;
+ if ((skb = dev_alloc_skb(len)) == NULL) {
+ WL_ERROR(("%s: dev_alloc_skb() failure, mon type 1", __FUNCTION__));
+ return;
+ }
+
+ skb_put(skb, len);
+ phdr = (p80211msg_t*)skb->data;
+
+ phdr->msgcode = WL_MON_FRAME;
+ phdr->msglen = sizeof(p80211msg_t);
+ strcpy(phdr->devname, wl->dev->name);
+
+ phdr->hosttime.did = WL_MON_FRAME_HOSTTIME;
+ phdr->hosttime.status = P80211ITEM_OK;
+ phdr->hosttime.len = 4;
+ phdr->hosttime.data = jiffies;
+
+ phdr->channel.did = WL_MON_FRAME_CHANNEL;
+ phdr->channel.status = P80211ITEM_NO_VALUE;
+ phdr->channel.len = 4;
+ phdr->channel.data = 0;
+
+ phdr->signal.did = WL_MON_FRAME_SIGNAL;
+ phdr->signal.status = P80211ITEM_OK;
+ phdr->signal.len = 4;
+
+ phdr->signal.data = rxsts->preamble;
+
+ phdr->noise.did = WL_MON_FRAME_NOISE;
+ phdr->noise.status = P80211ITEM_NO_VALUE;
+ phdr->noise.len = 4;
+ phdr->noise.data = 0;
+
+ phdr->rate.did = WL_MON_FRAME_RATE;
+ phdr->rate.status = P80211ITEM_OK;
+ phdr->rate.len = 4;
+ phdr->rate.data = rxsts->datarate;
+
+ phdr->istx.did = WL_MON_FRAME_ISTX;
+ phdr->istx.status = P80211ITEM_NO_VALUE;
+ phdr->istx.len = 4;
+ phdr->istx.data = 0;
+
+ phdr->mactime.did = WL_MON_FRAME_MACTIME;
+ phdr->mactime.status = P80211ITEM_OK;
+ phdr->mactime.len = 4;
+ phdr->mactime.data = rxsts->mactime;
+
+ phdr->rssi.did = WL_MON_FRAME_RSSI;
+ phdr->rssi.status = P80211ITEM_OK;
+ phdr->rssi.len = 4;
+ phdr->rssi.data = rxsts->signal;
+
+ phdr->sq.did = WL_MON_FRAME_SQ;
+ phdr->sq.status = P80211ITEM_OK;
+ phdr->sq.len = 4;
+ phdr->sq.data = rxsts->sq;
+
+ phdr->frmlen.did = WL_MON_FRAME_FRMLEN;
+ phdr->frmlen.status = P80211ITEM_OK;
+ phdr->frmlen.status = P80211ITEM_OK;
+ phdr->frmlen.len = 4;
+ phdr->frmlen.data = rxsts->pktlength;
+
+ pdata = skb->data + sizeof(p80211msg_t);
+ bcopy(oskb->data + D11_PHY_HDR_LEN, pdata, oskb->len - D11_PHY_HDR_LEN);
+
+ }
+ else if (wl->monitor_type == 2) {
+ int channel_frequency;
+ uint16 channel_flags;
+ uint8 flags;
+ uint16 rtap_len;
+ struct dot11_header *mac_header;
+ uint16 fc;
+
+ if (rxsts->phytype != WL_RXS_PHY_N)
+ rtap_len = sizeof(wl_radiotap_legacy_t);
+ else
+ rtap_len = sizeof(wl_radiotap_ht_brcm_2_t);
+
+ len = rtap_len + (oskb->len - D11_PHY_HDR_LEN);
+ if ((skb = dev_alloc_skb(len)) == NULL) {
+ WL_ERROR(("%s: dev_alloc_skb() failure, mon type 2", __FUNCTION__));
+ return;
+ }
+
+ skb_put(skb, len);
+
+ if (CHSPEC_IS2G(rxsts->chanspec)) {
+ channel_flags = IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN;
+ channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
+ WF_CHAN_FACTOR_2_4_G);
+ } else {
+ channel_flags = IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
+ channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
+ WF_CHAN_FACTOR_5_G);
+ }
+
+ mac_header = (struct dot11_header *)(oskb->data + D11_PHY_HDR_LEN);
+ fc = ltoh16(mac_header->fc);
+
+ flags = IEEE80211_RADIOTAP_F_FCS;
+
+ if (rxsts->preamble == WL_RXS_PREAMBLE_SHORT)
+ flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+ if (fc & FC_WEP)
+ flags |= IEEE80211_RADIOTAP_F_WEP;
+
+ if (fc & FC_MOREFRAG)
+ flags |= IEEE80211_RADIOTAP_F_FRAG;
+
+ if (rxsts->pkterror & WL_RXS_CRC_ERROR)
+ flags |= IEEE80211_RADIOTAP_F_BADFCS;
+
+ if (rxsts->phytype != WL_RXS_PHY_N) {
+ wl_radiotap_legacy_t *rtl = (wl_radiotap_legacy_t *)skb->data;
+
+ rtl->ieee_radiotap.it_version = 0;
+ rtl->ieee_radiotap.it_pad = 0;
+ rtl->ieee_radiotap.it_len = HTOL16(rtap_len);
+ rtl->ieee_radiotap.it_present = HTOL32(WL_RADIOTAP_PRESENT_LEGACY);
+
+ rtl->tsft_l = htol32(rxsts->mactime);
+ rtl->tsft_h = 0;
+ rtl->flags = flags;
+ rtl->rate = rxsts->datarate;
+ rtl->channel_freq = HTOL16(channel_frequency);
+ rtl->channel_flags = HTOL16(channel_flags);
+ rtl->signal = (int8)rxsts->signal;
+ rtl->noise = (int8)rxsts->noise;
+ rtl->antenna = rxsts->antenna;
+
+ memcpy(rtl->vend_oui, brcm_oui, sizeof(brcm_oui));
+ rtl->vend_skip_len = WL_RADIOTAP_LEGACY_SKIP_LEN;
+ rtl->vend_sns = 0;
+
+ memset(&rtl->nonht_vht, 0, sizeof(rtl->nonht_vht));
+ rtl->nonht_vht.len = WL_RADIOTAP_NONHT_VHT_LEN;
+ } else {
+ wl_radiotap_ht_brcm_2_t *rtht = (wl_radiotap_ht_brcm_2_t *)skb->data;
+
+ rtht->ieee_radiotap.it_version = 0;
+ rtht->ieee_radiotap.it_pad = 0;
+ rtht->ieee_radiotap.it_len = HTOL16(rtap_len);
+ rtht->ieee_radiotap.it_present = HTOL32(WL_RADIOTAP_PRESENT_HT_BRCM2);
+ rtht->it_present_ext = HTOL32(WL_RADIOTAP_BRCM2_HT_MCS);
+ rtht->pad1 = 0;
+
+ rtht->tsft_l = htol32(rxsts->mactime);
+ rtht->tsft_h = 0;
+ rtht->flags = flags;
+ rtht->pad2 = 0;
+ rtht->channel_freq = HTOL16(channel_frequency);
+ rtht->channel_flags = HTOL16(channel_flags);
+ rtht->signal = (int8)rxsts->signal;
+ rtht->noise = (int8)rxsts->noise;
+ rtht->antenna = rxsts->antenna;
+ rtht->pad3 = 0;
+
+ memcpy(rtht->vend_oui, brcm_oui, sizeof(brcm_oui));
+ rtht->vend_sns = WL_RADIOTAP_BRCM2_HT_SNS;
+ rtht->vend_skip_len = WL_RADIOTAP_HT_BRCM2_SKIP_LEN;
+ rtht->mcs = rxsts->mcs;
+ rtht->htflags = 0;
+ if (rxsts->htflags & WL_RXS_HTF_40)
+ rtht->htflags |= IEEE80211_RADIOTAP_HTMOD_40;
+ if (rxsts->htflags & WL_RXS_HTF_SGI)
+ rtht->htflags |= IEEE80211_RADIOTAP_HTMOD_SGI;
+ if (rxsts->preamble & WL_RXS_PREAMBLE_HT_GF)
+ rtht->htflags |= IEEE80211_RADIOTAP_HTMOD_GF;
+ if (rxsts->htflags & WL_RXS_HTF_LDPC)
+ rtht->htflags |= IEEE80211_RADIOTAP_HTMOD_LDPC;
+ rtht->htflags |=
+ (rxsts->htflags & WL_RXS_HTF_STBC_MASK) <<
+ IEEE80211_RADIOTAP_HTMOD_STBC_SHIFT;
+ }
+
+ pdata = skb->data + rtap_len;
+ bcopy(oskb->data + D11_PHY_HDR_LEN, pdata, oskb->len - D11_PHY_HDR_LEN);
+ }
+ else if (wl->monitor_type == 3) {
+ int channel_frequency;
+ uint16 channel_flags;
+ uint8 flags;
+ uint16 rtap_len;
+ struct dot11_header * mac_header;
+ uint16 fc;
+
+ if (rxsts->phytype == WL_RXS_PHY_N) {
+ if (rxsts->encoding == WL_RXS_ENCODING_HT)
+ rtap_len = sizeof(wl_radiotap_ht_t);
+ else if (rxsts->encoding == WL_RXS_ENCODING_VHT)
+ rtap_len = sizeof(wl_radiotap_vht_t);
+ else
+ rtap_len = sizeof(wl_radiotap_legacy_t);
+ } else {
+ rtap_len = sizeof(wl_radiotap_legacy_t);
+ }
+
+ len = rtap_len + (oskb->len - D11_PHY_HDR_LEN);
+
+ if (oskb->next) {
+ struct sk_buff *amsdu_p = oskb->next;
+ uint amsdu_len = 0;
+ while (amsdu_p) {
+ amsdu_len += amsdu_p->len;
+ amsdu_p = amsdu_p->next;
+ }
+ len += amsdu_len;
+ }
+
+ if ((skb = dev_alloc_skb(len)) == NULL) {
+ WL_ERROR(("%s: dev_alloc_skb() failure, mon type 3", __FUNCTION__));
+ return;
+ }
+
+ skb_put(skb, len);
+
+ if (CHSPEC_IS2G(rxsts->chanspec)) {
+ channel_flags = IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN;
+ channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
+ WF_CHAN_FACTOR_2_4_G);
+ } else {
+ channel_flags = IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
+ channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec),
+ WF_CHAN_FACTOR_5_G);
+ }
+
+ mac_header = (struct dot11_header *)(oskb->data + D11_PHY_HDR_LEN);
+ fc = ltoh16(mac_header->fc);
+
+ flags = IEEE80211_RADIOTAP_F_FCS;
+
+ if (rxsts->preamble == WL_RXS_PREAMBLE_SHORT)
+ flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+ if (fc & FC_WEP)
+ flags |= IEEE80211_RADIOTAP_F_WEP;
+
+ if (fc & FC_MOREFRAG)
+ flags |= IEEE80211_RADIOTAP_F_FRAG;
+
+ if (rxsts->pkterror & WL_RXS_CRC_ERROR)
+ flags |= IEEE80211_RADIOTAP_F_BADFCS;
+
+ if ((rxsts->phytype != WL_RXS_PHY_N) ||
+ ((rxsts->encoding != WL_RXS_ENCODING_HT) &&
+ (rxsts->encoding != WL_RXS_ENCODING_VHT))) {
+ wl_radiotap_legacy_t *rtl = (wl_radiotap_legacy_t *)skb->data;
+
+ rtl->ieee_radiotap.it_version = 0;
+ rtl->ieee_radiotap.it_pad = 0;
+ rtl->ieee_radiotap.it_len = HTOL16(rtap_len);
+ rtl->ieee_radiotap.it_present = HTOL32(WL_RADIOTAP_PRESENT_LEGACY);
+
+ rtl->it_present_ext = HTOL32(WL_RADIOTAP_LEGACY_VHT);
+ rtl->tsft_l = htol32(rxsts->mactime);
+ rtl->tsft_h = 0;
+ rtl->flags = flags;
+ rtl->rate = rxsts->datarate;
+ rtl->channel_freq = HTOL16(channel_frequency);
+ rtl->channel_flags = HTOL16(channel_flags);
+ rtl->signal = (int8)rxsts->signal;
+ rtl->noise = (int8)rxsts->noise;
+ rtl->antenna = rxsts->antenna;
+
+ memcpy(rtl->vend_oui, brcm_oui, sizeof(brcm_oui));
+ rtl->vend_skip_len = WL_RADIOTAP_LEGACY_SKIP_LEN;
+ rtl->vend_sns = 0;
+
+ memset(&rtl->nonht_vht, 0, sizeof(rtl->nonht_vht));
+ rtl->nonht_vht.len = WL_RADIOTAP_NONHT_VHT_LEN;
+ if (((fc & FC_KIND_MASK) == FC_RTS) ||
+ ((fc & FC_KIND_MASK) == FC_CTS)) {
+ rtl->nonht_vht.flags |= WL_RADIOTAP_F_NONHT_VHT_BW;
+ rtl->nonht_vht.bw = rxsts->bw_nonht;
+ rtl->vend_sns = WL_RADIOTAP_LEGACY_SNS;
+
+ }
+ if ((fc & FC_KIND_MASK) == FC_RTS) {
+ if (rxsts->vhtflags & WL_RXS_VHTF_DYN_BW_NONHT)
+ rtl->nonht_vht.flags
+ |= WL_RADIOTAP_F_NONHT_VHT_DYN_BW;
+ }
+ }
+ else if (rxsts->encoding == WL_RXS_ENCODING_VHT) {
+ wl_radiotap_vht_t *rtvht = (wl_radiotap_vht_t *)skb->data;
+
+ rtvht->ieee_radiotap.it_version = 0;
+ rtvht->ieee_radiotap.it_pad = 0;
+ rtvht->ieee_radiotap.it_len = HTOL16(rtap_len);
+ rtvht->ieee_radiotap.it_present =
+ HTOL32(WL_RADIOTAP_PRESENT_VHT);
+
+ rtvht->tsft_l = htol32(rxsts->mactime);
+ rtvht->tsft_h = 0;
+ rtvht->flags = flags;
+ rtvht->pad1 = 0;
+ rtvht->channel_freq = HTOL16(channel_frequency);
+ rtvht->channel_flags = HTOL16(channel_flags);
+ rtvht->signal = (int8)rxsts->signal;
+ rtvht->noise = (int8)rxsts->noise;
+ rtvht->antenna = rxsts->antenna;
+
+ rtvht->vht_known = (IEEE80211_RADIOTAP_VHT_HAVE_STBC |
+ IEEE80211_RADIOTAP_VHT_HAVE_TXOP_PS |
+ IEEE80211_RADIOTAP_VHT_HAVE_GI |
+ IEEE80211_RADIOTAP_VHT_HAVE_SGI_NSYM_DA |
+ IEEE80211_RADIOTAP_VHT_HAVE_LDPC_EXTRA |
+ IEEE80211_RADIOTAP_VHT_HAVE_BF |
+ IEEE80211_RADIOTAP_VHT_HAVE_BW |
+ IEEE80211_RADIOTAP_VHT_HAVE_GID |
+ IEEE80211_RADIOTAP_VHT_HAVE_PAID);
+
+ STATIC_ASSERT(WL_RXS_VHTF_STBC ==
+ IEEE80211_RADIOTAP_VHT_STBC);
+ STATIC_ASSERT(WL_RXS_VHTF_TXOP_PS ==
+ IEEE80211_RADIOTAP_VHT_TXOP_PS);
+ STATIC_ASSERT(WL_RXS_VHTF_SGI ==
+ IEEE80211_RADIOTAP_VHT_SGI);
+ STATIC_ASSERT(WL_RXS_VHTF_SGI_NSYM_DA ==
+ IEEE80211_RADIOTAP_VHT_SGI_NSYM_DA);
+ STATIC_ASSERT(WL_RXS_VHTF_LDPC_EXTRA ==
+ IEEE80211_RADIOTAP_VHT_LDPC_EXTRA);
+ STATIC_ASSERT(WL_RXS_VHTF_BF ==
+ IEEE80211_RADIOTAP_VHT_BF);
+
+ rtvht->vht_flags = HTOL16(rxsts->vhtflags);
+
+ STATIC_ASSERT(WL_RXS_VHT_BW_20 ==
+ IEEE80211_RADIOTAP_VHT_BW_20);
+ STATIC_ASSERT(WL_RXS_VHT_BW_40 ==
+ IEEE80211_RADIOTAP_VHT_BW_40);
+ STATIC_ASSERT(WL_RXS_VHT_BW_20L ==
+ IEEE80211_RADIOTAP_VHT_BW_20L);
+ STATIC_ASSERT(WL_RXS_VHT_BW_20U ==
+ IEEE80211_RADIOTAP_VHT_BW_20U);
+ STATIC_ASSERT(WL_RXS_VHT_BW_80 ==
+ IEEE80211_RADIOTAP_VHT_BW_80);
+ STATIC_ASSERT(WL_RXS_VHT_BW_40L ==
+ IEEE80211_RADIOTAP_VHT_BW_40L);
+ STATIC_ASSERT(WL_RXS_VHT_BW_40U ==
+ IEEE80211_RADIOTAP_VHT_BW_40U);
+ STATIC_ASSERT(WL_RXS_VHT_BW_20LL ==
+ IEEE80211_RADIOTAP_VHT_BW_20LL);
+ STATIC_ASSERT(WL_RXS_VHT_BW_20LU ==
+ IEEE80211_RADIOTAP_VHT_BW_20LU);
+ STATIC_ASSERT(WL_RXS_VHT_BW_20UL ==
+ IEEE80211_RADIOTAP_VHT_BW_20UL);
+ STATIC_ASSERT(WL_RXS_VHT_BW_20UU ==
+ IEEE80211_RADIOTAP_VHT_BW_20UU);
+
+ rtvht->vht_bw = rxsts->bw;
+
+ rtvht->vht_mcs_nss[0] = (rxsts->mcs << 4) |
+ (rxsts->nss & IEEE80211_RADIOTAP_VHT_NSS);
+ rtvht->vht_mcs_nss[1] = 0;
+ rtvht->vht_mcs_nss[2] = 0;
+ rtvht->vht_mcs_nss[3] = 0;
+
+ STATIC_ASSERT(WL_RXS_VHTF_CODING_LDCP ==
+ IEEE80211_RADIOTAP_VHT_CODING_LDPC);
+
+ rtvht->vht_coding = rxsts->coding;
+ rtvht->vht_group_id = rxsts->gid;
+ rtvht->vht_partial_aid = HTOL16(rxsts->aid);
+
+ rtvht->ampdu_flags = 0;
+ rtvht->ampdu_delim_crc = 0;
+
+ rtvht->ampdu_ref_num = rxsts->ampdu_counter;
+
+ if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) &&
+ !(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB))
+ rtvht->ampdu_flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST;
+
+ if (rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_NONE)
+ rtvht->ampdu_flags |= IEEE80211_RADIOTAP_AMPDU_MPDU_ONLY;
+ }
+ else if (rxsts->encoding == WL_RXS_ENCODING_HT) {
+ wl_radiotap_ht_t *rtht =
+ (wl_radiotap_ht_t *)skb->data;
+
+ rtht->ieee_radiotap.it_version = 0;
+ rtht->ieee_radiotap.it_pad = 0;
+ rtht->ieee_radiotap.it_len = HTOL16(rtap_len);
+ rtht->ieee_radiotap.it_present
+ = HTOL32(WL_RADIOTAP_PRESENT_HT);
+ rtht->pad1 = 0;
+
+ rtht->tsft_l = htol32(rxsts->mactime);
+ rtht->tsft_h = 0;
+ rtht->flags = flags;
+ rtht->channel_freq = HTOL16(channel_frequency);
+ rtht->channel_flags = HTOL16(channel_flags);
+ rtht->signal = (int8)rxsts->signal;
+ rtht->noise = (int8)rxsts->noise;
+ rtht->antenna = rxsts->antenna;
+
+ rtht->mcs_known = (IEEE80211_RADIOTAP_MCS_HAVE_BW |
+ IEEE80211_RADIOTAP_MCS_HAVE_MCS |
+ IEEE80211_RADIOTAP_MCS_HAVE_GI |
+ IEEE80211_RADIOTAP_MCS_HAVE_FEC |
+ IEEE80211_RADIOTAP_MCS_HAVE_FMT);
+
+ rtht->mcs_flags = 0;
+ switch (rxsts->htflags & WL_RXS_HTF_BW_MASK) {
+ case WL_RXS_HTF_20L:
+ rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20L;
+ break;
+ case WL_RXS_HTF_20U:
+ rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20U;
+ break;
+ case WL_RXS_HTF_40:
+ rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_40;
+ break;
+ default:
+ rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20;
+ }
+
+ if (rxsts->htflags & WL_RXS_HTF_SGI) {
+ rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_SGI;
+ }
+ if (rxsts->preamble & WL_RXS_PREAMBLE_HT_GF) {
+ rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+ }
+ if (rxsts->htflags & WL_RXS_HTF_LDPC) {
+ rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
+ }
+ rtht->mcs_index = rxsts->mcs;
+ }
+
+ pdata = skb->data + rtap_len;
+ bcopy(oskb->data + D11_PHY_HDR_LEN, pdata, oskb->len - D11_PHY_HDR_LEN);
+
+ if (oskb->next) {
+ struct sk_buff *amsdu_p = oskb->next;
+ amsdu_p = oskb->next;
+ pdata += (oskb->len - D11_PHY_HDR_LEN);
+ while (amsdu_p) {
+ bcopy(amsdu_p->data, pdata, amsdu_p->len);
+ pdata += amsdu_p->len;
+ amsdu_p = amsdu_p->next;
+ }
+ }
+ }
+
+ if (skb == NULL) return;
+
+ skb->dev = wl->monitor_dev;
+ skb->dev->last_rx = jiffies;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ skb_reset_mac_header(skb);
+#else
+ skb->mac.raw = skb->data;
+#endif
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_80211_RAW);
+
+ netif_rx(skb);
+}
+
+static int
+wl_monitor_start(struct sk_buff *skb, struct net_device *dev)
+{
+ wl_info_t *wl;
+
+ wl = WL_DEV_IF(dev)->wl;
+ PKTFREE(wl->osh, skb, FALSE);
+ return 0;
+}
+
+static void
+_wl_add_monitor_if(wl_task_t *task)
+{
+ struct net_device *dev;
+ wl_if_t *wlif = (wl_if_t *) task->context;
+ wl_info_t *wl = wlif->wl;
+
+ WL_TRACE(("wl%d: %s\n", wl->pub->unit, __FUNCTION__));
+ ASSERT(wl);
+ ASSERT(!wl->monitor_dev);
+
+ if ((dev = wl_alloc_linux_if(wlif)) == NULL) {
+ WL_ERROR(("wl%d: %s: wl_alloc_linux_if failed\n", wl->pub->unit, __FUNCTION__));
+ goto done;
+ }
+
+ ASSERT(strlen(wlif->name) > 0);
+ strncpy(wlif->dev->name, wlif->name, strlen(wlif->name));
+
+ wl->monitor_dev = dev;
+ if (wl->monitor_type == 1)
+ dev->type = ARPHRD_IEEE80211_PRISM;
+ else
+ dev->type = ARPHRD_IEEE80211_RADIOTAP;
+
+ bcopy(wl->dev->dev_addr, dev->dev_addr, ETHER_ADDR_LEN);
+
+#if defined(WL_USE_NETDEV_OPS)
+ dev->netdev_ops = &wl_netdev_monitor_ops;
+#else
+ dev->hard_start_xmit = wl_monitor_start;
+ dev->do_ioctl = wl_ioctl;
+ dev->get_stats = wl_get_stats;
+#endif
+
+ if (register_netdev(dev)) {
+ WL_ERROR(("wl%d: %s, register_netdev failed for %s\n",
+ wl->pub->unit, __FUNCTION__, wl->monitor_dev->name));
+ wl->monitor_dev = NULL;
+ goto done;
+ }
+ wlif->dev_registed = TRUE;
+
+done:
+ MFREE(wl->osh, task, sizeof(wl_task_t));
+ atomic_dec(&wl->callbacks);
+}
+
+static void
+_wl_del_monitor(wl_task_t *task)
+{
+ wl_info_t *wl = (wl_info_t *) task->context;
+
+ ASSERT(wl);
+ ASSERT(wl->monitor_dev);
+
+ WL_TRACE(("wl%d: _wl_del_monitor\n", wl->pub->unit));
+
+ wl_free_if(wl, WL_DEV_IF(wl->monitor_dev));
+ wl->monitor_dev = NULL;
+
+ MFREE(wl->osh, task, sizeof(wl_task_t));
+ atomic_dec(&wl->callbacks);
+}
+
+void
+wl_set_monitor(wl_info_t *wl, int val)
+{
+ const char *devname;
+ wl_if_t *wlif;
+
+ WL_TRACE(("wl%d: wl_set_monitor: val %d\n", wl->pub->unit, val));
+ if ((val && wl->monitor_dev) || (!val && !wl->monitor_dev)) {
+ WL_ERROR(("%s: Mismatched params, return\n", __FUNCTION__));
+ return;
+ }
+
+ if (!val) {
+ (void) wl_schedule_task(wl, _wl_del_monitor, wl);
+ return;
+ }
+
+ if (val >= 1 && val <= 3) {
+ wl->monitor_type = val;
+ } else {
+ WL_ERROR(("monitor type %d not supported\n", val));
+ ASSERT(0);
+ }
+
+ wlif = wl_alloc_if(wl, WL_IFTYPE_MON, wl->pub->unit, NULL);
+ if (!wlif) {
+ WL_ERROR(("wl%d: %s: alloc wlif failed\n", wl->pub->unit, __FUNCTION__));
+ return;
+ }
+
+ if (wl->monitor_type == 1)
+ devname = "prism";
+ else
+ devname = "radiotap";
+ sprintf(wlif->name, "%s%d", devname, wl->pub->unit);
+
+ if (wl_schedule_task(wl, _wl_add_monitor_if, wlif)) {
+ MFREE(wl->osh, wlif, sizeof(wl_if_t));
+ return;
+ }
+}
+
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
+const char *
+print_tainted()
+{
+ return "";
+}
+#endif
+
+struct net_device *
+wl_netdev_get(wl_info_t *wl)
+{
+ return wl->dev;
+}
+
+int
+wl_set_pktlen(osl_t *osh, void *p, int len)
+{
+ PKTSETLEN(osh, p, len);
+ return len;
+}
+
+void *
+wl_get_pktbuffer(osl_t *osh, int len)
+{
+ return (PKTGET(osh, len, FALSE));
+}
+
+uint
+wl_buf_to_pktcopy(osl_t *osh, void *p, uchar *buf, int len, uint offset)
+{
+ if (PKTLEN(osh, p) < len + offset)
+ return 0;
+ bcopy(buf, (char *)PKTDATA(osh, p) + offset, len);
+ return len;
+}
+
+#if defined(WL_CONFIG_RFKILL)
+
+static int
+wl_set_radio_block(void *data, bool blocked)
+{
+ wl_info_t *wl = data;
+ uint32 radioval;
+
+ WL_TRACE(("%s: kernel set blocked = %d\n", __FUNCTION__, blocked));
+
+ radioval = WL_RADIO_SW_DISABLE << 16 | blocked;
+
+ WL_LOCK(wl);
+
+ if (wlc_set(wl->wlc, WLC_SET_RADIO, radioval) < 0) {
+ WL_ERROR(("%s: SET_RADIO failed\n", __FUNCTION__));
+ return 1;
+ }
+
+ WL_UNLOCK(wl);
+
+ return 0;
+}
+
+static const struct rfkill_ops bcmwl_rfkill_ops = {
+ .set_block = wl_set_radio_block
+};
+
+static int
+wl_init_rfkill(wl_info_t *wl)
+{
+ int status;
+
+ snprintf(wl->wl_rfkill.rfkill_name, sizeof(wl->wl_rfkill.rfkill_name),
+ "brcmwl-%d", wl->pub->unit);
+
+ wl->wl_rfkill.rfkill = rfkill_alloc(wl->wl_rfkill.rfkill_name, &wl->dev->dev,
+ RFKILL_TYPE_WLAN, &bcmwl_rfkill_ops, wl);
+
+ if (!wl->wl_rfkill.rfkill) {
+ WL_ERROR(("%s: RFKILL: Failed to allocate rfkill\n", __FUNCTION__));
+ return -ENOMEM;
+ }
+
+ if (wlc_get(wl->wlc, WLC_GET_RADIO, &status) < 0) {
+ WL_ERROR(("%s: WLC_GET_RADIO failed\n", __FUNCTION__));
+ return 1;
+ }
+
+ rfkill_init_sw_state(wl->wl_rfkill.rfkill, status);
+
+ if (rfkill_register(wl->wl_rfkill.rfkill)) {
+ WL_ERROR(("%s: rfkill_register failed! \n", __FUNCTION__));
+ rfkill_destroy(wl->wl_rfkill.rfkill);
+ return 2;
+ }
+
+ WL_ERROR(("%s: rfkill registered\n", __FUNCTION__));
+ wl->wl_rfkill.registered = TRUE;
+ return 0;
+}
+
+static void
+wl_uninit_rfkill(wl_info_t *wl)
+{
+ if (wl->wl_rfkill.registered) {
+ rfkill_unregister(wl->wl_rfkill.rfkill);
+ rfkill_destroy(wl->wl_rfkill.rfkill);
+ wl->wl_rfkill.registered = FALSE;
+ wl->wl_rfkill.rfkill = NULL;
+ }
+}
+
+static void
+wl_report_radio_state(wl_info_t *wl)
+{
+ WL_TRACE(("%s: report radio state %d\n", __FUNCTION__, wl->last_phyind));
+
+ rfkill_set_hw_state(wl->wl_rfkill.rfkill, wl->last_phyind != 0);
+}
+
+#endif
+
+static int
+wl_linux_watchdog(void *ctx)
+{
+ wl_info_t *wl = (wl_info_t *) ctx;
+ struct net_device_stats *stats = NULL;
+ uint id;
+ wl_if_t *wlif;
+ wlc_if_stats_t wlcif_stats;
+#ifdef USE_IW
+ struct iw_statistics *wstats = NULL;
+ int phy_noise;
+#endif
+ if (wl == NULL)
+ return -1;
+
+ if (wl->if_list) {
+ for (wlif = wl->if_list; wlif != NULL; wlif = wlif->next) {
+ memset(&wlcif_stats, 0, sizeof(wlc_if_stats_t));
+ wlc_wlcif_stats_get(wl->wlc, wlif->wlcif, &wlcif_stats);
+
+ if (wl->pub->up) {
+ ASSERT(wlif->stats_id < 2);
+
+ id = 1 - wlif->stats_id;
+ stats = &wlif->stats_watchdog[id];
+ if (stats) {
+ stats->rx_packets = WLCNTVAL(wlcif_stats.rxframe);
+ stats->tx_packets = WLCNTVAL(wlcif_stats.txframe);
+ stats->rx_bytes = WLCNTVAL(wlcif_stats.rxbyte);
+ stats->tx_bytes = WLCNTVAL(wlcif_stats.txbyte);
+ stats->rx_errors = WLCNTVAL(wlcif_stats.rxerror);
+ stats->tx_errors = WLCNTVAL(wlcif_stats.txerror);
+ stats->collisions = 0;
+ stats->rx_length_errors = 0;
+
+ stats->rx_over_errors = WLCNTVAL(wl->pub->_cnt->rxoflo);
+ stats->rx_crc_errors = WLCNTVAL(wl->pub->_cnt->rxcrc);
+ stats->rx_frame_errors = 0;
+ stats->rx_fifo_errors = WLCNTVAL(wl->pub->_cnt->rxoflo);
+ stats->rx_missed_errors = 0;
+ stats->tx_fifo_errors = 0;
+ }
+
+#ifdef USE_IW
+ wstats = &wlif->wstats_watchdog[id];
+ if (wstats) {
+#if WIRELESS_EXT > 11
+ wstats->discard.nwid = 0;
+ wstats->discard.code = WLCNTVAL(wl->pub->_cnt->rxundec);
+ wstats->discard.fragment = WLCNTVAL(wlcif_stats.rxfragerr);
+ wstats->discard.retries = WLCNTVAL(wlcif_stats.txfail);
+ wstats->discard.misc = WLCNTVAL(wl->pub->_cnt->rxrunt) +
+ WLCNTVAL(wl->pub->_cnt->rxgiant);
+ wstats->miss.beacon = 0;
+#endif
+ }
+#endif
+
+ wlif->stats_id = id;
+ }
+#ifdef USE_IW
+ if (!wlc_get(wl->wlc, WLC_GET_PHY_NOISE, &phy_noise))
+ wlif->phy_noise = phy_noise;
+#endif
+
+ }
+ }
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+static int
+wl_proc_read(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+ wl_info_t * wl = (wl_info_t *)data;
+#else
+static ssize_t
+wl_proc_read(struct file *filp, char __user *buffer, size_t length, loff_t *offp)
+{
+ wl_info_t * wl = PDE_DATA(file_inode(filp));
+#endif
+ int bcmerror, len;
+ int to_user = 0;
+ char tmp[8];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+ if (offset > 0) {
+ *eof = 1;
+ return 0;
+ }
+#else
+ if (*offp > 0) {
+ return 0;
+ }
+#endif
+
+ WL_LOCK(wl);
+ bcmerror = wlc_ioctl(wl->wlc, WLC_GET_MONITOR, &to_user, sizeof(int), NULL);
+ WL_UNLOCK(wl);
+
+ if (bcmerror != BCME_OK) {
+ WL_ERROR(("%s: GET_MONITOR failed with %d\n", __FUNCTION__, bcmerror));
+ return -EIO;
+ }
+
+ len = snprintf(tmp, ARRAY_SIZE(tmp), "%d\n", to_user);
+ tmp[ARRAY_SIZE(tmp) - 1] = '\0';
+ if ((len < 0) || (len >= ARRAY_SIZE(tmp))) {
+ WL_ERROR(("%s: tmp array not big enough %d > %zu", __FUNCTION__, len, ARRAY_SIZE(tmp)));
+ return -ERANGE;
+ }
+ if (length < len) {
+ WL_ERROR(( "%s: user buffer is too small (%d < %d)", __FUNCTION__, (int)length, len));
+ return -EMSGSIZE;
+ }
+ if (copy_to_user(buffer, tmp, len) != 0) {
+ WL_ERROR(( "%s: unable to copy data!", __FUNCTION__));
+ return -EFAULT;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ *offp += len;
+#endif
+
+ return len;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+static int
+wl_proc_write(struct file *filp, const char *buff, unsigned long length, void *data)
+{
+ wl_info_t * wl = (wl_info_t *)data;
+#else
+static ssize_t
+wl_proc_write(struct file *filp, const char __user *buff, size_t length, loff_t *offp)
+{
+ wl_info_t * wl = PDE_DATA(file_inode(filp));
+#endif
+ int from_user = 0;
+ int bcmerror;
+
+ if (length == 0 || length > 2) {
+
+ WL_ERROR(("%s: Invalid data length\n", __FUNCTION__));
+ return -EIO;
+ }
+ if (copy_from_user(&from_user, buff, 1)) {
+ WL_ERROR(("%s: copy from user failed\n", __FUNCTION__));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+ return -EIO;
+#else
+ return -EFAULT;
+#endif
+ }
+
+ if (from_user >= 0x30)
+ from_user -= 0x30;
+
+ WL_LOCK(wl);
+ bcmerror = wlc_ioctl(wl->wlc, WLC_SET_MONITOR, &from_user, sizeof(int), NULL);
+ WL_UNLOCK(wl);
+
+ if (bcmerror != BCME_OK) {
+ WL_ERROR(("%s: SET_MONITOR failed with %d\n", __FUNCTION__, bcmerror));
+ return -EIO;
+ }
+ return length;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+static const struct file_operations wl_fops = {
+ .owner = THIS_MODULE,
+ .read = wl_proc_read,
+ .write = wl_proc_write,
+};
+#endif
+
+static int
+wl_reg_proc_entry(wl_info_t *wl)
+{
+ char tmp[32];
+ sprintf(tmp, "%s%d", HYBRID_PROC, wl->pub->unit);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+ if ((wl->proc_entry = create_proc_entry(tmp, 0644, NULL)) == NULL) {
+ WL_ERROR(("%s: create_proc_entry %s failed\n", __FUNCTION__, tmp));
+#else
+ if ((wl->proc_entry = proc_create_data(tmp, 0644, NULL, &wl_fops, wl)) == NULL) {
+ WL_ERROR(("%s: proc_create_data %s failed\n", __FUNCTION__, tmp));
+#endif
+ ASSERT(0);
+ return -1;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+ wl->proc_entry->read_proc = wl_proc_read;
+ wl->proc_entry->write_proc = wl_proc_write;
+ wl->proc_entry->data = wl;
+#endif
+ return 0;
+}
+uint32 wl_pcie_bar1(struct wl_info *wl, uchar** addr)
+{
+ *addr = wl->bar1_addr;
+ return (wl->bar1_size);
+}
diff --git a/src/wl/sys/wl_linux.h b/src/wl/sys/wl_linux.h
new file mode 100644
index 0000000..5b1048e
--- /dev/null
+++ b/src/wl/sys/wl_linux.h
@@ -0,0 +1,192 @@
+/*
+ * wl_linux.c exported functions and definitions
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wl_linux.h 369548 2012-11-19 09:01:01Z $
+ */
+
+#ifndef _wl_linux_h_
+#define _wl_linux_h_
+
+#include <wlc_types.h>
+
+typedef struct wl_timer {
+ struct timer_list timer;
+ struct wl_info *wl;
+ void (*fn)(void *);
+ void *arg;
+ uint ms;
+ bool periodic;
+ bool set;
+ struct wl_timer *next;
+#ifdef BCMDBG
+ char* name;
+ uint32 ticks;
+#endif
+} wl_timer_t;
+
+typedef struct wl_task {
+ struct work_struct work;
+ void *context;
+} wl_task_t;
+
+#define WL_IFTYPE_BSS 1
+#define WL_IFTYPE_WDS 2
+#define WL_IFTYPE_MON 3
+
+struct wl_if {
+#ifdef USE_IW
+ wl_iw_t iw;
+#endif
+ struct wl_if *next;
+ struct wl_info *wl;
+ struct net_device *dev;
+ struct wlc_if *wlcif;
+ uint subunit;
+ bool dev_registed;
+ int if_type;
+ char name[IFNAMSIZ];
+ struct net_device_stats stats;
+ uint stats_id;
+ struct net_device_stats stats_watchdog[2];
+
+#ifdef USE_IW
+ struct iw_statistics wstats_watchdog[2];
+ struct iw_statistics wstats;
+ int phy_noise;
+#endif
+};
+
+struct rfkill_stuff {
+ struct rfkill *rfkill;
+ char rfkill_name[32];
+ char registered;
+};
+
+struct wl_info {
+ uint unit;
+ wlc_pub_t *pub;
+ void *wlc;
+ osl_t *osh;
+ struct net_device *dev;
+
+ struct semaphore sem;
+ spinlock_t lock;
+ spinlock_t isr_lock;
+
+ uint bcm_bustype;
+ bool piomode;
+ void *regsva;
+ wl_if_t *if_list;
+ atomic_t callbacks;
+ struct wl_timer *timers;
+ struct tasklet_struct tasklet;
+ struct tasklet_struct tx_tasklet;
+
+#if 0 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30))
+ struct napi_struct napi;
+#endif
+
+ struct net_device *monitor_dev;
+ uint monitor_type;
+ bool resched;
+ uint32 pci_psstate[16];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
+#define NUM_GROUP_KEYS 4
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+ struct lib80211_crypto_ops *tkipmodops;
+#else
+ struct ieee80211_crypto_ops *tkipmodops;
+#endif
+ struct ieee80211_tkip_data *tkip_ucast_data;
+ struct ieee80211_tkip_data *tkip_bcast_data[NUM_GROUP_KEYS];
+#endif
+
+ bool txq_dispatched;
+ spinlock_t txq_lock;
+ struct sk_buff *txq_head;
+ struct sk_buff *txq_tail;
+ int txq_cnt;
+
+ wl_task_t txq_task;
+ wl_task_t multicast_task;
+
+ wl_task_t wl_dpc_task;
+ bool all_dispatch_mode;
+
+#if defined(WL_CONFIG_RFKILL)
+ struct rfkill_stuff wl_rfkill;
+ mbool last_phyind;
+#endif
+
+ uint processed;
+ struct proc_dir_entry *proc_entry;
+ uchar* bar1_addr;
+ uint32 bar1_size;
+};
+
+#define HYBRID_PROC "brcm_monitor"
+
+#if defined(WL_ALL_PASSIVE_ON)
+#define WL_ALL_PASSIVE_ENAB(wl) 1
+#else
+#define WL_ALL_PASSIVE_ENAB(wl) (!(wl)->all_dispatch_mode)
+#endif
+
+#define WL_LOCK(wl) \
+do { \
+ if (WL_ALL_PASSIVE_ENAB(wl)) \
+ down(&(wl)->sem); \
+ else \
+ spin_lock_bh(&(wl)->lock); \
+} while (0)
+
+#define WL_UNLOCK(wl) \
+do { \
+ if (WL_ALL_PASSIVE_ENAB(wl)) \
+ up(&(wl)->sem); \
+ else \
+ spin_unlock_bh(&(wl)->lock); \
+} while (0)
+
+#define WL_ISRLOCK(wl, flags) do {spin_lock(&(wl)->isr_lock); (void)(flags);} while (0)
+#define WL_ISRUNLOCK(wl, flags) do {spin_unlock(&(wl)->isr_lock); (void)(flags);} while (0)
+
+#define INT_LOCK(wl, flags) spin_lock_irqsave(&(wl)->isr_lock, flags)
+#define INT_UNLOCK(wl, flags) spin_unlock_irqrestore(&(wl)->isr_lock, flags)
+
+typedef struct wl_info wl_info_t;
+
+#ifndef PCI_D0
+#define PCI_D0 0
+#endif
+
+#ifndef PCI_D3hot
+#define PCI_D3hot 3
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+extern irqreturn_t wl_isr(int irq, void *dev_id);
+#else
+extern irqreturn_t wl_isr(int irq, void *dev_id, struct pt_regs *ptregs);
+#endif
+
+extern int __devinit wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+extern void wl_free(wl_info_t *wl);
+extern int wl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+extern struct net_device * wl_netdev_get(wl_info_t *wl);
+
+#endif
diff --git a/src/wl/sys/wlc_ethereal.h b/src/wl/sys/wlc_ethereal.h
new file mode 100644
index 0000000..2f9e128
--- /dev/null
+++ b/src/wl/sys/wlc_ethereal.h
@@ -0,0 +1,129 @@
+/*
+ * Structures and defines for the prism-style rx header that Ethereal
+ * understands.
+ * Broadcom 802.11abg Networking Device Driver
+ * Derived from http://airsnort.shmoo.com/orinoco-09b-packet-1.diff
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wlc_ethereal.h 328348 2012-04-18 22:57:38Z $
+ */
+
+#ifndef _WLC_ETHEREAL_H_
+#define _WLC_ETHEREAL_H_
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
+#endif
+
+#ifndef ARPHRD_ETHER
+#define ARPHRD_ETHER 1
+#endif
+
+#ifndef ARPHRD_IEEE80211_PRISM
+#define ARPHRD_IEEE80211_PRISM 802
+#endif
+
+#define DNAMELEN 16
+
+#define WL_MON_FRAME 0x0041
+#define WL_MON_FRAME_HOSTTIME 0x1041
+#define WL_MON_FRAME_MACTIME 0x2041
+#define WL_MON_FRAME_CHANNEL 0x3041
+#define WL_MON_FRAME_RSSI 0x4041
+#define WL_MON_FRAME_SQ 0x5041
+#define WL_MON_FRAME_SIGNAL 0x6041
+#define WL_MON_FRAME_NOISE 0x7041
+#define WL_MON_FRAME_RATE 0x8041
+#define WL_MON_FRAME_ISTX 0x9041
+#define WL_MON_FRAME_FRMLEN 0xA041
+
+#define P80211ITEM_OK 0
+#define P80211ITEM_NO_VALUE 1
+
+typedef struct p80211item
+{
+ uint32 did;
+ uint16 status;
+ uint16 len;
+ uint32 data;
+} p80211item_t;
+
+typedef struct p80211msg
+{
+ uint32 msgcode;
+ uint32 msglen;
+ uint8 devname[DNAMELEN];
+ p80211item_t hosttime;
+ p80211item_t mactime;
+ p80211item_t channel;
+ p80211item_t rssi;
+ p80211item_t sq;
+ p80211item_t signal;
+ p80211item_t noise;
+ p80211item_t rate;
+ p80211item_t istx;
+ p80211item_t frmlen;
+} p80211msg_t;
+
+#define WLANCAP_MAGIC_COOKIE_V1 0x80211001
+
+#define WLANCAP_PHY_UNKOWN 0
+#define WLANCAP_PHY_FHSS_97 1
+#define WLANCAP_PHY_DSSS_97 2
+#define WLANCAP_PHY_IR 3
+#define WLANCAP_PHY_DSSS_11B 4
+#define WLANCAP_PHY_PBCC_11B 5
+#define WLANCAP_PHY_OFDM_11G 6
+#define WLANCAP_PHY_PBCC_11G 7
+#define WLANCAP_PHY_OFDM_11A 8
+#define WLANCAP_PHY_OFDM_11N 9
+
+#define WLANCAP_ENCODING_UNKNOWN 0
+#define WLANCAP_ENCODING_CCK 1
+#define WLANCAP_ENCODING_PBCC 2
+#define WLANCAP_ENCODING_OFDM 3
+
+#define WLANCAP_SSI_TYPE_NONE 0
+#define WLANCAP_SSI_TYPE_NORM 1
+#define WLANCAP_SSI_TYPE_DBM 2
+#define WLANCAP_SSI_TYPE_RAW 3
+
+#define WLANCAP_PREAMBLE_UNKNOWN 0
+#define WLANCAP_PREAMBLE_SHORT 1
+#define WLANCAP_PREAMBLE_LONG 2
+#define WLANCAP_PREAMBLE_MIMO_MM 3
+#define WLANCAP_PREAMBLE_MIMO_GF 4
+
+typedef struct wlan_header_v1 {
+ uint32 version;
+ uint32 length;
+ uint32 mactime_h;
+ uint32 mactime_l;
+ uint32 hosttime_h;
+ uint32 hosttime_l;
+ uint32 phytype;
+ uint32 channel;
+ uint32 datarate;
+ uint32 antenna;
+ uint32 priority;
+ uint32 ssi_type;
+ int32 ssi_signal;
+ int32 ssi_noise;
+ uint32 preamble;
+ uint32 encoding;
+} wlan_header_v1_t;
+
+#endif
diff --git a/src/wl/sys/wlc_key.h b/src/wl/sys/wlc_key.h
new file mode 100644
index 0000000..dfe99df
--- /dev/null
+++ b/src/wl/sys/wlc_key.h
@@ -0,0 +1,76 @@
+/*
+ * Key management related declarations
+ * and exported functions for
+ * Broadcom 802.11abg Networking Device Driver
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wlc_key.h 458427 2014-02-26 23:12:38Z $
+ */
+
+#ifndef _wlc_key_h_
+#define _wlc_key_h_
+
+#include <bcmcrypto/tkhash.h>
+
+typedef struct tkip_info {
+ uint16 phase1[TKHASH_P1_KEY_SIZE/sizeof(uint16)];
+ uint8 phase2[TKHASH_P2_KEY_SIZE];
+ uint32 micl;
+ uint32 micr;
+} tkip_info_t;
+
+typedef struct wsec_iv {
+ uint32 hi;
+ uint16 lo;
+} wsec_iv_t;
+
+#define WLC_NUMRXIVS 4
+
+#define TWSIZE 128
+
+typedef struct wsec_key {
+ struct ether_addr ea;
+ uint8 idx;
+ uint8 id;
+ uint8 algo;
+ uint8 rcmta;
+ uint16 flags;
+ uint8 algo_hw;
+ uint8 aes_mode;
+ int8 iv_len;
+ int8 icv_len;
+ uint32 len;
+
+ uint8 data[DOT11_MAX_KEY_SIZE];
+ wsec_iv_t rxiv[WLC_NUMRXIVS];
+ wsec_iv_t txiv;
+ tkip_info_t tkip_tx;
+ tkip_info_t tkip_rx;
+ uint32 tkip_rx_iv32;
+ uint8 tkip_rx_ividx;
+ uint8 tkip_tx_lefts;
+ uint8 tkip_tx_left[4];
+ uint16 tkip_tx_offset;
+ uint8 tkip_tx_fmic[8];
+ int tkip_tx_fmic_written;
+
+#if defined(UCODE_SEQ)
+ wsec_iv_t bk_iv;
+ tkip_info_t tkip_bk_tx;
+#endif
+} wsec_key_t;
+
+#endif
diff --git a/src/wl/sys/wlc_pub.h b/src/wl/sys/wlc_pub.h
new file mode 100644
index 0000000..53a98b8
--- /dev/null
+++ b/src/wl/sys/wlc_pub.h
@@ -0,0 +1,927 @@
+/*
+ * Common (OS-independent) definitions for
+ * Broadcom 802.11abg Networking Device Driver
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wlc_pub.h 458427 2014-02-26 23:12:38Z $
+ */
+
+#ifndef _wlc_pub_h_
+#define _wlc_pub_h_
+
+#include <wlc_types.h>
+#include <wlc_utils.h>
+#include "proto/802.11.h"
+#include "proto/bcmevent.h"
+
+#define MAX_TIMERS (34 + WLC_MAXMFPS + WLC_MAXDLS_TIMERS + (2 * WLC_MAXDPT))
+
+#define WLC_NUMRATES 16
+#define MAXMULTILIST 32
+#define D11_PHY_HDR_LEN 6
+
+#define PHY_TYPE_A 0
+#define PHY_TYPE_G 2
+#define PHY_TYPE_N 4
+#define PHY_TYPE_LP 5
+#define PHY_TYPE_SSN 6
+#define PHY_TYPE_HT 7
+#define PHY_TYPE_LCN 8
+#define PHY_TYPE_LCNXN 9
+
+#define WLC_10_MHZ 10
+#define WLC_20_MHZ 20
+#define WLC_40_MHZ 40
+#define WLC_80_MHZ 80
+#define WLC_160_MHZ 160
+
+#define CHSPEC_WLC_BW(chanspec)(CHSPEC_IS160(chanspec) ? WLC_160_MHZ : \
+ CHSPEC_IS80(chanspec) ? WLC_80_MHZ : \
+ CHSPEC_IS40(chanspec) ? WLC_40_MHZ : \
+ CHSPEC_IS20(chanspec) ? WLC_20_MHZ : \
+ WLC_10_MHZ)
+
+#define WLC_RSSI_MINVAL -200
+#define WLC_RSSI_NO_SIGNAL -91
+#define WLC_RSSI_VERY_LOW -80
+#define WLC_RSSI_LOW -70
+#define WLC_RSSI_GOOD -68
+#define WLC_RSSI_VERY_GOOD -58
+#define WLC_RSSI_EXCELLENT -57
+
+#define PREFSZ 160
+#define WLPREFHDRS(h, sz) OSL_PREF_RANGE_ST((h), (sz))
+
+struct wlc_info;
+struct wlc_hw_info;
+struct wlc_bsscfg;
+struct wlc_if;
+
+typedef struct wlc_tunables {
+ int ntxd;
+ int nrxd;
+ int rxbufsz;
+ int nrxbufpost;
+ int maxscb;
+ int ampdunummpdu2streams;
+ int ampdunummpdu3streams;
+ int maxpktcb;
+ int maxdpt;
+ int maxucodebss;
+ int maxucodebss4;
+ int maxbss;
+ int datahiwat;
+ int ampdudatahiwat;
+ int rxbnd;
+ int txsbnd;
+ int pktcbnd;
+ int dngl_mem_restrict_rxdma;
+ int rpctxbufpost;
+ int pkt_maxsegs;
+ int maxscbcubbies;
+ int maxbsscfgcubbies;
+ int max_notif_servers;
+ int max_notif_clients;
+ int max_mempools;
+ int maxtdls;
+ int amsdu_resize_buflen;
+ int ampdu_pktq_size;
+ int ampdu_pktq_fav_size;
+ int ntxd_large;
+ int nrxd_large;
+ int wlfcfifocreditac0;
+ int wlfcfifocreditac1;
+ int wlfcfifocreditac2;
+ int wlfcfifocreditac3;
+ int wlfcfifocreditbcmc;
+ int wlfcfifocreditother;
+ int scan_settle_time;
+ int wlfc_fifo_cr_pending_thresh_ac_bk;
+ int wlfc_fifo_cr_pending_thresh_ac_be;
+ int wlfc_fifo_cr_pending_thresh_ac_vi;
+ int wlfc_fifo_cr_pending_thresh_ac_vo;
+ int ampdunummpdu1stream;
+} wlc_tunables_t;
+
+typedef struct wlc_rateset {
+ uint count;
+ uint8 rates[WLC_NUMRATES];
+ uint8 htphy_membership;
+ uint8 mcs[MCSSET_LEN];
+ uint16 vht_mcsmap;
+} wlc_rateset_t;
+
+typedef void *wlc_pkt_t;
+
+typedef struct wlc_event {
+ wl_event_msg_t event;
+ struct ether_addr *addr;
+ struct wlc_if *wlcif;
+ void *data;
+ struct wlc_event *next;
+} wlc_event_t;
+
+typedef struct wlc_bss_info
+{
+ struct ether_addr BSSID;
+ uint16 flags;
+ uint8 SSID_len;
+ uint8 SSID[32];
+ int16 RSSI;
+ int16 SNR;
+ uint16 beacon_period;
+ uint16 atim_window;
+ chanspec_t chanspec;
+ int8 infra;
+ wlc_rateset_t rateset;
+ uint8 dtim_period;
+ int8 phy_noise;
+ uint16 capability;
+ struct dot11_bcn_prb *bcn_prb;
+ uint16 bcn_prb_len;
+ uint8 wme_qosinfo;
+ struct rsn_parms wpa;
+ struct rsn_parms wpa2;
+ uint16 qbss_load_aac;
+
+ uint8 qbss_load_chan_free;
+ uint8 mcipher;
+ uint8 wpacfg;
+ uint16 mdid;
+ uint16 flags2;
+ uint32 vht_capabilities;
+ uint16 vht_rxmcsmap;
+ uint16 vht_txmcsmap;
+} wlc_bss_info_t;
+
+#define WLC_BSS_54G 0x0001
+#define WLC_BSS_RSSI_ON_CHANNEL 0x0002
+#define WLC_BSS_WME 0x0004
+#define WLC_BSS_BRCM 0x0008
+#define WLC_BSS_WPA 0x0010
+#define WLC_BSS_HT 0x0020
+#define WLC_BSS_40MHZ 0x0040
+#define WLC_BSS_WPA2 0x0080
+#define WLC_BSS_BEACON 0x0100
+#define WLC_BSS_40INTOL 0x0200
+#define WLC_BSS_SGI_20 0x0400
+#define WLC_BSS_SGI_40 0x0800
+#define WLC_BSS_CACHE 0x2000
+#define WLC_BSS_FBT 0x8000
+
+#define WLC_BSS_OVERDS_FBT 0x0001
+#define WLC_BSS_VHT 0x0002
+#define WLC_BSS_80MHZ 0x0004
+#define WLC_BSS_SGI_80 0x0008
+
+#define WLC_ENOIOCTL 1
+#define WLC_EINVAL 2
+#define WLC_ETOOSMALL 3
+#define WLC_ETOOBIG 4
+#define WLC_ERANGE 5
+#define WLC_EDOWN 6
+#define WLC_EUP 7
+#define WLC_ENOMEM 8
+#define WLC_EBUSY 9
+
+#define IOVF_BSSCFG_STA_ONLY (1<<0)
+#define IOVF_BSSCFG_AP_ONLY (1<<1)
+#define IOVF_BSS_SET_DOWN (1<<2)
+
+#define IOVF_MFG (1<<3)
+#define IOVF_WHL (1<<4)
+#define IOVF_NTRL (1<<5)
+
+#define IOVF_SET_UP (1<<6)
+#define IOVF_SET_DOWN (1<<7)
+#define IOVF_SET_CLK (1<<8)
+#define IOVF_SET_BAND (1<<9)
+
+#define IOVF_GET_UP (1<<10)
+#define IOVF_GET_DOWN (1<<11)
+#define IOVF_GET_CLK (1<<12)
+#define IOVF_GET_BAND (1<<13)
+#define IOVF_OPEN_ALLOW (1<<14)
+
+#define IOVF_BMAC_IOVAR (1<<15)
+
+#define BAR0_INVALID (1 << 0)
+#define VENDORID_INVALID (1 << 1)
+#define NOCARD_PRESENT (1 << 2)
+#define PHY_PLL_ERROR (1 << 3)
+#define DEADCHIP_ERROR (1 << 4)
+#define MACSPEND_TIMOUT (1 << 5)
+#define MACSPEND_WOWL_TIMOUT (1 << 6)
+#define DMATX_ERROR (1 << 7)
+#define DMARX_ERROR (1 << 8)
+#define DESCRIPTOR_ERROR (1 << 9)
+#define CARD_NOT_POWERED (1 << 10)
+
+#define WL_HEALTH_LOG(w, s) do {} while (0)
+
+typedef int (*watchdog_fn_t)(void *handle);
+typedef int (*up_fn_t)(void *handle);
+typedef int (*down_fn_t)(void *handle);
+typedef int (*dump_fn_t)(void *handle, struct bcmstrbuf *b);
+
+typedef int (*iovar_fn_t)(void *handle, const bcm_iovar_t *vi, uint32 actionid,
+ const char *name, void *params, uint plen, void *arg, int alen,
+ int vsize, struct wlc_if *wlcif);
+
+#define WLC_IOCF_BSSCFG_STA_ONLY (1<<0)
+#define WLC_IOCF_BSSCFG_AP_ONLY (1<<1)
+
+#define WLC_IOCF_MFG (1<<2)
+
+#define WLC_IOCF_DRIVER_UP (1<<3)
+#define WLC_IOCF_DRIVER_DOWN (1<<4)
+#define WLC_IOCF_CORE_CLK (1<<5)
+#define WLC_IOCF_FIXED_BAND (1<<6)
+#define WLC_IOCF_OPEN_ALLOW (1<<7)
+
+typedef int (*wlc_ioctl_fn_t)(void *handle, int cmd, void *arg, int len, struct wlc_if *wlcif);
+
+typedef struct wlc_ioctl_cmd_s {
+ uint16 cmd;
+ uint16 flags;
+ int min_len;
+} wlc_ioctl_cmd_t;
+
+typedef struct wlc_pub {
+ void *wlc;
+ struct ether_addr cur_etheraddr;
+ uint unit;
+ uint corerev;
+ osl_t *osh;
+ si_t *sih_obsolete;
+ char *vars_obsolete;
+ uint vars_size_obsolete;
+ bool up;
+ bool hw_off;
+ wlc_tunables_t *tunables;
+ bool hw_up;
+ bool _piomode;
+ uint _nbands;
+ uint now;
+
+ bool promisc;
+ bool delayed_down;
+ bool _ap;
+ bool _apsta;
+ bool _assoc_recreate;
+ int _wme;
+ uint8 _mbss;
+ bool associated;
+
+ bool phytest_on;
+ bool bf_preempt_4306;
+
+ bool _wowl;
+ bool _wowl_active;
+ bool _ampdu_tx;
+ bool _ampdu_rx;
+ bool _amsdu_tx;
+ bool _cac;
+ uint _spect_management;
+ uint8 _n_enab;
+ bool _n_reqd;
+
+ uint8 _vht_enab;
+
+ int8 _coex;
+ bool _priofc;
+ bool phy_bw40_capable;
+ bool phy_bw80_capable;
+
+ uint32 wlfeatureflag;
+ int psq_pkts_total;
+
+ uint16 txmaxpkts;
+
+ uint32 swdecrypt;
+
+ int bcmerror;
+
+ mbool radio_disabled;
+ mbool last_radio_disabled;
+ bool radio_active;
+ uint16 roam_time_thresh;
+ bool align_wd_tbtt;
+ uint16 boardrev;
+ uint8 sromrev;
+ uint32 boardflags;
+ uint32 boardflags2;
+
+ wl_cnt_t *_cnt;
+ wl_wme_cnt_t *_wme_cnt;
+
+ uint8 _ndis_cap;
+ bool _extsta;
+ bool _pkt_filter;
+ bool phy_11ncapable;
+ bool _fbt;
+ pktpool_t *pktpool;
+ uint8 _ampdumac;
+ bool _wleind;
+ bool _sup_enab;
+ uint driverrev;
+
+ bool _11h;
+ bool _11d;
+#ifdef WLCNTRY
+ bool _autocountry;
+#endif
+ uint32 health;
+ uint8 d11tpl_phy_hdr_len;
+ uint wsec_max_rcmta_keys;
+ uint max_addrma_idx;
+ uint16 m_seckindxalgo_blk;
+ uint m_seckindxalgo_blk_sz;
+ uint16 m_coremask_blk;
+ uint16 m_coremask_blk_wowl;
+#ifdef WL_BEAMFORMING
+ bool _txbf;
+#endif
+
+ bool wet_tunnel;
+ int _ol;
+
+ uint16 vht_features;
+
+ bool _ampdu_hostreorder;
+
+ int8 _pktc;
+ bool _tdls_support;
+ bool _okc;
+ bool _p2po;
+ bool _anqpo;
+ bool _wl_rxearlyrc;
+ bool _tiny_pktjoin;
+ si_t *sih;
+ char *vars;
+ uint vars_size;
+ bool _proxd;
+
+ bool _arpoe_support;
+ bool _11u;
+
+ bool _lpc_algo;
+ bool _relmcast;
+ bool _relmcast_support;
+
+ bool _l2_filter;
+
+ uint bcn_tmpl_len;
+#ifdef WL_OFFLOADSTATS
+ uint32 offld_cnt_received[4];
+ uint32 offld_cnt_consumed[4];
+#endif
+#ifdef WL_INTERRUPTSTATS
+ uint32 intr_cnt[32];
+#endif
+
+#ifdef TCPKAOE
+ bool _icmpoe;
+ bool _tcp_keepalive;
+#endif
+ bool _olpc;
+} wlc_pub_t;
+
+typedef struct wl_rxsts {
+ uint pkterror;
+ uint phytype;
+ chanspec_t chanspec;
+ uint16 datarate;
+ uint8 mcs;
+ uint8 htflags;
+ uint antenna;
+ uint pktlength;
+ uint32 mactime;
+ uint sq;
+ int32 signal;
+ int32 noise;
+ uint preamble;
+ uint encoding;
+ uint nfrmtype;
+ struct wl_if *wlif;
+ uint8 nss;
+ uint8 coding;
+ uint16 aid;
+ uint8 gid;
+ uint8 bw;
+ uint16 vhtflags;
+ uint8 bw_nonht;
+ uint32 ampdu_counter;
+} wl_rxsts_t;
+
+typedef struct wl_txsts {
+ uint pkterror;
+ uint phytype;
+ chanspec_t chanspec;
+ uint16 datarate;
+ uint8 mcs;
+ uint8 htflags;
+ uint antenna;
+ uint pktlength;
+ uint32 mactime;
+ uint preamble;
+ uint encoding;
+ uint nfrmtype;
+ uint txflags;
+ uint retries;
+ struct wl_if *wlif;
+} wl_txsts_t;
+
+typedef struct wlc_if_stats {
+
+ uint32 txframe;
+ uint32 txbyte;
+ uint32 txerror;
+ uint32 txnobuf;
+ uint32 txrunt;
+ uint32 txfail;
+
+ uint32 rxframe;
+ uint32 rxbyte;
+ uint32 rxerror;
+ uint32 rxnobuf;
+ uint32 rxrunt;
+ uint32 rxfragerr;
+
+ uint32 txretry;
+ uint32 txretrie;
+ uint32 txfrmsnt;
+ uint32 txmulti;
+ uint32 txfrag;
+
+ uint32 rxmulti;
+
+} wlc_if_stats_t;
+
+#define WL_RXS_CRC_ERROR 0x00000001
+#define WL_RXS_RUNT_ERROR 0x00000002
+#define WL_RXS_ALIGN_ERROR 0x00000004
+#define WL_RXS_OVERSIZE_ERROR 0x00000008
+#define WL_RXS_WEP_ICV_ERROR 0x00000010
+#define WL_RXS_WEP_ENCRYPTED 0x00000020
+#define WL_RXS_PLCP_SHORT 0x00000040
+#define WL_RXS_DECRYPT_ERR 0x00000080
+#define WL_RXS_OTHER_ERR 0x80000000
+
+#define WL_RXS_PHY_A 0x00000000
+#define WL_RXS_PHY_B 0x00000001
+#define WL_RXS_PHY_G 0x00000002
+#define WL_RXS_PHY_N 0x00000004
+
+#define WL_RXS_ENCODING_UNKNOWN 0x00000000
+#define WL_RXS_ENCODING_DSSS_CCK 0x00000001
+#define WL_RXS_ENCODING_OFDM 0x00000002
+#define WL_RXS_ENCODING_HT 0x00000003
+#define WL_RXS_ENCODING_VHT 0x00000004
+
+#define WL_RXS_UNUSED_STUB 0x0
+#define WL_RXS_PREAMBLE_SHORT 0x00000001
+#define WL_RXS_PREAMBLE_LONG 0x00000002
+#define WL_RXS_PREAMBLE_HT_MM 0x00000003
+#define WL_RXS_PREAMBLE_HT_GF 0x00000004
+
+#define WL_RXS_HTF_BW_MASK 0x07
+#define WL_RXS_HTF_40 0x01
+#define WL_RXS_HTF_20L 0x02
+#define WL_RXS_HTF_20U 0x04
+#define WL_RXS_HTF_SGI 0x08
+#define WL_RXS_HTF_STBC_MASK 0x30
+#define WL_RXS_HTF_STBC_SHIFT 4
+#define WL_RXS_HTF_LDPC 0x40
+
+#define WL_RXS_VHTF_STBC 0x01
+#define WL_RXS_VHTF_TXOP_PS 0x02
+#define WL_RXS_VHTF_SGI 0x04
+#define WL_RXS_VHTF_SGI_NSYM_DA 0x08
+#define WL_RXS_VHTF_LDPC_EXTRA 0x10
+#define WL_RXS_VHTF_BF 0x20
+#define WL_RXS_VHTF_DYN_BW_NONHT 0x40
+
+#define WL_RXS_VHTF_CODING_LDCP 0x01
+
+#define WL_RXS_VHT_BW_20 0
+#define WL_RXS_VHT_BW_40 1
+#define WL_RXS_VHT_BW_20L 2
+#define WL_RXS_VHT_BW_20U 3
+#define WL_RXS_VHT_BW_80 4
+#define WL_RXS_VHT_BW_40L 5
+#define WL_RXS_VHT_BW_40U 6
+#define WL_RXS_VHT_BW_20LL 7
+#define WL_RXS_VHT_BW_20LU 8
+#define WL_RXS_VHT_BW_20UL 9
+#define WL_RXS_VHT_BW_20UU 10
+
+#define WL_RXS_NFRM_AMPDU_FIRST 0x00000001
+#define WL_RXS_NFRM_AMPDU_SUB 0x00000002
+#define WL_RXS_NFRM_AMSDU_FIRST 0x00000004
+#define WL_RXS_NFRM_AMSDU_SUB 0x00000008
+#define WL_RXS_NFRM_AMPDU_NONE 0x00000100
+
+#define WL_TXS_TXF_FAIL 0x01
+#define WL_TXS_TXF_CTS 0x02
+#define WL_TXS_TXF_RTSCTS 0x04
+
+#define BPRESET_ENAB(pub) (0)
+
+#define AP_ENAB(pub) (0)
+
+#define APSTA_ENAB(pub) (0)
+
+#define PSTA_ENAB(pub) (0)
+
+#if defined(PKTC_DONGLE)
+#define PKTC_ENAB(pub) ((pub)->_pktc)
+#else
+#define PKTC_ENAB(pub) (0)
+#endif
+
+#if defined(WL_BEAMFORMING)
+ #if defined(WL_ENAB_RUNTIME_CHECK) || !defined(DONGLEBUILD)
+ #define TXBF_ENAB(pub) ((pub)->_txbf)
+ #elif defined(WLTXBF_DISABLED)
+ #define TXBF_ENAB(pub) (0)
+ #else
+ #define TXBF_ENAB(pub) (1)
+ #endif
+#else
+ #define TXBF_ENAB(pub) (0)
+#endif
+
+#define STA_ONLY(pub) (!AP_ENAB(pub))
+#define AP_ONLY(pub) (AP_ENAB(pub) && !APSTA_ENAB(pub))
+
+ #define PROP_TXSTATUS_ENAB(pub) 0
+
+#define WLOFFLD_CAP(wlc) ((wlc)->ol != NULL)
+#define WLOFFLD_ENAB(pub) ((pub)->_ol)
+#define WLOFFLD_BCN_ENAB(pub) ((pub)->_ol & OL_BCN_ENAB)
+#define WLOFFLD_ARP_ENAB(pub) ((pub)->_ol & OL_ARP_ENAB)
+#define WLOFFLD_ND_ENAB(pub) ((pub)->_ol & OL_ND_ENAB)
+#define WLOFFLD_ARM_TX(pub) ((pub)->_ol & OL_ARM_TX_ENAB)
+
+#define WOWL_ENAB(pub) ((pub)->_wowl)
+#define WOWL_ACTIVE(pub) ((pub)->_wowl_active)
+
+ #define DPT_ENAB(pub) 0
+
+ #define TDLS_SUPPORT(pub) (0)
+ #define TDLS_ENAB(pub) (0)
+
+#define WLDLS_ENAB(pub) 0
+
+#ifdef WL_OKC
+ #if defined(WL_ENAB_RUNTIME_CHECK)
+#define OKC_ENAB(pub) ((pub)->_okc)
+ #elif defined(WL_OKC_DISABLED)
+ #define OKC_ENAB(pub) (0)
+#else
+ #define OKC_ENAB(pub) ((pub)->_okc)
+#endif
+#else
+ #define OKC_ENAB(pub) (0)
+#endif
+
+#define WLBSSLOAD_ENAB(pub) (0)
+
+ #define MCNX_ENAB(pub) 0
+
+ #define P2P_ENAB(pub) 0
+
+ #define MCHAN_ENAB(pub) (0)
+ #define MCHAN_ACTIVE(pub) (0)
+
+ #define MQUEUE_ENAB(pub) (0)
+
+ #define BTA_ENAB(pub) (0)
+
+#define PIO_ENAB(pub) 0
+
+#define CAC_ENAB(pub) ((pub)->_cac)
+
+#define COEX_ACTIVE(wlc) 0
+#define COEX_ENAB(pub) 0
+
+#define RXIQEST_ENAB(pub) (0)
+
+#define EDCF_ENAB(pub) (WME_ENAB(pub))
+#define QOS_ENAB(pub) (WME_ENAB(pub) || N_ENAB(pub))
+
+#define PRIOFC_ENAB(pub) ((pub)->_priofc)
+
+#define MONITOR_ENAB(wlc) ((wlc)->monitor != 0)
+
+#define PROMISC_ENAB(wlc_pub) (wlc_pub)->promisc
+
+#define WLC_SENDUP_MGMT_ENAB(cfg) 0
+
+ #define TOE_ENAB(pub) (0)
+
+ #define ARPOE_SUPPORT(pub) (0)
+ #define ARPOE_ENAB(pub) (0)
+#define ICMPOE_ENAB(pub) 0
+
+ #define NWOE_ENAB(pub) (0)
+
+#define TRAFFIC_MGMT_ENAB(pub) 0
+
+ #define L2_FILTER_ENAB(pub) (0)
+
+#define NET_DETECT_ENAB(pub) 0
+
+#ifdef PACKET_FILTER
+#define PKT_FILTER_ENAB(pub) ((pub)->_pkt_filter)
+#else
+#define PKT_FILTER_ENAB(pub) 0
+#endif
+
+#ifdef P2PO
+ #if defined(WL_ENAB_RUNTIME_CHECK) || !defined(DONGLEBUILD)
+ #define P2PO_ENAB(pub) ((pub)->_p2po)
+ #elif defined(P2PO_DISABLED)
+ #define P2PO_ENAB(pub) (0)
+ #else
+ #define P2PO_ENAB(pub) (1)
+ #endif
+#else
+ #define P2PO_ENAB(pub) 0
+#endif
+
+#ifdef ANQPO
+ #if defined(WL_ENAB_RUNTIME_CHECK) || !defined(DONGLEBUILD)
+ #define ANQPO_ENAB(pub) ((pub)->_anqpo)
+ #elif defined(ANQPO_DISABLED)
+ #define ANQPO_ENAB(pub) (0)
+ #else
+ #define ANQPO_ENAB(pub) (1)
+ #endif
+#else
+ #define ANQPO_ENAB(pub) 0
+#endif
+
+#define ASSOC_RECREATE_ENAB(pub) 0
+
+#define WLFBT_ENAB(pub) (0)
+
+#if 0 && (NDISVER >= 0x0620)
+#define WIN7_AND_UP_OS(pub) ((pub)->_ndis_cap)
+#else
+#define WIN7_AND_UP_OS(pub) 0
+#endif
+
+ #define NDOE_ENAB(pub) (0)
+
+ #define WLEXTSTA_ENAB(pub) 0
+
+ #define IBSS_PEER_GROUP_KEY_ENAB(pub) (0)
+
+ #define IBSS_PEER_DISCOVERY_EVENT_ENAB(pub) (0)
+
+ #define IBSS_PEER_MGMT_ENAB(pub) (0)
+
+ #if defined(WL_ENAB_RUNTIME_CHECK) || !defined(DONGLEBUILD)
+ #define WLEIND_ENAB(pub) ((pub)->_wleind)
+ #elif defined(WLEIND_DISABLED)
+ #define WLEIND_ENAB(pub) (0)
+ #else
+ #define WLEIND_ENAB(pub) (1)
+ #endif
+
+ #define CCX_ENAB(pub) 0
+
+ #define BCMAUTH_PSK_ENAB(pub) 0
+
+ #if defined(WL_ENAB_RUNTIME_CHECK) || !defined(DONGLEBUILD)
+ #define SUP_ENAB(pub) ((pub)->_sup_enab)
+ #elif defined(BCMSUP_PSK_DISABLED)
+ #define SUP_ENAB(pub) (0)
+ #else
+ #define SUP_ENAB(pub) (1)
+ #endif
+
+#define WLC_PREC_BMP_ALL MAXBITVAL(WLC_PREC_COUNT)
+
+#define WLC_PREC_BMP_AC_BE (NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_BE)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_BE)) | \
+ NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_EE)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_EE)))
+#define WLC_PREC_BMP_AC_BK (NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_BK)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_BK)) | \
+ NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_NONE)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_NONE)))
+#define WLC_PREC_BMP_AC_VI (NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_CL)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_CL)) | \
+ NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_VI)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_VI)))
+#define WLC_PREC_BMP_AC_VO (NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_VO)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_VO)) | \
+ NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_NC)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_NC)))
+
+#define WME_ENAB(pub) ((pub)->_wme != OFF)
+#define WME_AUTO(wlc) ((wlc)->pub->_wme == AUTO)
+
+#ifdef WLCNTRY
+#define WLC_AUTOCOUNTRY_ENAB(wlc) ((wlc)->pub->_autocountry)
+#else
+#define WLC_AUTOCOUNTRY_ENAB(wlc) FALSE
+#endif
+
+#define WL11D_ENAB(wlc) ((wlc)->pub->_11d)
+
+#define WL11H_ENAB(wlc) ((wlc)->pub->_11h)
+
+#define WL11U_ENAB(wlc) FALSE
+
+#define WLPROBRESP_SW_ENAB(wlc) FALSE
+
+#define LPC_ENAB(wlc) (FALSE)
+
+#if defined(WLOLPC)
+#define OLPC_ENAB(wlc) ((wlc)->pub->_olpc)
+#else
+#define OLPC_ENAB(wlc) (FALSE)
+#endif
+
+#ifdef WL_RELMCAST
+ #if defined(WL_ENAB_RUNTIME_CHECK)
+ #define RMC_SUPPORT(pub) ((pub)->_relmcast_support)
+ #define RMC_ENAB(pub) ((pub)->_relmcast)
+ #elif defined(WL_RELMCAST_DISABLED)
+ #define RMC_SUPPORT(pub) (0)
+ #define RMC_ENAB(pub) (0)
+ #else
+ #define RMC_SUPPORT(pub) (1)
+ #define RMC_ENAB(pub) ((pub)->_relmcast)
+ #endif
+#else
+ #define RMC_SUPPORT(pub) (0)
+ #define RMC_ENAB(pub) (0)
+#endif
+
+#define WLC_USE_COREFLAGS 0xffffffff
+
+#define WLC_UPDATE_STATS(wlc) 1
+#define WLCNTINCR(a) ((a)++)
+#define WLCNTCONDINCR(c, a) do { if (c) (a)++; } while (0)
+#define WLCNTDECR(a) ((a)--)
+#define WLCNTADD(a,delta) ((a) += (delta))
+#define WLCNTSET(a,value) ((a) = (value))
+#define WLCNTVAL(a) (a)
+
+#if !defined(RXCHAIN_PWRSAVE) && !defined(RADIO_PWRSAVE)
+#define WLPWRSAVERXFADD(wlc, v)
+#define WLPWRSAVERXFINCR(wlc)
+#define WLPWRSAVETXFINCR(wlc)
+#define WLPWRSAVERXFVAL(wlc) 0
+#define WLPWRSAVETXFVAL(wlc) 0
+#endif
+
+struct wlc_dpc_info {
+ uint processed;
+};
+
+extern void *wlc_attach(void *wl, uint16 vendor, uint16 device, uint unit, bool piomode,
+ osl_t *osh, void *regsva, uint bustype, void *btparam, uint *perr);
+extern uint wlc_detach(struct wlc_info *wlc);
+extern int wlc_up(struct wlc_info *wlc);
+extern uint wlc_down(struct wlc_info *wlc);
+
+extern int wlc_set(struct wlc_info *wlc, int cmd, int arg);
+extern int wlc_get(struct wlc_info *wlc, int cmd, int *arg);
+extern int wlc_iovar_getint(struct wlc_info *wlc, const char *name, int *arg);
+extern int wlc_iovar_setint(struct wlc_info *wlc, const char *name, int arg);
+extern bool wlc_chipmatch(uint16 vendor, uint16 device);
+extern void wlc_init(struct wlc_info *wlc);
+extern void wlc_reset(struct wlc_info *wlc);
+#ifdef MCAST_REGEN
+extern int32 wlc_mcast_reverse_translation(struct ether_header *eh);
+#endif
+
+extern void wlc_intrson(struct wlc_info *wlc);
+extern uint32 wlc_intrsoff(struct wlc_info *wlc);
+extern void wlc_intrsrestore(struct wlc_info *wlc, uint32 macintmask);
+extern bool wlc_intrsupd(struct wlc_info *wlc);
+extern bool wlc_isr(struct wlc_info *wlc, bool *wantdpc);
+extern bool wlc_dpc(struct wlc_info *wlc, bool bounded, struct wlc_dpc_info *dpc);
+
+extern bool wlc_sendpkt(struct wlc_info *wlc, void *sdu, struct wlc_if *wlcif);
+extern bool wlc_send80211_specified(wlc_info_t *wlc, void *sdu, uint32 rspec, struct wlc_if *wlcif);
+extern bool wlc_send80211_raw(struct wlc_info *wlc, wlc_if_t *wlcif, void *p, uint ac);
+extern int wlc_iovar_op(struct wlc_info *wlc, const char *name, void *params, int p_len, void *arg,
+ int len, bool set, struct wlc_if *wlcif);
+extern int wlc_ioctl(struct wlc_info *wlc, int cmd, void *arg, int len, struct wlc_if *wlcif);
+
+extern void wlc_statsupd(struct wlc_info *wlc);
+
+extern wlc_pub_t *wlc_pub(void *wlc);
+
+extern void tcm_sem_enter(wlc_info_t *wlc);
+extern void tcm_sem_exit(wlc_info_t *wlc);
+extern void tcm_sem_cleanup(wlc_info_t *wlc);
+
+extern int wlc_module_register(wlc_pub_t *pub, const bcm_iovar_t *iovars,
+ const char *name, void *hdl, iovar_fn_t iovar_fn,
+ watchdog_fn_t watchdog_fn, up_fn_t up_fn, down_fn_t down_fn);
+extern int wlc_module_unregister(wlc_pub_t *pub, const char *name, void *hdl);
+extern int wlc_module_add_ioctl_fn(wlc_pub_t *pub, void *hdl,
+ wlc_ioctl_fn_t ioctl_fn,
+ int num_cmds, const wlc_ioctl_cmd_t *ioctls);
+extern int wlc_module_remove_ioctl_fn(wlc_pub_t *pub, void *hdl);
+
+#define WLC_RPCTX_PARAMS 32
+
+extern void wlc_wlcif_stats_get(wlc_info_t *wlc, wlc_if_t *wlcif, wlc_if_stats_t *wlcif_stats);
+extern wlc_if_t *wlc_wlcif_get_by_index(wlc_info_t *wlc, uint idx);
+
+#if defined(BCMDBG)
+
+#define WLC_PERF_STATS_ISR 0x01
+#define WLC_PERF_STATS_DPC 0x02
+#define WLC_PERF_STATS_TMR_DPC 0x04
+#define WLC_PERF_STATS_PRB_REQ 0x08
+#define WLC_PERF_STATS_PRB_RESP 0x10
+#define WLC_PERF_STATS_BCN_ISR 0x20
+#define WLC_PERF_STATS_BCNS 0x40
+
+void wlc_update_perf_stats(wlc_info_t *wlc, uint32 mask);
+void wlc_update_isr_stats(wlc_info_t *wlc, uint32 macintstatus);
+#endif
+
+#define WLC_REPLAY_CNTRS_VALUE WPA_CAP_4_REPLAY_CNTRS
+
+#if WLC_REPLAY_CNTRS_VALUE == WPA_CAP_16_REPLAY_CNTRS
+#define PRIO2IVIDX(prio) (prio)
+#elif WLC_REPLAY_CNTRS_VALUE == WPA_CAP_4_REPLAY_CNTRS
+#define PRIO2IVIDX(prio) WME_PRIO2AC(prio)
+#else
+#error "Neither WPA_CAP_4_REPLAY_CNTRS nor WPA_CAP_16_REPLAY_CNTRS is used"
+#endif
+
+#define GPIO_2_PA_CTRL_5G_0 0x4
+
+#ifdef WL_INTERRUPTSTATS
+typedef enum {
+ nMI_MACSSPNDD = 0,
+ nMI_BCNTPL,
+ nMI_TBTT,
+ nMI_BCNSUCCESS,
+ nMI_BCNCANCLD,
+ nMI_ATIMWINEND,
+ nMI_PMQ,
+ nMI_NSPECGEN_0,
+ nMI_NSPECGEN_1,
+ nMI_MACTXERR,
+ nMI_NSPECGEN_3,
+ nMI_PHYTXERR,
+ nMI_PME,
+ nMI_GP0,
+ nMI_GP1,
+ nMI_DMAINT,
+ nMI_TXSTOP,
+ nMI_CCA,
+ nMI_BG_NOISE,
+ nMI_DTIM_TBTT,
+ nMI_PRQ,
+ nMI_PWRUP,
+ nMI_BT_RFACT_STUCK,
+ nMI_BT_PRED_REQ,
+ nMI_NOTUSED,
+ nMI_P2P,
+ nMI_DMATX,
+ nMI_TSSI_LIMIT,
+ nMI_RFDISABLE,
+ nMI_TFS,
+ nMI_PHYCHANGED,
+ nMI_TO
+} intr_enum;
+
+#define WLCINC_INTRCNT(intr) (wlc->pub->intr_cnt[(intr)]++)
+#else
+#define WLCINC_INTRCNT(intr)
+#endif
+
+#if defined(CONFIG_WL) || defined(CONFIG_WL_MODULE)
+#define WL_RTR() TRUE
+#else
+#define WL_RTR() FALSE
+#endif
+
+#endif
diff --git a/src/wl/sys/wlc_types.h b/src/wl/sys/wlc_types.h
new file mode 100644
index 0000000..42fcb37
--- /dev/null
+++ b/src/wl/sys/wlc_types.h
@@ -0,0 +1,137 @@
+/*
+ * Forward declarations for commonly used wl driver structs
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wlc_types.h 402685 2013-05-16 17:47:20Z $
+ */
+
+#ifndef _wlc_types_h_
+#define _wlc_types_h_
+
+typedef struct wlc_info wlc_info_t;
+typedef struct wlc_bsscfg wlc_bsscfg_t;
+typedef struct vndr_ie_listel vndr_ie_listel_t;
+typedef struct wlc_if wlc_if_t;
+typedef struct wl_if wl_if_t;
+typedef struct led_info led_info_t;
+typedef struct bmac_led bmac_led_t;
+typedef struct bmac_led_info bmac_led_info_t;
+typedef struct seq_cmds_info wlc_seq_cmds_info_t;
+typedef struct wlc_ccx ccx_t;
+typedef struct wlc_ccx_rm ccx_rm_t;
+typedef struct apps_wlc_psinfo apps_wlc_psinfo_t;
+typedef struct scb_module scb_module_t;
+typedef struct ba_info ba_info_t;
+typedef struct wlc_frminfo wlc_frminfo_t;
+typedef struct amsdu_info amsdu_info_t;
+typedef struct cram_info cram_info_t;
+typedef struct wlc_extlog_info wlc_extlog_info_t;
+typedef struct wlc_txq_info wlc_txq_info_t;
+typedef struct wlc_hrt_info wlc_hrt_info_t;
+typedef struct wlc_hrt_to wlc_hrt_to_t;
+typedef struct wlc_cac wlc_cac_t;
+typedef struct ampdu_tx_info ampdu_tx_info_t;
+typedef struct ampdu_rx_info ampdu_rx_info_t;
+typedef struct wlc_ratesel_info wlc_ratesel_info_t;
+typedef struct ratesel_info ratesel_info_t;
+typedef struct wlc_ap_info wlc_ap_info_t;
+typedef struct wlc_scan_info wlc_scan_info_t;
+typedef struct dpt_info dpt_info_t;
+typedef struct tdls_info tdls_info_t;
+typedef struct dls_info dls_info_t;
+typedef struct l2_filter_info l2_filter_info_t;
+typedef struct wlc_auth_info wlc_auth_info_t;
+typedef struct wlc_psta_info wlc_psta_info_t;
+typedef struct wlc_psa wlc_psa_t;
+typedef struct wowl_info wowl_info_t;
+typedef struct wlc_plt_info wlc_plt_pub_t;
+typedef struct supplicant supplicant_t;
+typedef struct authenticator authenticator_t;
+typedef struct antsel_info antsel_info_t;
+typedef struct bmac_pmq bmac_pmq_t;
+typedef struct wlc_rrm_info wlc_rrm_info_t;
+typedef struct rm_info rm_info_t;
+
+struct d11init;
+
+#ifndef _hnddma_pub_
+#define _hnddma_pub_
+typedef const struct hnddma_pub hnddma_t;
+#endif
+
+typedef struct wlc_dpc_info wlc_dpc_info_t;
+
+typedef struct wlc_11h_info wlc_11h_info_t;
+typedef struct wlc_tpc_info wlc_tpc_info_t;
+typedef struct wlc_csa_info wlc_csa_info_t;
+typedef struct wlc_quiet_info wlc_quiet_info_t;
+typedef struct cca_info cca_info_t;
+typedef struct itfr_info itfr_info_t;
+
+typedef struct wlc_ol_info_t wlc_ol_info_t;
+#ifdef WLOLPC
+typedef struct wlc_olpc_eng_info_t wlc_olpc_eng_info_t;
+#endif
+typedef void(*wlc_stf_txchain_evt_notify)(wlc_info_t *wlc);
+
+typedef struct wlc_11d_info wlc_11d_info_t;
+typedef struct wlc_cntry_info wlc_cntry_info_t;
+
+typedef struct wlc_dfs_info wlc_dfs_info_t;
+
+typedef struct bsscfg_module bsscfg_module_t;
+
+typedef struct wlc_prq_info_s wlc_prq_info_t;
+
+typedef struct wlc_prot_info wlc_prot_info_t;
+typedef struct wlc_prot_g_info wlc_prot_g_info_t;
+typedef struct wlc_prot_n_info wlc_prot_n_info_t;
+
+typedef struct wlc_11u_info wlc_11u_info_t;
+typedef struct wlc_probresp_info wlc_probresp_info_t;
+typedef struct wlc_wapi_info wlc_wapi_info_t;
+
+typedef struct wlc_bssload_info wlc_bssload_info_t;
+
+typedef struct wlc_rfc wlc_rfc_t;
+typedef struct wlc_pktc_info wlc_pktc_info_t;
+
+typedef struct wlc_lpc_info wlc_lpc_info_t;
+typedef struct lpc_info lpc_info_t;
+typedef struct rate_lcb_info rate_lcb_info_t;
+
+typedef struct wlc_txbf_info wlc_txbf_info_t;
+
+typedef struct wlc_bcn_clsg_info wlc_bcn_clsg_info_t;
+
+typedef struct wlc_nar_info wlc_nar_info_t;
+
+typedef struct wlc_relmcast_info wlc_relmcast_info_t;
+
+typedef struct okc_info okc_info_t;
+
+typedef struct wlc_gas_info wlc_gas_info_t;
+typedef struct wlc_p2po_info wlc_p2po_info_t;
+typedef struct wlc_disc_info wlc_disc_info_t;
+typedef struct wlc_anqpo_info wlc_anqpo_info_t;
+
+typedef struct wlc_pdsvc_info wlc_pdsvc_info_t;
+
+typedef struct wlc_hw wlc_hw_t;
+typedef struct wlc_hw_info wlc_hw_info_t;
+typedef struct wlc_hwband wlc_hwband_t;
+
+#endif
diff --git a/src/wl/sys/wlc_utils.h b/src/wl/sys/wlc_utils.h
new file mode 100644
index 0000000..194ca69
--- /dev/null
+++ b/src/wl/sys/wlc_utils.h
@@ -0,0 +1,51 @@
+/*
+ * utilities related header file
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wlc_p2p.h 274724 2011-08-01 17:06:47Z $
+ */
+
+#ifndef _wlc_utils_h_
+#define _wlc_utils_h_
+
+#include <typedefs.h>
+
+struct rsn_parms {
+ uint8 flags;
+ uint8 multicast;
+ uint8 ucount;
+ uint8 unicast[4];
+ uint8 acount;
+ uint8 auth[4];
+ uint8 PAD[4];
+ uint8 cap[4];
+};
+
+typedef struct rsn_parms rsn_parms_t;
+
+extern void wlc_uint64_add(uint32* high, uint32* low, uint32 inc_high, uint32 inc_low);
+extern void wlc_uint64_sub(uint32* a_high, uint32* a_low, uint32 b_high, uint32 b_low);
+extern bool wlc_uint64_lt(uint32 a_high, uint32 a_low, uint32 b_high, uint32 b_low);
+extern uint32 wlc_calc_tbtt_offset(uint32 bi, uint32 tsf_h, uint32 tsf_l);
+extern uint32 wlc_calc_next_pos32(uint32 tsf, uint32 cur, uint32 interval, bool wrap);
+extern void wlc_tsf64_to_next_tbtt64(uint32 bcn_int, uint32 *tsf_h, uint32 *tsf_l);
+extern void wlc_tbtt21_to_tbtt32(uint32 tsf_l, uint32 *tbtt_l);
+extern void wlc_tbtt21_to_tbtt64(uint32 tsf_h, uint32 tsf_l, uint32 *tbtt_h, uint32 *tbtt_l);
+
+extern bool wlc_rsn_ucast_lookup(struct rsn_parms *rsn, uint8 auth);
+extern bool wlc_rsn_akm_lookup(struct rsn_parms *rsn, uint8 akm);
+
+#endif
diff --git a/src/wl/sys/wlc_wowl.h b/src/wl/sys/wlc_wowl.h
new file mode 100644
index 0000000..d21eb58
--- /dev/null
+++ b/src/wl/sys/wlc_wowl.h
@@ -0,0 +1,86 @@
+/*
+ * Wake-on-Wireless related header file
+ *
+ * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: wlc_wowl.h 458427 2014-02-26 23:12:38Z $
+*/
+
+#ifndef _wlc_wowl_h_
+#define _wlc_wowl_h_
+
+#define WLC_WOWL_OFFLOADS
+
+extern wowl_info_t *wlc_wowl_attach(wlc_info_t *wlc);
+extern void wlc_wowl_detach(wowl_info_t *wowl);
+extern bool wlc_wowl_cap(struct wlc_info *wlc);
+extern bool wlc_wowl_enable(wowl_info_t *wowl);
+extern uint32 wlc_wowl_clear(wowl_info_t *wowl);
+void wlc_wowl_wake_reason_process(wlc_info_t *wlc);
+#if defined(WLC_WOWL_OFFLOADS)
+extern void wlc_wowl_set_wpa_m1(wowl_info_t *wowl);
+extern void wlc_wowl_set_eapol_id(wowl_info_t *wowl);
+extern int wlc_wowl_set_key_info(wowl_info_t *wowl, uint32 offload_id, void *kek,
+ int kek_len, void* kck, int kck_len, void *replay_counter, int replay_counter_len);
+extern int wlc_wowl_add_offload_ipv4_arp(wowl_info_t *wowl, uint32 offload_id,
+ uint8 * RemoteIPv4Address, uint8 *HostIPv4Address, uint8 * MacAddress);
+extern int wlc_wowl_add_offload_ipv6_ns(wowl_info_t *wowl, uint32 offload_id,
+ uint8 * RemoteIPv6Address, uint8 *SolicitedNodeIPv6Address,
+ uint8 * MacAddress, uint8 * TargetIPv6Address1, uint8 * TargetIPv6Address2);
+
+extern void wlc_wowl_set_keepalive(wowl_info_t *wowl, uint16 period_keepalive);
+extern uint8 *wlc_wowl_solicitipv6_addr(uint8 *TargetIPv6Address1, uint8 *solicitaddress);
+extern int wlc_wowl_remove_offload(wowl_info_t *wowl, uint32 offload_id, uint32 * type);
+extern int wlc_wowl_get_replay_counter(wowl_info_t *wowl, void *replay_counter, int *len);
+
+extern void wlc_wowl_enable_completed(wowl_info_t *wowl);
+extern void wlc_wowl_disable_completed(wowl_info_t *wowl, void *wowl_host_info);
+#endif
+
+#define WOWL_IPV4_ARP_TYPE 0
+#define WOWL_IPV6_NS_TYPE 1
+#define WOWL_DOT11_RSN_REKEY_TYPE 2
+#define WOWL_OFFLOAD_INVALID_TYPE 3
+
+#define WOWL_IPV4_ARP_IDX 0
+#define WOWL_IPV6_NS_0_IDX 1
+#define WOWL_IPV6_NS_1_IDX 2
+#define WOWL_DOT11_RSN_REKEY_IDX 3
+#define WOWL_OFFLOAD_INVALID_IDX 4
+
+#define MAX_WOWL_OFFLOAD_ROWS 4
+#define MAX_WOWL_IPV6_ARP_PATTERNS 1
+#define MAX_WOWL_IPV6_NS_PATTERNS 2
+#define MAX_WOWL_IPV6_NS_OFFLOADS 1
+
+#define WOWL_INT_RESERVED_MASK 0xFF000000
+#define WOWL_INT_DATA_MASK 0x00FFFFFF
+#define WOWL_INT_PATTERN_FLAG 0x80000000
+#define WOWL_INT_NS_TA2_FLAG 0x40000000
+#define WOWL_INT_PATTERN_IDX_MASK 0x0F000000
+#define WOWL_INT_PATTERN_IDX_SHIFT 24
+
+#define MAXPATTERNS(wlc) \
+ (wlc_wowl_cap(wlc) ? \
+ (WLOFFLD_CAP(wlc) ? 12 : \
+ ((D11REV_GE((wlc)->pub->corerev, 15) && D11REV_LT((wlc)->pub->corerev, 40)) ? 12 : 4)) \
+ : 0)
+
+#define WOWL_OFFLOAD_ENABLED(wlc) \
+ ((CHIPID(wlc->pub->sih->chip) == BCM4360_CHIP_ID) || WIN7_AND_UP_OS(wlc->pub))
+
+#define WOWL_KEEPALIVE_FIXED_PARAM 11
+
+#endif