diff options
Diffstat (limited to 'src/wl')
-rw-r--r-- | src/wl/sys/wl_cfg80211_hybrid.c | 3022 | ||||
-rw-r--r-- | src/wl/sys/wl_cfg80211_hybrid.h | 231 | ||||
-rw-r--r-- | src/wl/sys/wl_dbg.h | 74 | ||||
-rw-r--r-- | src/wl/sys/wl_export.h | 81 | ||||
-rw-r--r-- | src/wl/sys/wl_iw.c | 2830 | ||||
-rw-r--r-- | src/wl/sys/wl_iw.h | 150 | ||||
-rw-r--r-- | src/wl/sys/wl_linux.c | 3371 | ||||
-rw-r--r-- | src/wl/sys/wl_linux.h | 192 | ||||
-rw-r--r-- | src/wl/sys/wlc_ethereal.h | 129 | ||||
-rw-r--r-- | src/wl/sys/wlc_key.h | 76 | ||||
-rw-r--r-- | src/wl/sys/wlc_pub.h | 927 | ||||
-rw-r--r-- | src/wl/sys/wlc_types.h | 137 | ||||
-rw-r--r-- | src/wl/sys/wlc_utils.h | 51 | ||||
-rw-r--r-- | src/wl/sys/wlc_wowl.h | 86 |
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, ðer_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(¶ms, 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, ¶ms); + 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 *)¶mval, 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 *)¶mval, 1); + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", (char *)¶mval, 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 *)¶mval, 1); + break; + + case IW_AUTH_DROP_UNENCRYPTED: + dev_wlc_bufvar_get(dev, "wsec_restrict", (char *)¶mval, 1); + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", (char *)¶mval, 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 |