162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Implement cfg80211 ("iw") support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany 662306a36Sopenharmony_ci * Holger Schurig <hs4233@mail.mn-solutions.de> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/hardirq.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/wait.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/ieee80211.h> 1762306a36Sopenharmony_ci#include <net/cfg80211.h> 1862306a36Sopenharmony_ci#include <asm/unaligned.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "decl.h" 2162306a36Sopenharmony_ci#include "cfg.h" 2262306a36Sopenharmony_ci#include "cmd.h" 2362306a36Sopenharmony_ci#include "mesh.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define CHAN2G(_channel, _freq, _flags) { \ 2762306a36Sopenharmony_ci .band = NL80211_BAND_2GHZ, \ 2862306a36Sopenharmony_ci .center_freq = (_freq), \ 2962306a36Sopenharmony_ci .hw_value = (_channel), \ 3062306a36Sopenharmony_ci .flags = (_flags), \ 3162306a36Sopenharmony_ci .max_antenna_gain = 0, \ 3262306a36Sopenharmony_ci .max_power = 30, \ 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct ieee80211_channel lbs_2ghz_channels[] = { 3662306a36Sopenharmony_ci CHAN2G(1, 2412, 0), 3762306a36Sopenharmony_ci CHAN2G(2, 2417, 0), 3862306a36Sopenharmony_ci CHAN2G(3, 2422, 0), 3962306a36Sopenharmony_ci CHAN2G(4, 2427, 0), 4062306a36Sopenharmony_ci CHAN2G(5, 2432, 0), 4162306a36Sopenharmony_ci CHAN2G(6, 2437, 0), 4262306a36Sopenharmony_ci CHAN2G(7, 2442, 0), 4362306a36Sopenharmony_ci CHAN2G(8, 2447, 0), 4462306a36Sopenharmony_ci CHAN2G(9, 2452, 0), 4562306a36Sopenharmony_ci CHAN2G(10, 2457, 0), 4662306a36Sopenharmony_ci CHAN2G(11, 2462, 0), 4762306a36Sopenharmony_ci CHAN2G(12, 2467, 0), 4862306a36Sopenharmony_ci CHAN2G(13, 2472, 0), 4962306a36Sopenharmony_ci CHAN2G(14, 2484, 0), 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define RATETAB_ENT(_rate, _hw_value, _flags) { \ 5362306a36Sopenharmony_ci .bitrate = (_rate), \ 5462306a36Sopenharmony_ci .hw_value = (_hw_value), \ 5562306a36Sopenharmony_ci .flags = (_flags), \ 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Table 6 in section 3.2.1.1 */ 6062306a36Sopenharmony_cistatic struct ieee80211_rate lbs_rates[] = { 6162306a36Sopenharmony_ci RATETAB_ENT(10, 0, 0), 6262306a36Sopenharmony_ci RATETAB_ENT(20, 1, 0), 6362306a36Sopenharmony_ci RATETAB_ENT(55, 2, 0), 6462306a36Sopenharmony_ci RATETAB_ENT(110, 3, 0), 6562306a36Sopenharmony_ci RATETAB_ENT(60, 9, 0), 6662306a36Sopenharmony_ci RATETAB_ENT(90, 6, 0), 6762306a36Sopenharmony_ci RATETAB_ENT(120, 7, 0), 6862306a36Sopenharmony_ci RATETAB_ENT(180, 8, 0), 6962306a36Sopenharmony_ci RATETAB_ENT(240, 9, 0), 7062306a36Sopenharmony_ci RATETAB_ENT(360, 10, 0), 7162306a36Sopenharmony_ci RATETAB_ENT(480, 11, 0), 7262306a36Sopenharmony_ci RATETAB_ENT(540, 12, 0), 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic struct ieee80211_supported_band lbs_band_2ghz = { 7662306a36Sopenharmony_ci .channels = lbs_2ghz_channels, 7762306a36Sopenharmony_ci .n_channels = ARRAY_SIZE(lbs_2ghz_channels), 7862306a36Sopenharmony_ci .bitrates = lbs_rates, 7962306a36Sopenharmony_ci .n_bitrates = ARRAY_SIZE(lbs_rates), 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const u32 cipher_suites[] = { 8462306a36Sopenharmony_ci WLAN_CIPHER_SUITE_WEP40, 8562306a36Sopenharmony_ci WLAN_CIPHER_SUITE_WEP104, 8662306a36Sopenharmony_ci WLAN_CIPHER_SUITE_TKIP, 8762306a36Sopenharmony_ci WLAN_CIPHER_SUITE_CCMP, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* Time to stay on the channel */ 9162306a36Sopenharmony_ci#define LBS_DWELL_PASSIVE 100 9262306a36Sopenharmony_ci#define LBS_DWELL_ACTIVE 40 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/*************************************************************************** 9662306a36Sopenharmony_ci * Misc utility functions 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * TLVs are Marvell specific. They are very similar to IEs, they have the 9962306a36Sopenharmony_ci * same structure: type, length, data*. The only difference: for IEs, the 10062306a36Sopenharmony_ci * type and length are u8, but for TLVs they're __le16. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* 10462306a36Sopenharmony_ci * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1 10562306a36Sopenharmony_ci * in the firmware spec 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic int lbs_auth_to_authtype(enum nl80211_auth_type auth_type) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci int ret = -ENOTSUPP; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci switch (auth_type) { 11262306a36Sopenharmony_ci case NL80211_AUTHTYPE_OPEN_SYSTEM: 11362306a36Sopenharmony_ci case NL80211_AUTHTYPE_SHARED_KEY: 11462306a36Sopenharmony_ci ret = auth_type; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case NL80211_AUTHTYPE_AUTOMATIC: 11762306a36Sopenharmony_ci ret = NL80211_AUTHTYPE_OPEN_SYSTEM; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case NL80211_AUTHTYPE_NETWORK_EAP: 12062306a36Sopenharmony_ci ret = 0x80; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci default: 12362306a36Sopenharmony_ci /* silence compiler */ 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * Various firmware commands need the list of supported rates, but with 13262306a36Sopenharmony_ci * the hight-bit set for basic rates 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_cistatic int lbs_add_rates(u8 *rates) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci size_t i; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { 13962306a36Sopenharmony_ci u8 rate = lbs_rates[i].bitrate / 5; 14062306a36Sopenharmony_ci if (rate == 0x02 || rate == 0x04 || 14162306a36Sopenharmony_ci rate == 0x0b || rate == 0x16) 14262306a36Sopenharmony_ci rate |= 0x80; 14362306a36Sopenharmony_ci rates[i] = rate; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci return ARRAY_SIZE(lbs_rates); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/*************************************************************************** 15062306a36Sopenharmony_ci * TLV utility functions 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * TLVs are Marvell specific. They are very similar to IEs, they have the 15362306a36Sopenharmony_ci * same structure: type, length, data*. The only difference: for IEs, the 15462306a36Sopenharmony_ci * type and length are u8, but for TLVs they're __le16. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* 15962306a36Sopenharmony_ci * Add ssid TLV 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci#define LBS_MAX_SSID_TLV_SIZE \ 16262306a36Sopenharmony_ci (sizeof(struct mrvl_ie_header) \ 16362306a36Sopenharmony_ci + IEEE80211_MAX_SSID_LEN) 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * TLV-ID SSID 00 00 17162306a36Sopenharmony_ci * length 06 00 17262306a36Sopenharmony_ci * ssid 4d 4e 54 45 53 54 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID); 17562306a36Sopenharmony_ci ssid_tlv->header.len = cpu_to_le16(ssid_len); 17662306a36Sopenharmony_ci memcpy(ssid_tlv->ssid, ssid, ssid_len); 17762306a36Sopenharmony_ci return sizeof(ssid_tlv->header) + ssid_len; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* 18262306a36Sopenharmony_ci * Add channel list TLV (section 8.4.2) 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Actual channel data comes from priv->wdev->wiphy->channels. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \ 18762306a36Sopenharmony_ci (sizeof(struct mrvl_ie_header) \ 18862306a36Sopenharmony_ci + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset))) 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv, 19162306a36Sopenharmony_ci int last_channel, int active_scan) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci int chanscanparamsize = sizeof(struct chanscanparamset) * 19462306a36Sopenharmony_ci (last_channel - priv->scan_channel); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci struct mrvl_ie_header *header = (void *) tlv; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * TLV-ID CHANLIST 01 01 20062306a36Sopenharmony_ci * length 0e 00 20162306a36Sopenharmony_ci * channel 00 01 00 00 00 64 00 20262306a36Sopenharmony_ci * radio type 00 20362306a36Sopenharmony_ci * channel 01 20462306a36Sopenharmony_ci * scan type 00 20562306a36Sopenharmony_ci * min scan time 00 00 20662306a36Sopenharmony_ci * max scan time 64 00 20762306a36Sopenharmony_ci * channel 2 00 02 00 00 00 64 00 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci header->type = cpu_to_le16(TLV_TYPE_CHANLIST); 21262306a36Sopenharmony_ci header->len = cpu_to_le16(chanscanparamsize); 21362306a36Sopenharmony_ci tlv += sizeof(struct mrvl_ie_header); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel, 21662306a36Sopenharmony_ci last_channel); */ 21762306a36Sopenharmony_ci memset(tlv, 0, chanscanparamsize); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci while (priv->scan_channel < last_channel) { 22062306a36Sopenharmony_ci struct chanscanparamset *param = (void *) tlv; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci param->radiotype = CMD_SCAN_RADIO_TYPE_BG; 22362306a36Sopenharmony_ci param->channumber = 22462306a36Sopenharmony_ci priv->scan_req->channels[priv->scan_channel]->hw_value; 22562306a36Sopenharmony_ci if (active_scan) { 22662306a36Sopenharmony_ci param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE); 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci param->chanscanmode.passivescan = 1; 22962306a36Sopenharmony_ci param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci tlv += sizeof(struct chanscanparamset); 23262306a36Sopenharmony_ci priv->scan_channel++; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci return sizeof(struct mrvl_ie_header) + chanscanparamsize; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * Add rates TLV 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * The rates are in lbs_bg_rates[], but for the 802.11b 24262306a36Sopenharmony_ci * rates the high bit is set. We add this TLV only because 24362306a36Sopenharmony_ci * there's a firmware which otherwise doesn't report all 24462306a36Sopenharmony_ci * APs in range. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ci#define LBS_MAX_RATES_TLV_SIZE \ 24762306a36Sopenharmony_ci (sizeof(struct mrvl_ie_header) \ 24862306a36Sopenharmony_ci + (ARRAY_SIZE(lbs_rates))) 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* Adds a TLV with all rates the hardware supports */ 25162306a36Sopenharmony_cistatic int lbs_add_supported_rates_tlv(u8 *tlv) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci size_t i; 25462306a36Sopenharmony_ci struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * TLV-ID RATES 01 00 25862306a36Sopenharmony_ci * length 0e 00 25962306a36Sopenharmony_ci * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); 26262306a36Sopenharmony_ci tlv += sizeof(rate_tlv->header); 26362306a36Sopenharmony_ci i = lbs_add_rates(tlv); 26462306a36Sopenharmony_ci tlv += i; 26562306a36Sopenharmony_ci rate_tlv->header.len = cpu_to_le16(i); 26662306a36Sopenharmony_ci return sizeof(rate_tlv->header) + i; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* Add common rates from a TLV and return the new end of the TLV */ 27062306a36Sopenharmony_cistatic u8 * 27162306a36Sopenharmony_ciadd_ie_rates(u8 *tlv, const u8 *ie, int *nrates) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci int hw, ap, ap_max = ie[1]; 27462306a36Sopenharmony_ci u8 hw_rate; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (ap_max > MAX_RATES) { 27762306a36Sopenharmony_ci lbs_deb_assoc("invalid rates\n"); 27862306a36Sopenharmony_ci return tlv; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci /* Advance past IE header */ 28162306a36Sopenharmony_ci ie += 2; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { 28662306a36Sopenharmony_ci hw_rate = lbs_rates[hw].bitrate / 5; 28762306a36Sopenharmony_ci for (ap = 0; ap < ap_max; ap++) { 28862306a36Sopenharmony_ci if (hw_rate == (ie[ap] & 0x7f)) { 28962306a36Sopenharmony_ci *tlv++ = ie[ap]; 29062306a36Sopenharmony_ci *nrates = *nrates + 1; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci return tlv; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* 29862306a36Sopenharmony_ci * Adds a TLV with all rates the hardware *and* BSS supports. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; 30362306a36Sopenharmony_ci const u8 *rates_eid, *ext_rates_eid; 30462306a36Sopenharmony_ci int n = 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci rcu_read_lock(); 30762306a36Sopenharmony_ci rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); 30862306a36Sopenharmony_ci ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * 01 00 TLV_TYPE_RATES 31262306a36Sopenharmony_ci * 04 00 len 31362306a36Sopenharmony_ci * 82 84 8b 96 rates 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); 31662306a36Sopenharmony_ci tlv += sizeof(rate_tlv->header); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Add basic rates */ 31962306a36Sopenharmony_ci if (rates_eid) { 32062306a36Sopenharmony_ci tlv = add_ie_rates(tlv, rates_eid, &n); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Add extended rates, if any */ 32362306a36Sopenharmony_ci if (ext_rates_eid) 32462306a36Sopenharmony_ci tlv = add_ie_rates(tlv, ext_rates_eid, &n); 32562306a36Sopenharmony_ci } else { 32662306a36Sopenharmony_ci lbs_deb_assoc("assoc: bss had no basic rate IE\n"); 32762306a36Sopenharmony_ci /* Fallback: add basic 802.11b rates */ 32862306a36Sopenharmony_ci *tlv++ = 0x82; 32962306a36Sopenharmony_ci *tlv++ = 0x84; 33062306a36Sopenharmony_ci *tlv++ = 0x8b; 33162306a36Sopenharmony_ci *tlv++ = 0x96; 33262306a36Sopenharmony_ci n = 4; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci rcu_read_unlock(); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci rate_tlv->header.len = cpu_to_le16(n); 33762306a36Sopenharmony_ci return sizeof(rate_tlv->header) + n; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* 34262306a36Sopenharmony_ci * Add auth type TLV. 34362306a36Sopenharmony_ci * 34462306a36Sopenharmony_ci * This is only needed for newer firmware (V9 and up). 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci#define LBS_MAX_AUTH_TYPE_TLV_SIZE \ 34762306a36Sopenharmony_ci sizeof(struct mrvl_ie_auth_type) 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct mrvl_ie_auth_type *auth = (void *) tlv; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * 1f 01 TLV_TYPE_AUTH_TYPE 35562306a36Sopenharmony_ci * 01 00 len 35662306a36Sopenharmony_ci * 01 auth type 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); 35962306a36Sopenharmony_ci auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header)); 36062306a36Sopenharmony_ci auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type)); 36162306a36Sopenharmony_ci return sizeof(*auth); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* 36662306a36Sopenharmony_ci * Add channel (phy ds) TLV 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci#define LBS_MAX_CHANNEL_TLV_SIZE \ 36962306a36Sopenharmony_ci sizeof(struct mrvl_ie_header) 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic int lbs_add_channel_tlv(u8 *tlv, u8 channel) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct mrvl_ie_ds_param_set *ds = (void *) tlv; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * 03 00 TLV_TYPE_PHY_DS 37762306a36Sopenharmony_ci * 01 00 len 37862306a36Sopenharmony_ci * 06 channel 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); 38162306a36Sopenharmony_ci ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header)); 38262306a36Sopenharmony_ci ds->channel = channel; 38362306a36Sopenharmony_ci return sizeof(*ds); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * Add (empty) CF param TLV of the form: 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci#define LBS_MAX_CF_PARAM_TLV_SIZE \ 39162306a36Sopenharmony_ci sizeof(struct mrvl_ie_header) 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int lbs_add_cf_param_tlv(u8 *tlv) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct mrvl_ie_cf_param_set *cf = (void *)tlv; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * 04 00 TLV_TYPE_CF 39962306a36Sopenharmony_ci * 06 00 len 40062306a36Sopenharmony_ci * 00 cfpcnt 40162306a36Sopenharmony_ci * 00 cfpperiod 40262306a36Sopenharmony_ci * 00 00 cfpmaxduration 40362306a36Sopenharmony_ci * 00 00 cfpdurationremaining 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci cf->header.type = cpu_to_le16(TLV_TYPE_CF); 40662306a36Sopenharmony_ci cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header)); 40762306a36Sopenharmony_ci return sizeof(*cf); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* 41162306a36Sopenharmony_ci * Add WPA TLV 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_ci#define LBS_MAX_WPA_TLV_SIZE \ 41462306a36Sopenharmony_ci (sizeof(struct mrvl_ie_header) \ 41562306a36Sopenharmony_ci + 128 /* TODO: I guessed the size */) 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct mrvl_ie_data *wpatlv = (struct mrvl_ie_data *)tlv; 42062306a36Sopenharmony_ci const struct element *wpaie; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Find the first RSN or WPA IE to use */ 42362306a36Sopenharmony_ci wpaie = cfg80211_find_elem(WLAN_EID_RSN, ie, ie_len); 42462306a36Sopenharmony_ci if (!wpaie) 42562306a36Sopenharmony_ci wpaie = cfg80211_find_vendor_elem(WLAN_OUI_MICROSOFT, 42662306a36Sopenharmony_ci WLAN_OUI_TYPE_MICROSOFT_WPA, 42762306a36Sopenharmony_ci ie, ie_len); 42862306a36Sopenharmony_ci if (!wpaie || wpaie->datalen > 128) 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* 43262306a36Sopenharmony_ci * Convert the found IE to a TLV. IEs use u8 for the header, 43362306a36Sopenharmony_ci * u8 type 43462306a36Sopenharmony_ci * u8 len 43562306a36Sopenharmony_ci * u8[] data 43662306a36Sopenharmony_ci * but TLVs use __le16 instead: 43762306a36Sopenharmony_ci * __le16 type 43862306a36Sopenharmony_ci * __le16 len 43962306a36Sopenharmony_ci * u8[] data 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci wpatlv->header.type = cpu_to_le16(wpaie->id); 44262306a36Sopenharmony_ci wpatlv->header.len = cpu_to_le16(wpaie->datalen); 44362306a36Sopenharmony_ci memcpy(wpatlv->data, wpaie->data, wpaie->datalen); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Return the total number of bytes added to the TLV buffer */ 44662306a36Sopenharmony_ci return sizeof(struct mrvl_ie_header) + wpaie->datalen; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* Add WPS enrollee TLV 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci#define LBS_MAX_WPS_ENROLLEE_TLV_SIZE \ 45262306a36Sopenharmony_ci (sizeof(struct mrvl_ie_header) \ 45362306a36Sopenharmony_ci + 256) 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int lbs_add_wps_enrollee_tlv(u8 *tlv, const u8 *ie, size_t ie_len) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct mrvl_ie_data *wpstlv = (struct mrvl_ie_data *)tlv; 45862306a36Sopenharmony_ci const struct element *wpsie; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Look for a WPS IE and add it to the probe request */ 46162306a36Sopenharmony_ci wpsie = cfg80211_find_vendor_elem(WLAN_OUI_MICROSOFT, 46262306a36Sopenharmony_ci WLAN_OUI_TYPE_MICROSOFT_WPS, 46362306a36Sopenharmony_ci ie, ie_len); 46462306a36Sopenharmony_ci if (!wpsie) 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Convert the WPS IE to a TLV. The IE looks like this: 46862306a36Sopenharmony_ci * u8 type (WLAN_EID_VENDOR_SPECIFIC) 46962306a36Sopenharmony_ci * u8 len 47062306a36Sopenharmony_ci * u8[] data 47162306a36Sopenharmony_ci * but the TLV will look like this instead: 47262306a36Sopenharmony_ci * __le16 type (TLV_TYPE_WPS_ENROLLEE) 47362306a36Sopenharmony_ci * __le16 len 47462306a36Sopenharmony_ci * u8[] data 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci wpstlv->header.type = cpu_to_le16(TLV_TYPE_WPS_ENROLLEE); 47762306a36Sopenharmony_ci wpstlv->header.len = cpu_to_le16(wpsie->datalen); 47862306a36Sopenharmony_ci memcpy(wpstlv->data, wpsie->data, wpsie->datalen); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* Return the total number of bytes added to the TLV buffer */ 48162306a36Sopenharmony_ci return sizeof(struct mrvl_ie_header) + wpsie->datalen; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/* 48562306a36Sopenharmony_ci * Set Channel 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, 48962306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 49262306a36Sopenharmony_ci int ret = -ENOTSUPP; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) 49562306a36Sopenharmony_ci goto out; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ret = lbs_set_channel(priv, chandef->chan->hw_value); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci out: 50062306a36Sopenharmony_ci return ret; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, 50462306a36Sopenharmony_ci struct net_device *netdev, 50562306a36Sopenharmony_ci struct ieee80211_channel *channel) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 50862306a36Sopenharmony_ci int ret = -ENOTSUPP; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (netdev != priv->mesh_dev) 51162306a36Sopenharmony_ci goto out; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ret = lbs_mesh_set_channel(priv, channel->hw_value); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci out: 51662306a36Sopenharmony_ci return ret; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/* 52262306a36Sopenharmony_ci * Scanning 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci/* 52662306a36Sopenharmony_ci * When scanning, the firmware doesn't send a nul packet with the power-safe 52762306a36Sopenharmony_ci * bit to the AP. So we cannot stay away from our current channel too long, 52862306a36Sopenharmony_ci * otherwise we loose data. So take a "nap" while scanning every other 52962306a36Sopenharmony_ci * while. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci#define LBS_SCAN_BEFORE_NAP 4 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci/* 53562306a36Sopenharmony_ci * When the firmware reports back a scan-result, it gives us an "u8 rssi", 53662306a36Sopenharmony_ci * which isn't really an RSSI, as it becomes larger when moving away from 53762306a36Sopenharmony_ci * the AP. Anyway, we need to convert that into mBm. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci#define LBS_SCAN_RSSI_TO_MBM(rssi) \ 54062306a36Sopenharmony_ci ((-(int)rssi + 3)*100) 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, 54362306a36Sopenharmony_ci struct cmd_header *resp) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct cfg80211_bss *bss; 54662306a36Sopenharmony_ci struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp; 54762306a36Sopenharmony_ci int bsssize; 54862306a36Sopenharmony_ci const u8 *pos; 54962306a36Sopenharmony_ci const u8 *tsfdesc; 55062306a36Sopenharmony_ci int tsfsize; 55162306a36Sopenharmony_ci int i; 55262306a36Sopenharmony_ci int ret = -EILSEQ; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci bsssize = get_unaligned_le16(&scanresp->bssdescriptsize); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n", 55762306a36Sopenharmony_ci scanresp->nr_sets, bsssize, le16_to_cpu(resp->size)); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (scanresp->nr_sets == 0) { 56062306a36Sopenharmony_ci ret = 0; 56162306a36Sopenharmony_ci goto done; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* 56562306a36Sopenharmony_ci * The general layout of the scan response is described in chapter 56662306a36Sopenharmony_ci * 5.7.1. Basically we have a common part, then any number of BSS 56762306a36Sopenharmony_ci * descriptor sections. Finally we have section with the same number 56862306a36Sopenharmony_ci * of TSFs. 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * cmd_ds_802_11_scan_rsp 57162306a36Sopenharmony_ci * cmd_header 57262306a36Sopenharmony_ci * pos_size 57362306a36Sopenharmony_ci * nr_sets 57462306a36Sopenharmony_ci * bssdesc 1 57562306a36Sopenharmony_ci * bssid 57662306a36Sopenharmony_ci * rssi 57762306a36Sopenharmony_ci * timestamp 57862306a36Sopenharmony_ci * intvl 57962306a36Sopenharmony_ci * capa 58062306a36Sopenharmony_ci * IEs 58162306a36Sopenharmony_ci * bssdesc 2 58262306a36Sopenharmony_ci * bssdesc n 58362306a36Sopenharmony_ci * MrvlIEtypes_TsfFimestamp_t 58462306a36Sopenharmony_ci * TSF for BSS 1 58562306a36Sopenharmony_ci * TSF for BSS 2 58662306a36Sopenharmony_ci * TSF for BSS n 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci pos = scanresp->bssdesc_and_tlvbuffer; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer, 59262306a36Sopenharmony_ci bsssize); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci tsfdesc = pos + bsssize; 59562306a36Sopenharmony_ci tsfsize = 4 + 8 * scanresp->nr_sets; 59662306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* Validity check: we expect a Marvell-Local TLV */ 59962306a36Sopenharmony_ci i = get_unaligned_le16(tsfdesc); 60062306a36Sopenharmony_ci tsfdesc += 2; 60162306a36Sopenharmony_ci if (i != TLV_TYPE_TSFTIMESTAMP) { 60262306a36Sopenharmony_ci lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i); 60362306a36Sopenharmony_ci goto done; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * Validity check: the TLV holds TSF values with 8 bytes each, so 60862306a36Sopenharmony_ci * the size in the TLV must match the nr_sets value 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci i = get_unaligned_le16(tsfdesc); 61162306a36Sopenharmony_ci tsfdesc += 2; 61262306a36Sopenharmony_ci if (i / 8 != scanresp->nr_sets) { 61362306a36Sopenharmony_ci lbs_deb_scan("scan response: invalid number of TSF timestamp " 61462306a36Sopenharmony_ci "sets (expected %d got %d)\n", scanresp->nr_sets, 61562306a36Sopenharmony_ci i / 8); 61662306a36Sopenharmony_ci goto done; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci for (i = 0; i < scanresp->nr_sets; i++) { 62062306a36Sopenharmony_ci const u8 *bssid; 62162306a36Sopenharmony_ci const u8 *ie; 62262306a36Sopenharmony_ci int left; 62362306a36Sopenharmony_ci int ielen; 62462306a36Sopenharmony_ci int rssi; 62562306a36Sopenharmony_ci u16 intvl; 62662306a36Sopenharmony_ci u16 capa; 62762306a36Sopenharmony_ci int chan_no = -1; 62862306a36Sopenharmony_ci const u8 *ssid = NULL; 62962306a36Sopenharmony_ci u8 ssid_len = 0; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci int len = get_unaligned_le16(pos); 63262306a36Sopenharmony_ci pos += 2; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* BSSID */ 63562306a36Sopenharmony_ci bssid = pos; 63662306a36Sopenharmony_ci pos += ETH_ALEN; 63762306a36Sopenharmony_ci /* RSSI */ 63862306a36Sopenharmony_ci rssi = *pos++; 63962306a36Sopenharmony_ci /* Packet time stamp */ 64062306a36Sopenharmony_ci pos += 8; 64162306a36Sopenharmony_ci /* Beacon interval */ 64262306a36Sopenharmony_ci intvl = get_unaligned_le16(pos); 64362306a36Sopenharmony_ci pos += 2; 64462306a36Sopenharmony_ci /* Capabilities */ 64562306a36Sopenharmony_ci capa = get_unaligned_le16(pos); 64662306a36Sopenharmony_ci pos += 2; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* To find out the channel, we must parse the IEs */ 64962306a36Sopenharmony_ci ie = pos; 65062306a36Sopenharmony_ci /* 65162306a36Sopenharmony_ci * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon 65262306a36Sopenharmony_ci * interval, capabilities 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_ci ielen = left = len - (6 + 1 + 8 + 2 + 2); 65562306a36Sopenharmony_ci while (left >= 2) { 65662306a36Sopenharmony_ci u8 id, elen; 65762306a36Sopenharmony_ci id = *pos++; 65862306a36Sopenharmony_ci elen = *pos++; 65962306a36Sopenharmony_ci left -= 2; 66062306a36Sopenharmony_ci if (elen > left) { 66162306a36Sopenharmony_ci lbs_deb_scan("scan response: invalid IE fmt\n"); 66262306a36Sopenharmony_ci goto done; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (id == WLAN_EID_DS_PARAMS) 66662306a36Sopenharmony_ci chan_no = *pos; 66762306a36Sopenharmony_ci if (id == WLAN_EID_SSID) { 66862306a36Sopenharmony_ci ssid = pos; 66962306a36Sopenharmony_ci ssid_len = elen; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci left -= elen; 67262306a36Sopenharmony_ci pos += elen; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* No channel, no luck */ 67662306a36Sopenharmony_ci if (chan_no != -1) { 67762306a36Sopenharmony_ci struct wiphy *wiphy = priv->wdev->wiphy; 67862306a36Sopenharmony_ci int freq = ieee80211_channel_to_frequency(chan_no, 67962306a36Sopenharmony_ci NL80211_BAND_2GHZ); 68062306a36Sopenharmony_ci struct ieee80211_channel *channel = 68162306a36Sopenharmony_ci ieee80211_get_channel(wiphy, freq); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %*pE, %d dBm\n", 68462306a36Sopenharmony_ci bssid, capa, chan_no, ssid_len, ssid, 68562306a36Sopenharmony_ci LBS_SCAN_RSSI_TO_MBM(rssi)/100); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (channel && 68862306a36Sopenharmony_ci !(channel->flags & IEEE80211_CHAN_DISABLED)) { 68962306a36Sopenharmony_ci bss = cfg80211_inform_bss(wiphy, channel, 69062306a36Sopenharmony_ci CFG80211_BSS_FTYPE_UNKNOWN, 69162306a36Sopenharmony_ci bssid, get_unaligned_le64(tsfdesc), 69262306a36Sopenharmony_ci capa, intvl, ie, ielen, 69362306a36Sopenharmony_ci LBS_SCAN_RSSI_TO_MBM(rssi), 69462306a36Sopenharmony_ci GFP_KERNEL); 69562306a36Sopenharmony_ci cfg80211_put_bss(wiphy, bss); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci } else 69862306a36Sopenharmony_ci lbs_deb_scan("scan response: missing BSS channel IE\n"); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci tsfdesc += 8; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci ret = 0; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci done: 70562306a36Sopenharmony_ci return ret; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci/* 71062306a36Sopenharmony_ci * Our scan command contains a TLV, consisting of a SSID TLV, a channel list 71162306a36Sopenharmony_ci * TLV, a rates TLV, and an optional WPS IE. Determine the maximum size of them: 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci#define LBS_SCAN_MAX_CMD_SIZE \ 71462306a36Sopenharmony_ci (sizeof(struct cmd_ds_802_11_scan) \ 71562306a36Sopenharmony_ci + LBS_MAX_SSID_TLV_SIZE \ 71662306a36Sopenharmony_ci + LBS_MAX_CHANNEL_LIST_TLV_SIZE \ 71762306a36Sopenharmony_ci + LBS_MAX_RATES_TLV_SIZE \ 71862306a36Sopenharmony_ci + LBS_MAX_WPS_ENROLLEE_TLV_SIZE) 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci/* 72162306a36Sopenharmony_ci * Assumes priv->scan_req is initialized and valid 72262306a36Sopenharmony_ci * Assumes priv->scan_channel is initialized 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_cistatic void lbs_scan_worker(struct work_struct *work) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct lbs_private *priv = 72762306a36Sopenharmony_ci container_of(work, struct lbs_private, scan_work.work); 72862306a36Sopenharmony_ci struct cmd_ds_802_11_scan *scan_cmd; 72962306a36Sopenharmony_ci u8 *tlv; /* pointer into our current, growing TLV storage area */ 73062306a36Sopenharmony_ci int last_channel; 73162306a36Sopenharmony_ci int running, carrier; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL); 73462306a36Sopenharmony_ci if (scan_cmd == NULL) 73562306a36Sopenharmony_ci return; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* prepare fixed part of scan command */ 73862306a36Sopenharmony_ci scan_cmd->bsstype = CMD_BSS_TYPE_ANY; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* stop network while we're away from our main channel */ 74162306a36Sopenharmony_ci running = !netif_queue_stopped(priv->dev); 74262306a36Sopenharmony_ci carrier = netif_carrier_ok(priv->dev); 74362306a36Sopenharmony_ci if (running) 74462306a36Sopenharmony_ci netif_stop_queue(priv->dev); 74562306a36Sopenharmony_ci if (carrier) 74662306a36Sopenharmony_ci netif_carrier_off(priv->dev); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* prepare fixed part of scan command */ 74962306a36Sopenharmony_ci tlv = scan_cmd->tlvbuffer; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* add SSID TLV */ 75262306a36Sopenharmony_ci if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0) 75362306a36Sopenharmony_ci tlv += lbs_add_ssid_tlv(tlv, 75462306a36Sopenharmony_ci priv->scan_req->ssids[0].ssid, 75562306a36Sopenharmony_ci priv->scan_req->ssids[0].ssid_len); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* add channel TLVs */ 75862306a36Sopenharmony_ci last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP; 75962306a36Sopenharmony_ci if (last_channel > priv->scan_req->n_channels) 76062306a36Sopenharmony_ci last_channel = priv->scan_req->n_channels; 76162306a36Sopenharmony_ci tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel, 76262306a36Sopenharmony_ci priv->scan_req->n_ssids); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* add rates TLV */ 76562306a36Sopenharmony_ci tlv += lbs_add_supported_rates_tlv(tlv); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* add optional WPS enrollee TLV */ 76862306a36Sopenharmony_ci if (priv->scan_req->ie && priv->scan_req->ie_len) 76962306a36Sopenharmony_ci tlv += lbs_add_wps_enrollee_tlv(tlv, priv->scan_req->ie, 77062306a36Sopenharmony_ci priv->scan_req->ie_len); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (priv->scan_channel < priv->scan_req->n_channels) { 77362306a36Sopenharmony_ci cancel_delayed_work(&priv->scan_work); 77462306a36Sopenharmony_ci if (netif_running(priv->dev)) 77562306a36Sopenharmony_ci queue_delayed_work(priv->work_thread, &priv->scan_work, 77662306a36Sopenharmony_ci msecs_to_jiffies(300)); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* This is the final data we are about to send */ 78062306a36Sopenharmony_ci scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd); 78162306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, 78262306a36Sopenharmony_ci sizeof(*scan_cmd)); 78362306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, 78462306a36Sopenharmony_ci tlv - scan_cmd->tlvbuffer); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr, 78762306a36Sopenharmony_ci le16_to_cpu(scan_cmd->hdr.size), 78862306a36Sopenharmony_ci lbs_ret_scan, 0); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (priv->scan_channel >= priv->scan_req->n_channels) { 79162306a36Sopenharmony_ci /* Mark scan done */ 79262306a36Sopenharmony_ci cancel_delayed_work(&priv->scan_work); 79362306a36Sopenharmony_ci lbs_scan_done(priv); 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Restart network */ 79762306a36Sopenharmony_ci if (carrier) 79862306a36Sopenharmony_ci netif_carrier_on(priv->dev); 79962306a36Sopenharmony_ci if (running && !priv->tx_pending_len) 80062306a36Sopenharmony_ci netif_wake_queue(priv->dev); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci kfree(scan_cmd); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Wake up anything waiting on scan completion */ 80562306a36Sopenharmony_ci if (priv->scan_req == NULL) { 80662306a36Sopenharmony_ci lbs_deb_scan("scan: waking up waiters\n"); 80762306a36Sopenharmony_ci wake_up_all(&priv->scan_q); 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic void _internal_start_scan(struct lbs_private *priv, bool internal, 81262306a36Sopenharmony_ci struct cfg80211_scan_request *request) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", 81562306a36Sopenharmony_ci request->n_ssids, request->n_channels, request->ie_len); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci priv->scan_channel = 0; 81862306a36Sopenharmony_ci priv->scan_req = request; 81962306a36Sopenharmony_ci priv->internal_scan = internal; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci queue_delayed_work(priv->work_thread, &priv->scan_work, 82262306a36Sopenharmony_ci msecs_to_jiffies(50)); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/* 82662306a36Sopenharmony_ci * Clean up priv->scan_req. Should be used to handle the allocation details. 82762306a36Sopenharmony_ci */ 82862306a36Sopenharmony_civoid lbs_scan_done(struct lbs_private *priv) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci WARN_ON(!priv->scan_req); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (priv->internal_scan) { 83362306a36Sopenharmony_ci kfree(priv->scan_req); 83462306a36Sopenharmony_ci } else { 83562306a36Sopenharmony_ci struct cfg80211_scan_info info = { 83662306a36Sopenharmony_ci .aborted = false, 83762306a36Sopenharmony_ci }; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci cfg80211_scan_done(priv->scan_req, &info); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci priv->scan_req = NULL; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic int lbs_cfg_scan(struct wiphy *wiphy, 84662306a36Sopenharmony_ci struct cfg80211_scan_request *request) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 84962306a36Sopenharmony_ci int ret = 0; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (priv->scan_req || delayed_work_pending(&priv->scan_work)) { 85262306a36Sopenharmony_ci /* old scan request not yet processed */ 85362306a36Sopenharmony_ci ret = -EAGAIN; 85462306a36Sopenharmony_ci goto out; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci _internal_start_scan(priv, false, request); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (priv->surpriseremoved) 86062306a36Sopenharmony_ci ret = -EIO; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci out: 86362306a36Sopenharmony_ci return ret; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/* 87062306a36Sopenharmony_ci * Events 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_civoid lbs_send_disconnect_notification(struct lbs_private *priv, 87462306a36Sopenharmony_ci bool locally_generated) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci cfg80211_disconnected(priv->dev, 0, NULL, 0, locally_generated, 87762306a36Sopenharmony_ci GFP_KERNEL); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_civoid lbs_send_mic_failureevent(struct lbs_private *priv, u32 event) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci cfg80211_michael_mic_failure(priv->dev, 88362306a36Sopenharmony_ci priv->assoc_bss, 88462306a36Sopenharmony_ci event == MACREG_INT_CODE_MIC_ERR_MULTICAST ? 88562306a36Sopenharmony_ci NL80211_KEYTYPE_GROUP : 88662306a36Sopenharmony_ci NL80211_KEYTYPE_PAIRWISE, 88762306a36Sopenharmony_ci -1, 88862306a36Sopenharmony_ci NULL, 88962306a36Sopenharmony_ci GFP_KERNEL); 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci/* 89662306a36Sopenharmony_ci * Connect/disconnect 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci/* 90162306a36Sopenharmony_ci * This removes all WEP keys 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_cistatic int lbs_remove_wep_keys(struct lbs_private *priv) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct cmd_ds_802_11_set_wep cmd; 90662306a36Sopenharmony_ci int ret; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 90962306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 91062306a36Sopenharmony_ci cmd.keyindex = cpu_to_le16(priv->wep_tx_key); 91162306a36Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_REMOVE); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci return ret; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci/* 91962306a36Sopenharmony_ci * Set WEP keys 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_cistatic int lbs_set_wep_keys(struct lbs_private *priv) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct cmd_ds_802_11_set_wep cmd; 92462306a36Sopenharmony_ci int i; 92562306a36Sopenharmony_ci int ret; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* 92862306a36Sopenharmony_ci * command 13 00 92962306a36Sopenharmony_ci * size 50 00 93062306a36Sopenharmony_ci * sequence xx xx 93162306a36Sopenharmony_ci * result 00 00 93262306a36Sopenharmony_ci * action 02 00 ACT_ADD 93362306a36Sopenharmony_ci * transmit key 00 00 93462306a36Sopenharmony_ci * type for key 1 01 WEP40 93562306a36Sopenharmony_ci * type for key 2 00 93662306a36Sopenharmony_ci * type for key 3 00 93762306a36Sopenharmony_ci * type for key 4 00 93862306a36Sopenharmony_ci * key 1 39 39 39 39 39 00 00 00 93962306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 94062306a36Sopenharmony_ci * key 2 00 00 00 00 00 00 00 00 94162306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 94262306a36Sopenharmony_ci * key 3 00 00 00 00 00 00 00 00 94362306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 94462306a36Sopenharmony_ci * key 4 00 00 00 00 00 00 00 00 94562306a36Sopenharmony_ci */ 94662306a36Sopenharmony_ci if (priv->wep_key_len[0] || priv->wep_key_len[1] || 94762306a36Sopenharmony_ci priv->wep_key_len[2] || priv->wep_key_len[3]) { 94862306a36Sopenharmony_ci /* Only set wep keys if we have at least one of them */ 94962306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 95062306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 95162306a36Sopenharmony_ci cmd.keyindex = cpu_to_le16(priv->wep_tx_key); 95262306a36Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_ADD); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 95562306a36Sopenharmony_ci switch (priv->wep_key_len[i]) { 95662306a36Sopenharmony_ci case WLAN_KEY_LEN_WEP40: 95762306a36Sopenharmony_ci cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; 95862306a36Sopenharmony_ci break; 95962306a36Sopenharmony_ci case WLAN_KEY_LEN_WEP104: 96062306a36Sopenharmony_ci cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; 96162306a36Sopenharmony_ci break; 96262306a36Sopenharmony_ci default: 96362306a36Sopenharmony_ci cmd.keytype[i] = 0; 96462306a36Sopenharmony_ci break; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci memcpy(cmd.keymaterial[i], priv->wep_key[i], 96762306a36Sopenharmony_ci priv->wep_key_len[i]); 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); 97162306a36Sopenharmony_ci } else { 97262306a36Sopenharmony_ci /* Otherwise remove all wep keys */ 97362306a36Sopenharmony_ci ret = lbs_remove_wep_keys(priv); 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci return ret; 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/* 98162306a36Sopenharmony_ci * Enable/Disable RSN status 98262306a36Sopenharmony_ci */ 98362306a36Sopenharmony_cistatic int lbs_enable_rsn(struct lbs_private *priv, int enable) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci struct cmd_ds_802_11_enable_rsn cmd; 98662306a36Sopenharmony_ci int ret; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* 98962306a36Sopenharmony_ci * cmd 2f 00 99062306a36Sopenharmony_ci * size 0c 00 99162306a36Sopenharmony_ci * sequence xx xx 99262306a36Sopenharmony_ci * result 00 00 99362306a36Sopenharmony_ci * action 01 00 ACT_SET 99462306a36Sopenharmony_ci * enable 01 00 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 99762306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 99862306a36Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 99962306a36Sopenharmony_ci cmd.enable = cpu_to_le16(enable); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return ret; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci/* 100862306a36Sopenharmony_ci * Set WPA/WPA key material 100962306a36Sopenharmony_ci */ 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci/* 101262306a36Sopenharmony_ci * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we 101362306a36Sopenharmony_ci * get rid of WEXT, this should go into host.h 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistruct cmd_key_material { 101762306a36Sopenharmony_ci struct cmd_header hdr; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci __le16 action; 102062306a36Sopenharmony_ci struct MrvlIEtype_keyParamSet param; 102162306a36Sopenharmony_ci} __packed; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int lbs_set_key_material(struct lbs_private *priv, 102462306a36Sopenharmony_ci int key_type, int key_info, 102562306a36Sopenharmony_ci const u8 *key, u16 key_len) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct cmd_key_material cmd; 102862306a36Sopenharmony_ci int ret; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* 103162306a36Sopenharmony_ci * Example for WPA (TKIP): 103262306a36Sopenharmony_ci * 103362306a36Sopenharmony_ci * cmd 5e 00 103462306a36Sopenharmony_ci * size 34 00 103562306a36Sopenharmony_ci * sequence xx xx 103662306a36Sopenharmony_ci * result 00 00 103762306a36Sopenharmony_ci * action 01 00 103862306a36Sopenharmony_ci * TLV type 00 01 key param 103962306a36Sopenharmony_ci * length 00 26 104062306a36Sopenharmony_ci * key type 01 00 TKIP 104162306a36Sopenharmony_ci * key info 06 00 UNICAST | ENABLED 104262306a36Sopenharmony_ci * key len 20 00 104362306a36Sopenharmony_ci * key 32 bytes 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 104662306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 104762306a36Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 104862306a36Sopenharmony_ci cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); 104962306a36Sopenharmony_ci cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4); 105062306a36Sopenharmony_ci cmd.param.keytypeid = cpu_to_le16(key_type); 105162306a36Sopenharmony_ci cmd.param.keyinfo = cpu_to_le16(key_info); 105262306a36Sopenharmony_ci cmd.param.keylen = cpu_to_le16(key_len); 105362306a36Sopenharmony_ci if (key && key_len) 105462306a36Sopenharmony_ci memcpy(cmd.param.key, key, key_len); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci return ret; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci/* 106362306a36Sopenharmony_ci * Sets the auth type (open, shared, etc) in the firmware. That 106462306a36Sopenharmony_ci * we use CMD_802_11_AUTHENTICATE is misleading, this firmware 106562306a36Sopenharmony_ci * command doesn't send an authentication frame at all, it just 106662306a36Sopenharmony_ci * stores the auth_type. 106762306a36Sopenharmony_ci */ 106862306a36Sopenharmony_cistatic int lbs_set_authtype(struct lbs_private *priv, 106962306a36Sopenharmony_ci struct cfg80211_connect_params *sme) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci struct cmd_ds_802_11_authenticate cmd; 107262306a36Sopenharmony_ci int ret; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* 107562306a36Sopenharmony_ci * cmd 11 00 107662306a36Sopenharmony_ci * size 19 00 107762306a36Sopenharmony_ci * sequence xx xx 107862306a36Sopenharmony_ci * result 00 00 107962306a36Sopenharmony_ci * BSS id 00 13 19 80 da 30 108062306a36Sopenharmony_ci * auth type 00 108162306a36Sopenharmony_ci * reserved 00 00 00 00 00 00 00 00 00 00 108262306a36Sopenharmony_ci */ 108362306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 108462306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 108562306a36Sopenharmony_ci if (sme->bssid) 108662306a36Sopenharmony_ci memcpy(cmd.bssid, sme->bssid, ETH_ALEN); 108762306a36Sopenharmony_ci /* convert auth_type */ 108862306a36Sopenharmony_ci ret = lbs_auth_to_authtype(sme->auth_type); 108962306a36Sopenharmony_ci if (ret < 0) 109062306a36Sopenharmony_ci goto done; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci cmd.authtype = ret; 109362306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci done: 109662306a36Sopenharmony_ci return ret; 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci/* 110162306a36Sopenharmony_ci * Create association request 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ci#define LBS_ASSOC_MAX_CMD_SIZE \ 110462306a36Sopenharmony_ci (sizeof(struct cmd_ds_802_11_associate) \ 110562306a36Sopenharmony_ci + LBS_MAX_SSID_TLV_SIZE \ 110662306a36Sopenharmony_ci + LBS_MAX_CHANNEL_TLV_SIZE \ 110762306a36Sopenharmony_ci + LBS_MAX_CF_PARAM_TLV_SIZE \ 110862306a36Sopenharmony_ci + LBS_MAX_AUTH_TYPE_TLV_SIZE \ 110962306a36Sopenharmony_ci + LBS_MAX_WPA_TLV_SIZE) 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic int lbs_associate(struct lbs_private *priv, 111262306a36Sopenharmony_ci struct cfg80211_bss *bss, 111362306a36Sopenharmony_ci struct cfg80211_connect_params *sme) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct cmd_ds_802_11_associate_response *resp; 111662306a36Sopenharmony_ci struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE, 111762306a36Sopenharmony_ci GFP_KERNEL); 111862306a36Sopenharmony_ci const u8 *ssid_eid; 111962306a36Sopenharmony_ci size_t len, resp_ie_len; 112062306a36Sopenharmony_ci int status; 112162306a36Sopenharmony_ci int ret; 112262306a36Sopenharmony_ci u8 *pos; 112362306a36Sopenharmony_ci u8 *tmp; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (!cmd) { 112662306a36Sopenharmony_ci ret = -ENOMEM; 112762306a36Sopenharmony_ci goto done; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci pos = &cmd->iebuf[0]; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* 113262306a36Sopenharmony_ci * cmd 50 00 113362306a36Sopenharmony_ci * length 34 00 113462306a36Sopenharmony_ci * sequence xx xx 113562306a36Sopenharmony_ci * result 00 00 113662306a36Sopenharmony_ci * BSS id 00 13 19 80 da 30 113762306a36Sopenharmony_ci * capabilities 11 00 113862306a36Sopenharmony_ci * listen interval 0a 00 113962306a36Sopenharmony_ci * beacon interval 00 00 114062306a36Sopenharmony_ci * DTIM period 00 114162306a36Sopenharmony_ci * TLVs xx (up to 512 bytes) 114262306a36Sopenharmony_ci */ 114362306a36Sopenharmony_ci cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* Fill in static fields */ 114662306a36Sopenharmony_ci memcpy(cmd->bssid, bss->bssid, ETH_ALEN); 114762306a36Sopenharmony_ci cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); 114862306a36Sopenharmony_ci cmd->capability = cpu_to_le16(bss->capability); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci /* add SSID TLV */ 115162306a36Sopenharmony_ci rcu_read_lock(); 115262306a36Sopenharmony_ci ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); 115362306a36Sopenharmony_ci if (ssid_eid) 115462306a36Sopenharmony_ci pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); 115562306a36Sopenharmony_ci else 115662306a36Sopenharmony_ci lbs_deb_assoc("no SSID\n"); 115762306a36Sopenharmony_ci rcu_read_unlock(); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci /* add DS param TLV */ 116062306a36Sopenharmony_ci if (bss->channel) 116162306a36Sopenharmony_ci pos += lbs_add_channel_tlv(pos, bss->channel->hw_value); 116262306a36Sopenharmony_ci else 116362306a36Sopenharmony_ci lbs_deb_assoc("no channel\n"); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci /* add (empty) CF param TLV */ 116662306a36Sopenharmony_ci pos += lbs_add_cf_param_tlv(pos); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci /* add rates TLV */ 116962306a36Sopenharmony_ci tmp = pos + 4; /* skip Marvell IE header */ 117062306a36Sopenharmony_ci pos += lbs_add_common_rates_tlv(pos, bss); 117162306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* add auth type TLV */ 117462306a36Sopenharmony_ci if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9) 117562306a36Sopenharmony_ci pos += lbs_add_auth_type_tlv(pos, sme->auth_type); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* add WPA/WPA2 TLV */ 117862306a36Sopenharmony_ci if (sme->ie && sme->ie_len) 117962306a36Sopenharmony_ci pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci len = sizeof(*cmd) + (u16)(pos - (u8 *) &cmd->iebuf); 118262306a36Sopenharmony_ci cmd->hdr.size = cpu_to_le16(len); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd, 118562306a36Sopenharmony_ci le16_to_cpu(cmd->hdr.size)); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* store for later use */ 118862306a36Sopenharmony_ci memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd); 119162306a36Sopenharmony_ci if (ret) 119262306a36Sopenharmony_ci goto done; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* generate connect message to cfg80211 */ 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci resp = (void *) cmd; /* recast for easier field access */ 119762306a36Sopenharmony_ci status = le16_to_cpu(resp->statuscode); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci /* Older FW versions map the IEEE 802.11 Status Code in the association 120062306a36Sopenharmony_ci * response to the following values returned in resp->statuscode: 120162306a36Sopenharmony_ci * 120262306a36Sopenharmony_ci * IEEE Status Code Marvell Status Code 120362306a36Sopenharmony_ci * 0 -> 0x0000 ASSOC_RESULT_SUCCESS 120462306a36Sopenharmony_ci * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED 120562306a36Sopenharmony_ci * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED 120662306a36Sopenharmony_ci * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED 120762306a36Sopenharmony_ci * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED 120862306a36Sopenharmony_ci * others -> 0x0003 ASSOC_RESULT_REFUSED 120962306a36Sopenharmony_ci * 121062306a36Sopenharmony_ci * Other response codes: 121162306a36Sopenharmony_ci * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) 121262306a36Sopenharmony_ci * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for 121362306a36Sopenharmony_ci * association response from the AP) 121462306a36Sopenharmony_ci */ 121562306a36Sopenharmony_ci if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { 121662306a36Sopenharmony_ci switch (status) { 121762306a36Sopenharmony_ci case 0: 121862306a36Sopenharmony_ci break; 121962306a36Sopenharmony_ci case 1: 122062306a36Sopenharmony_ci lbs_deb_assoc("invalid association parameters\n"); 122162306a36Sopenharmony_ci status = WLAN_STATUS_CAPS_UNSUPPORTED; 122262306a36Sopenharmony_ci break; 122362306a36Sopenharmony_ci case 2: 122462306a36Sopenharmony_ci lbs_deb_assoc("timer expired while waiting for AP\n"); 122562306a36Sopenharmony_ci status = WLAN_STATUS_AUTH_TIMEOUT; 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci case 3: 122862306a36Sopenharmony_ci lbs_deb_assoc("association refused by AP\n"); 122962306a36Sopenharmony_ci status = WLAN_STATUS_ASSOC_DENIED_UNSPEC; 123062306a36Sopenharmony_ci break; 123162306a36Sopenharmony_ci case 4: 123262306a36Sopenharmony_ci lbs_deb_assoc("authentication refused by AP\n"); 123362306a36Sopenharmony_ci status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; 123462306a36Sopenharmony_ci break; 123562306a36Sopenharmony_ci default: 123662306a36Sopenharmony_ci lbs_deb_assoc("association failure %d\n", status); 123762306a36Sopenharmony_ci /* v5 OLPC firmware does return the AP status code if 123862306a36Sopenharmony_ci * it's not one of the values above. Let that through. 123962306a36Sopenharmony_ci */ 124062306a36Sopenharmony_ci break; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, " 124562306a36Sopenharmony_ci "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode), 124662306a36Sopenharmony_ci le16_to_cpu(resp->capability), le16_to_cpu(resp->aid)); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci resp_ie_len = le16_to_cpu(resp->hdr.size) 124962306a36Sopenharmony_ci - sizeof(resp->hdr) 125062306a36Sopenharmony_ci - 6; 125162306a36Sopenharmony_ci cfg80211_connect_result(priv->dev, 125262306a36Sopenharmony_ci priv->assoc_bss, 125362306a36Sopenharmony_ci sme->ie, sme->ie_len, 125462306a36Sopenharmony_ci resp->iebuf, resp_ie_len, 125562306a36Sopenharmony_ci status, 125662306a36Sopenharmony_ci GFP_KERNEL); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (status == 0) { 125962306a36Sopenharmony_ci /* TODO: get rid of priv->connect_status */ 126062306a36Sopenharmony_ci priv->connect_status = LBS_CONNECTED; 126162306a36Sopenharmony_ci netif_carrier_on(priv->dev); 126262306a36Sopenharmony_ci if (!priv->tx_pending_len) 126362306a36Sopenharmony_ci netif_tx_wake_all_queues(priv->dev); 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci kfree(cmd); 126762306a36Sopenharmony_cidone: 126862306a36Sopenharmony_ci return ret; 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cistatic struct cfg80211_scan_request * 127262306a36Sopenharmony_ci_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci struct cfg80211_scan_request *creq = NULL; 127562306a36Sopenharmony_ci int i, n_channels = ieee80211_get_num_supported_channels(wiphy); 127662306a36Sopenharmony_ci enum nl80211_band band; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + 127962306a36Sopenharmony_ci n_channels * sizeof(void *), 128062306a36Sopenharmony_ci GFP_ATOMIC); 128162306a36Sopenharmony_ci if (!creq) 128262306a36Sopenharmony_ci return NULL; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* SSIDs come after channels */ 128562306a36Sopenharmony_ci creq->ssids = (void *)&creq->channels[n_channels]; 128662306a36Sopenharmony_ci creq->n_channels = n_channels; 128762306a36Sopenharmony_ci creq->n_ssids = 1; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci /* Scan all available channels */ 129062306a36Sopenharmony_ci i = 0; 129162306a36Sopenharmony_ci for (band = 0; band < NUM_NL80211_BANDS; band++) { 129262306a36Sopenharmony_ci int j; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (!wiphy->bands[band]) 129562306a36Sopenharmony_ci continue; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci for (j = 0; j < wiphy->bands[band]->n_channels; j++) { 129862306a36Sopenharmony_ci /* ignore disabled channels */ 129962306a36Sopenharmony_ci if (wiphy->bands[band]->channels[j].flags & 130062306a36Sopenharmony_ci IEEE80211_CHAN_DISABLED) 130162306a36Sopenharmony_ci continue; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci creq->channels[i] = &wiphy->bands[band]->channels[j]; 130462306a36Sopenharmony_ci i++; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci if (i) { 130862306a36Sopenharmony_ci /* Set real number of channels specified in creq->channels[] */ 130962306a36Sopenharmony_ci creq->n_channels = i; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci /* Scan for the SSID we're going to connect to */ 131262306a36Sopenharmony_ci memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len); 131362306a36Sopenharmony_ci creq->ssids[0].ssid_len = sme->ssid_len; 131462306a36Sopenharmony_ci } else { 131562306a36Sopenharmony_ci /* No channels found... */ 131662306a36Sopenharmony_ci kfree(creq); 131762306a36Sopenharmony_ci creq = NULL; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci return creq; 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cistatic int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, 132462306a36Sopenharmony_ci struct cfg80211_connect_params *sme) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 132762306a36Sopenharmony_ci struct cfg80211_bss *bss = NULL; 132862306a36Sopenharmony_ci int ret = 0; 132962306a36Sopenharmony_ci u8 preamble = RADIO_PREAMBLE_SHORT; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (dev == priv->mesh_dev) 133262306a36Sopenharmony_ci return -EOPNOTSUPP; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci if (!sme->bssid) { 133562306a36Sopenharmony_ci struct cfg80211_scan_request *creq; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* 133862306a36Sopenharmony_ci * Scan for the requested network after waiting for existing 133962306a36Sopenharmony_ci * scans to finish. 134062306a36Sopenharmony_ci */ 134162306a36Sopenharmony_ci lbs_deb_assoc("assoc: waiting for existing scans\n"); 134262306a36Sopenharmony_ci wait_event_interruptible_timeout(priv->scan_q, 134362306a36Sopenharmony_ci (priv->scan_req == NULL), 134462306a36Sopenharmony_ci (15 * HZ)); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci creq = _new_connect_scan_req(wiphy, sme); 134762306a36Sopenharmony_ci if (!creq) { 134862306a36Sopenharmony_ci ret = -EINVAL; 134962306a36Sopenharmony_ci goto done; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci lbs_deb_assoc("assoc: scanning for compatible AP\n"); 135362306a36Sopenharmony_ci _internal_start_scan(priv, true, creq); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci lbs_deb_assoc("assoc: waiting for scan to complete\n"); 135662306a36Sopenharmony_ci wait_event_interruptible_timeout(priv->scan_q, 135762306a36Sopenharmony_ci (priv->scan_req == NULL), 135862306a36Sopenharmony_ci (15 * HZ)); 135962306a36Sopenharmony_ci lbs_deb_assoc("assoc: scanning completed\n"); 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci /* Find the BSS we want using available scan results */ 136362306a36Sopenharmony_ci bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, 136462306a36Sopenharmony_ci sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS, 136562306a36Sopenharmony_ci IEEE80211_PRIVACY_ANY); 136662306a36Sopenharmony_ci if (!bss) { 136762306a36Sopenharmony_ci wiphy_err(wiphy, "assoc: bss %pM not in scan results\n", 136862306a36Sopenharmony_ci sme->bssid); 136962306a36Sopenharmony_ci ret = -ENOENT; 137062306a36Sopenharmony_ci goto done; 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci lbs_deb_assoc("trying %pM\n", bss->bssid); 137362306a36Sopenharmony_ci lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n", 137462306a36Sopenharmony_ci sme->crypto.cipher_group, 137562306a36Sopenharmony_ci sme->key_idx, sme->key_len); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* As this is a new connection, clear locally stored WEP keys */ 137862306a36Sopenharmony_ci priv->wep_tx_key = 0; 137962306a36Sopenharmony_ci memset(priv->wep_key, 0, sizeof(priv->wep_key)); 138062306a36Sopenharmony_ci memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* set/remove WEP keys */ 138362306a36Sopenharmony_ci switch (sme->crypto.cipher_group) { 138462306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 138562306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 138662306a36Sopenharmony_ci /* Store provided WEP keys in priv-> */ 138762306a36Sopenharmony_ci priv->wep_tx_key = sme->key_idx; 138862306a36Sopenharmony_ci priv->wep_key_len[sme->key_idx] = sme->key_len; 138962306a36Sopenharmony_ci memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len); 139062306a36Sopenharmony_ci /* Set WEP keys and WEP mode */ 139162306a36Sopenharmony_ci lbs_set_wep_keys(priv); 139262306a36Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; 139362306a36Sopenharmony_ci lbs_set_mac_control(priv); 139462306a36Sopenharmony_ci /* No RSN mode for WEP */ 139562306a36Sopenharmony_ci lbs_enable_rsn(priv, 0); 139662306a36Sopenharmony_ci break; 139762306a36Sopenharmony_ci case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */ 139862306a36Sopenharmony_ci /* 139962306a36Sopenharmony_ci * If we don't have no WEP, no WPA and no WPA2, 140062306a36Sopenharmony_ci * we remove all keys like in the WPA/WPA2 setup, 140162306a36Sopenharmony_ci * we just don't set RSN. 140262306a36Sopenharmony_ci * 140362306a36Sopenharmony_ci * Therefore: fall-through 140462306a36Sopenharmony_ci */ 140562306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 140662306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 140762306a36Sopenharmony_ci /* Remove WEP keys and WEP mode */ 140862306a36Sopenharmony_ci lbs_remove_wep_keys(priv); 140962306a36Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; 141062306a36Sopenharmony_ci lbs_set_mac_control(priv); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci /* clear the WPA/WPA2 keys */ 141362306a36Sopenharmony_ci lbs_set_key_material(priv, 141462306a36Sopenharmony_ci KEY_TYPE_ID_WEP, /* doesn't matter */ 141562306a36Sopenharmony_ci KEY_INFO_WPA_UNICAST, 141662306a36Sopenharmony_ci NULL, 0); 141762306a36Sopenharmony_ci lbs_set_key_material(priv, 141862306a36Sopenharmony_ci KEY_TYPE_ID_WEP, /* doesn't matter */ 141962306a36Sopenharmony_ci KEY_INFO_WPA_MCAST, 142062306a36Sopenharmony_ci NULL, 0); 142162306a36Sopenharmony_ci /* RSN mode for WPA/WPA2 */ 142262306a36Sopenharmony_ci lbs_enable_rsn(priv, sme->crypto.cipher_group != 0); 142362306a36Sopenharmony_ci break; 142462306a36Sopenharmony_ci default: 142562306a36Sopenharmony_ci wiphy_err(wiphy, "unsupported cipher group 0x%x\n", 142662306a36Sopenharmony_ci sme->crypto.cipher_group); 142762306a36Sopenharmony_ci ret = -ENOTSUPP; 142862306a36Sopenharmony_ci goto done; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci ret = lbs_set_authtype(priv, sme); 143262306a36Sopenharmony_ci if (ret == -ENOTSUPP) { 143362306a36Sopenharmony_ci wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type); 143462306a36Sopenharmony_ci goto done; 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci lbs_set_radio(priv, preamble, 1); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci /* Do the actual association */ 144062306a36Sopenharmony_ci ret = lbs_associate(priv, bss, sme); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci done: 144362306a36Sopenharmony_ci if (bss) 144462306a36Sopenharmony_ci cfg80211_put_bss(wiphy, bss); 144562306a36Sopenharmony_ci return ret; 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ciint lbs_disconnect(struct lbs_private *priv, u16 reason) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci struct cmd_ds_802_11_deauthenticate cmd; 145162306a36Sopenharmony_ci int ret; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 145462306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 145562306a36Sopenharmony_ci /* Mildly ugly to use a locally store my own BSSID ... */ 145662306a36Sopenharmony_ci memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN); 145762306a36Sopenharmony_ci cmd.reasoncode = cpu_to_le16(reason); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); 146062306a36Sopenharmony_ci if (ret) 146162306a36Sopenharmony_ci return ret; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci cfg80211_disconnected(priv->dev, 146462306a36Sopenharmony_ci reason, 146562306a36Sopenharmony_ci NULL, 0, true, 146662306a36Sopenharmony_ci GFP_KERNEL); 146762306a36Sopenharmony_ci priv->connect_status = LBS_DISCONNECTED; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci return 0; 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_cistatic int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, 147362306a36Sopenharmony_ci u16 reason_code) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (dev == priv->mesh_dev) 147862306a36Sopenharmony_ci return -EOPNOTSUPP; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci /* store for lbs_cfg_ret_disconnect() */ 148162306a36Sopenharmony_ci priv->disassoc_reason = reason_code; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci return lbs_disconnect(priv, reason_code); 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic int lbs_cfg_set_default_key(struct wiphy *wiphy, 148762306a36Sopenharmony_ci struct net_device *netdev, int link_id, 148862306a36Sopenharmony_ci u8 key_index, bool unicast, 148962306a36Sopenharmony_ci bool multicast) 149062306a36Sopenharmony_ci{ 149162306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci if (netdev == priv->mesh_dev) 149462306a36Sopenharmony_ci return -EOPNOTSUPP; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (key_index != priv->wep_tx_key) { 149762306a36Sopenharmony_ci lbs_deb_assoc("set_default_key: to %d\n", key_index); 149862306a36Sopenharmony_ci priv->wep_tx_key = key_index; 149962306a36Sopenharmony_ci lbs_set_wep_keys(priv); 150062306a36Sopenharmony_ci } 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci return 0; 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_cistatic int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev, 150762306a36Sopenharmony_ci int link_id, u8 idx, bool pairwise, 150862306a36Sopenharmony_ci const u8 *mac_addr, struct key_params *params) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 151162306a36Sopenharmony_ci u16 key_info; 151262306a36Sopenharmony_ci u16 key_type; 151362306a36Sopenharmony_ci int ret = 0; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci if (netdev == priv->mesh_dev) 151662306a36Sopenharmony_ci return -EOPNOTSUPP; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n", 151962306a36Sopenharmony_ci params->cipher, mac_addr); 152062306a36Sopenharmony_ci lbs_deb_assoc("add_key: key index %d, key len %d\n", 152162306a36Sopenharmony_ci idx, params->key_len); 152262306a36Sopenharmony_ci if (params->key_len) 152362306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_CFG80211, "KEY", 152462306a36Sopenharmony_ci params->key, params->key_len); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci lbs_deb_assoc("add_key: seq len %d\n", params->seq_len); 152762306a36Sopenharmony_ci if (params->seq_len) 152862306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_CFG80211, "SEQ", 152962306a36Sopenharmony_ci params->seq, params->seq_len); 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci switch (params->cipher) { 153262306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 153362306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 153462306a36Sopenharmony_ci /* actually compare if something has changed ... */ 153562306a36Sopenharmony_ci if ((priv->wep_key_len[idx] != params->key_len) || 153662306a36Sopenharmony_ci memcmp(priv->wep_key[idx], 153762306a36Sopenharmony_ci params->key, params->key_len) != 0) { 153862306a36Sopenharmony_ci priv->wep_key_len[idx] = params->key_len; 153962306a36Sopenharmony_ci memcpy(priv->wep_key[idx], 154062306a36Sopenharmony_ci params->key, params->key_len); 154162306a36Sopenharmony_ci lbs_set_wep_keys(priv); 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci break; 154462306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 154562306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 154662306a36Sopenharmony_ci key_info = KEY_INFO_WPA_ENABLED | ((idx == 0) 154762306a36Sopenharmony_ci ? KEY_INFO_WPA_UNICAST 154862306a36Sopenharmony_ci : KEY_INFO_WPA_MCAST); 154962306a36Sopenharmony_ci key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP) 155062306a36Sopenharmony_ci ? KEY_TYPE_ID_TKIP 155162306a36Sopenharmony_ci : KEY_TYPE_ID_AES; 155262306a36Sopenharmony_ci lbs_set_key_material(priv, 155362306a36Sopenharmony_ci key_type, 155462306a36Sopenharmony_ci key_info, 155562306a36Sopenharmony_ci params->key, params->key_len); 155662306a36Sopenharmony_ci break; 155762306a36Sopenharmony_ci default: 155862306a36Sopenharmony_ci wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher); 155962306a36Sopenharmony_ci ret = -ENOTSUPP; 156062306a36Sopenharmony_ci break; 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci return ret; 156462306a36Sopenharmony_ci} 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_cistatic int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, 156862306a36Sopenharmony_ci int link_id, u8 key_index, bool pairwise, 156962306a36Sopenharmony_ci const u8 *mac_addr) 157062306a36Sopenharmony_ci{ 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n", 157362306a36Sopenharmony_ci key_index, mac_addr); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci#ifdef TODO 157662306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 157762306a36Sopenharmony_ci /* 157862306a36Sopenharmony_ci * I think can keep this a NO-OP, because: 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci * - we clear all keys whenever we do lbs_cfg_connect() anyway 158162306a36Sopenharmony_ci * - neither "iw" nor "wpa_supplicant" won't call this during 158262306a36Sopenharmony_ci * an ongoing connection 158362306a36Sopenharmony_ci * - TODO: but I have to check if this is still true when 158462306a36Sopenharmony_ci * I set the AP to periodic re-keying 158562306a36Sopenharmony_ci * - we've not kzallec() something when we've added a key at 158662306a36Sopenharmony_ci * lbs_cfg_connect() or lbs_cfg_add_key(). 158762306a36Sopenharmony_ci * 158862306a36Sopenharmony_ci * This causes lbs_cfg_del_key() only called at disconnect time, 158962306a36Sopenharmony_ci * where we'd just waste time deleting a key that is not going 159062306a36Sopenharmony_ci * to be used anyway. 159162306a36Sopenharmony_ci */ 159262306a36Sopenharmony_ci if (key_index < 3 && priv->wep_key_len[key_index]) { 159362306a36Sopenharmony_ci priv->wep_key_len[key_index] = 0; 159462306a36Sopenharmony_ci lbs_set_wep_keys(priv); 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci#endif 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci return 0; 159962306a36Sopenharmony_ci} 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci/* 160362306a36Sopenharmony_ci * Get station 160462306a36Sopenharmony_ci */ 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistatic int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, 160762306a36Sopenharmony_ci const u8 *mac, struct station_info *sinfo) 160862306a36Sopenharmony_ci{ 160962306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 161062306a36Sopenharmony_ci s8 signal, noise; 161162306a36Sopenharmony_ci int ret; 161262306a36Sopenharmony_ci size_t i; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES) | 161562306a36Sopenharmony_ci BIT_ULL(NL80211_STA_INFO_TX_PACKETS) | 161662306a36Sopenharmony_ci BIT_ULL(NL80211_STA_INFO_RX_BYTES) | 161762306a36Sopenharmony_ci BIT_ULL(NL80211_STA_INFO_RX_PACKETS); 161862306a36Sopenharmony_ci sinfo->tx_bytes = priv->dev->stats.tx_bytes; 161962306a36Sopenharmony_ci sinfo->tx_packets = priv->dev->stats.tx_packets; 162062306a36Sopenharmony_ci sinfo->rx_bytes = priv->dev->stats.rx_bytes; 162162306a36Sopenharmony_ci sinfo->rx_packets = priv->dev->stats.rx_packets; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* Get current RSSI */ 162462306a36Sopenharmony_ci ret = lbs_get_rssi(priv, &signal, &noise); 162562306a36Sopenharmony_ci if (ret == 0) { 162662306a36Sopenharmony_ci sinfo->signal = signal; 162762306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci /* Convert priv->cur_rate from hw_value to NL80211 value */ 163162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { 163262306a36Sopenharmony_ci if (priv->cur_rate == lbs_rates[i].hw_value) { 163362306a36Sopenharmony_ci sinfo->txrate.legacy = lbs_rates[i].bitrate; 163462306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 163562306a36Sopenharmony_ci break; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci return 0; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci/* 164662306a36Sopenharmony_ci * Change interface 164762306a36Sopenharmony_ci */ 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_cistatic int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, 165062306a36Sopenharmony_ci enum nl80211_iftype type, 165162306a36Sopenharmony_ci struct vif_params *params) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 165462306a36Sopenharmony_ci int ret = 0; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci if (dev == priv->mesh_dev) 165762306a36Sopenharmony_ci return -EOPNOTSUPP; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci switch (type) { 166062306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 166162306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 166262306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 166362306a36Sopenharmony_ci break; 166462306a36Sopenharmony_ci default: 166562306a36Sopenharmony_ci return -EOPNOTSUPP; 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci if (priv->iface_running) 166962306a36Sopenharmony_ci ret = lbs_set_iface_type(priv, type); 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if (!ret) 167262306a36Sopenharmony_ci priv->wdev->iftype = type; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci return ret; 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci/* 168062306a36Sopenharmony_ci * IBSS (Ad-Hoc) 168162306a36Sopenharmony_ci */ 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci/* 168462306a36Sopenharmony_ci * The firmware needs the following bits masked out of the beacon-derived 168562306a36Sopenharmony_ci * capability field when associating/joining to a BSS: 168662306a36Sopenharmony_ci * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused) 168762306a36Sopenharmony_ci */ 168862306a36Sopenharmony_ci#define CAPINFO_MASK (~(0xda00)) 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_cistatic void lbs_join_post(struct lbs_private *priv, 169262306a36Sopenharmony_ci struct cfg80211_ibss_params *params, 169362306a36Sopenharmony_ci u8 *bssid, u16 capability) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */ 169662306a36Sopenharmony_ci 2 + 4 + /* basic rates */ 169762306a36Sopenharmony_ci 2 + 1 + /* DS parameter */ 169862306a36Sopenharmony_ci 2 + 2 + /* atim */ 169962306a36Sopenharmony_ci 2 + 8]; /* extended rates */ 170062306a36Sopenharmony_ci u8 *fake = fake_ie; 170162306a36Sopenharmony_ci struct cfg80211_bss *bss; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci /* 170462306a36Sopenharmony_ci * For cfg80211_inform_bss, we'll need a fake IE, as we can't get 170562306a36Sopenharmony_ci * the real IE from the firmware. So we fabricate a fake IE based on 170662306a36Sopenharmony_ci * what the firmware actually sends (sniffed with wireshark). 170762306a36Sopenharmony_ci */ 170862306a36Sopenharmony_ci /* Fake SSID IE */ 170962306a36Sopenharmony_ci *fake++ = WLAN_EID_SSID; 171062306a36Sopenharmony_ci *fake++ = params->ssid_len; 171162306a36Sopenharmony_ci memcpy(fake, params->ssid, params->ssid_len); 171262306a36Sopenharmony_ci fake += params->ssid_len; 171362306a36Sopenharmony_ci /* Fake supported basic rates IE */ 171462306a36Sopenharmony_ci *fake++ = WLAN_EID_SUPP_RATES; 171562306a36Sopenharmony_ci *fake++ = 4; 171662306a36Sopenharmony_ci *fake++ = 0x82; 171762306a36Sopenharmony_ci *fake++ = 0x84; 171862306a36Sopenharmony_ci *fake++ = 0x8b; 171962306a36Sopenharmony_ci *fake++ = 0x96; 172062306a36Sopenharmony_ci /* Fake DS channel IE */ 172162306a36Sopenharmony_ci *fake++ = WLAN_EID_DS_PARAMS; 172262306a36Sopenharmony_ci *fake++ = 1; 172362306a36Sopenharmony_ci *fake++ = params->chandef.chan->hw_value; 172462306a36Sopenharmony_ci /* Fake IBSS params IE */ 172562306a36Sopenharmony_ci *fake++ = WLAN_EID_IBSS_PARAMS; 172662306a36Sopenharmony_ci *fake++ = 2; 172762306a36Sopenharmony_ci *fake++ = 0; /* ATIM=0 */ 172862306a36Sopenharmony_ci *fake++ = 0; 172962306a36Sopenharmony_ci /* Fake extended rates IE, TODO: don't add this for 802.11b only, 173062306a36Sopenharmony_ci * but I don't know how this could be checked */ 173162306a36Sopenharmony_ci *fake++ = WLAN_EID_EXT_SUPP_RATES; 173262306a36Sopenharmony_ci *fake++ = 8; 173362306a36Sopenharmony_ci *fake++ = 0x0c; 173462306a36Sopenharmony_ci *fake++ = 0x12; 173562306a36Sopenharmony_ci *fake++ = 0x18; 173662306a36Sopenharmony_ci *fake++ = 0x24; 173762306a36Sopenharmony_ci *fake++ = 0x30; 173862306a36Sopenharmony_ci *fake++ = 0x48; 173962306a36Sopenharmony_ci *fake++ = 0x60; 174062306a36Sopenharmony_ci *fake++ = 0x6c; 174162306a36Sopenharmony_ci lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci bss = cfg80211_inform_bss(priv->wdev->wiphy, 174462306a36Sopenharmony_ci params->chandef.chan, 174562306a36Sopenharmony_ci CFG80211_BSS_FTYPE_UNKNOWN, 174662306a36Sopenharmony_ci bssid, 174762306a36Sopenharmony_ci 0, 174862306a36Sopenharmony_ci capability, 174962306a36Sopenharmony_ci params->beacon_interval, 175062306a36Sopenharmony_ci fake_ie, fake - fake_ie, 175162306a36Sopenharmony_ci 0, GFP_KERNEL); 175262306a36Sopenharmony_ci cfg80211_put_bss(priv->wdev->wiphy, bss); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, 175562306a36Sopenharmony_ci GFP_KERNEL); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ 175862306a36Sopenharmony_ci priv->connect_status = LBS_CONNECTED; 175962306a36Sopenharmony_ci netif_carrier_on(priv->dev); 176062306a36Sopenharmony_ci if (!priv->tx_pending_len) 176162306a36Sopenharmony_ci netif_wake_queue(priv->dev); 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_cistatic int lbs_ibss_join_existing(struct lbs_private *priv, 176562306a36Sopenharmony_ci struct cfg80211_ibss_params *params, 176662306a36Sopenharmony_ci struct cfg80211_bss *bss) 176762306a36Sopenharmony_ci{ 176862306a36Sopenharmony_ci const u8 *rates_eid; 176962306a36Sopenharmony_ci struct cmd_ds_802_11_ad_hoc_join cmd; 177062306a36Sopenharmony_ci u8 preamble = RADIO_PREAMBLE_SHORT; 177162306a36Sopenharmony_ci int ret = 0; 177262306a36Sopenharmony_ci int hw, i; 177362306a36Sopenharmony_ci u8 rates_max; 177462306a36Sopenharmony_ci u8 *rates; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci /* TODO: set preamble based on scan result */ 177762306a36Sopenharmony_ci ret = lbs_set_radio(priv, preamble, 1); 177862306a36Sopenharmony_ci if (ret) 177962306a36Sopenharmony_ci goto out; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci /* 178262306a36Sopenharmony_ci * Example CMD_802_11_AD_HOC_JOIN command: 178362306a36Sopenharmony_ci * 178462306a36Sopenharmony_ci * command 2c 00 CMD_802_11_AD_HOC_JOIN 178562306a36Sopenharmony_ci * size 65 00 178662306a36Sopenharmony_ci * sequence xx xx 178762306a36Sopenharmony_ci * result 00 00 178862306a36Sopenharmony_ci * bssid 02 27 27 97 2f 96 178962306a36Sopenharmony_ci * ssid 49 42 53 53 00 00 00 00 179062306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 179162306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 179262306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 179362306a36Sopenharmony_ci * type 02 CMD_BSS_TYPE_IBSS 179462306a36Sopenharmony_ci * beacon period 64 00 179562306a36Sopenharmony_ci * dtim period 00 179662306a36Sopenharmony_ci * timestamp 00 00 00 00 00 00 00 00 179762306a36Sopenharmony_ci * localtime 00 00 00 00 00 00 00 00 179862306a36Sopenharmony_ci * IE DS 03 179962306a36Sopenharmony_ci * IE DS len 01 180062306a36Sopenharmony_ci * IE DS channel 01 180162306a36Sopenharmony_ci * reserveed 00 00 00 00 180262306a36Sopenharmony_ci * IE IBSS 06 180362306a36Sopenharmony_ci * IE IBSS len 02 180462306a36Sopenharmony_ci * IE IBSS atim 00 00 180562306a36Sopenharmony_ci * reserved 00 00 00 00 180662306a36Sopenharmony_ci * capability 02 00 180762306a36Sopenharmony_ci * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00 180862306a36Sopenharmony_ci * fail timeout ff 00 180962306a36Sopenharmony_ci * probe delay 00 00 181062306a36Sopenharmony_ci */ 181162306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 181262306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN); 181562306a36Sopenharmony_ci memcpy(cmd.bss.ssid, params->ssid, params->ssid_len); 181662306a36Sopenharmony_ci cmd.bss.type = CMD_BSS_TYPE_IBSS; 181762306a36Sopenharmony_ci cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); 181862306a36Sopenharmony_ci cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; 181962306a36Sopenharmony_ci cmd.bss.ds.header.len = 1; 182062306a36Sopenharmony_ci cmd.bss.ds.channel = params->chandef.chan->hw_value; 182162306a36Sopenharmony_ci cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; 182262306a36Sopenharmony_ci cmd.bss.ibss.header.len = 2; 182362306a36Sopenharmony_ci cmd.bss.ibss.atimwindow = 0; 182462306a36Sopenharmony_ci cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci /* set rates to the intersection of our rates and the rates in the 182762306a36Sopenharmony_ci bss */ 182862306a36Sopenharmony_ci rcu_read_lock(); 182962306a36Sopenharmony_ci rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); 183062306a36Sopenharmony_ci if (!rates_eid) { 183162306a36Sopenharmony_ci lbs_add_rates(cmd.bss.rates); 183262306a36Sopenharmony_ci } else { 183362306a36Sopenharmony_ci rates_max = rates_eid[1]; 183462306a36Sopenharmony_ci if (rates_max > MAX_RATES) { 183562306a36Sopenharmony_ci lbs_deb_join("invalid rates"); 183662306a36Sopenharmony_ci rcu_read_unlock(); 183762306a36Sopenharmony_ci ret = -EINVAL; 183862306a36Sopenharmony_ci goto out; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci rates = cmd.bss.rates; 184162306a36Sopenharmony_ci for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { 184262306a36Sopenharmony_ci u8 hw_rate = lbs_rates[hw].bitrate / 5; 184362306a36Sopenharmony_ci for (i = 0; i < rates_max; i++) { 184462306a36Sopenharmony_ci if (hw_rate == (rates_eid[i+2] & 0x7f)) { 184562306a36Sopenharmony_ci u8 rate = rates_eid[i+2]; 184662306a36Sopenharmony_ci if (rate == 0x02 || rate == 0x04 || 184762306a36Sopenharmony_ci rate == 0x0b || rate == 0x16) 184862306a36Sopenharmony_ci rate |= 0x80; 184962306a36Sopenharmony_ci *rates++ = rate; 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci } 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci rcu_read_unlock(); 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci /* Only v8 and below support setting this */ 185762306a36Sopenharmony_ci if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { 185862306a36Sopenharmony_ci cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); 185962306a36Sopenharmony_ci cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); 186062306a36Sopenharmony_ci } 186162306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd); 186262306a36Sopenharmony_ci if (ret) 186362306a36Sopenharmony_ci goto out; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci /* 186662306a36Sopenharmony_ci * This is a sample response to CMD_802_11_AD_HOC_JOIN: 186762306a36Sopenharmony_ci * 186862306a36Sopenharmony_ci * response 2c 80 186962306a36Sopenharmony_ci * size 09 00 187062306a36Sopenharmony_ci * sequence xx xx 187162306a36Sopenharmony_ci * result 00 00 187262306a36Sopenharmony_ci * reserved 00 187362306a36Sopenharmony_ci */ 187462306a36Sopenharmony_ci lbs_join_post(priv, params, bss->bssid, bss->capability); 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci out: 187762306a36Sopenharmony_ci return ret; 187862306a36Sopenharmony_ci} 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_cistatic int lbs_ibss_start_new(struct lbs_private *priv, 188362306a36Sopenharmony_ci struct cfg80211_ibss_params *params) 188462306a36Sopenharmony_ci{ 188562306a36Sopenharmony_ci struct cmd_ds_802_11_ad_hoc_start cmd; 188662306a36Sopenharmony_ci struct cmd_ds_802_11_ad_hoc_result *resp = 188762306a36Sopenharmony_ci (struct cmd_ds_802_11_ad_hoc_result *) &cmd; 188862306a36Sopenharmony_ci u8 preamble = RADIO_PREAMBLE_SHORT; 188962306a36Sopenharmony_ci int ret = 0; 189062306a36Sopenharmony_ci u16 capability; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci ret = lbs_set_radio(priv, preamble, 1); 189362306a36Sopenharmony_ci if (ret) 189462306a36Sopenharmony_ci goto out; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci /* 189762306a36Sopenharmony_ci * Example CMD_802_11_AD_HOC_START command: 189862306a36Sopenharmony_ci * 189962306a36Sopenharmony_ci * command 2b 00 CMD_802_11_AD_HOC_START 190062306a36Sopenharmony_ci * size b1 00 190162306a36Sopenharmony_ci * sequence xx xx 190262306a36Sopenharmony_ci * result 00 00 190362306a36Sopenharmony_ci * ssid 54 45 53 54 00 00 00 00 190462306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 190562306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 190662306a36Sopenharmony_ci * 00 00 00 00 00 00 00 00 190762306a36Sopenharmony_ci * bss type 02 190862306a36Sopenharmony_ci * beacon period 64 00 190962306a36Sopenharmony_ci * dtim period 00 191062306a36Sopenharmony_ci * IE IBSS 06 191162306a36Sopenharmony_ci * IE IBSS len 02 191262306a36Sopenharmony_ci * IE IBSS atim 00 00 191362306a36Sopenharmony_ci * reserved 00 00 00 00 191462306a36Sopenharmony_ci * IE DS 03 191562306a36Sopenharmony_ci * IE DS len 01 191662306a36Sopenharmony_ci * IE DS channel 01 191762306a36Sopenharmony_ci * reserved 00 00 00 00 191862306a36Sopenharmony_ci * probe delay 00 00 191962306a36Sopenharmony_ci * capability 02 00 192062306a36Sopenharmony_ci * rates 82 84 8b 96 (basic rates with have bit 7 set) 192162306a36Sopenharmony_ci * 0c 12 18 24 30 48 60 6c 192262306a36Sopenharmony_ci * padding 100 bytes 192362306a36Sopenharmony_ci */ 192462306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 192562306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 192662306a36Sopenharmony_ci memcpy(cmd.ssid, params->ssid, params->ssid_len); 192762306a36Sopenharmony_ci cmd.bsstype = CMD_BSS_TYPE_IBSS; 192862306a36Sopenharmony_ci cmd.beaconperiod = cpu_to_le16(params->beacon_interval); 192962306a36Sopenharmony_ci cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS; 193062306a36Sopenharmony_ci cmd.ibss.header.len = 2; 193162306a36Sopenharmony_ci cmd.ibss.atimwindow = 0; 193262306a36Sopenharmony_ci cmd.ds.header.id = WLAN_EID_DS_PARAMS; 193362306a36Sopenharmony_ci cmd.ds.header.len = 1; 193462306a36Sopenharmony_ci cmd.ds.channel = params->chandef.chan->hw_value; 193562306a36Sopenharmony_ci /* Only v8 and below support setting probe delay */ 193662306a36Sopenharmony_ci if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) 193762306a36Sopenharmony_ci cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); 193862306a36Sopenharmony_ci /* TODO: mix in WLAN_CAPABILITY_PRIVACY */ 193962306a36Sopenharmony_ci capability = WLAN_CAPABILITY_IBSS; 194062306a36Sopenharmony_ci cmd.capability = cpu_to_le16(capability); 194162306a36Sopenharmony_ci lbs_add_rates(cmd.rates); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd); 194562306a36Sopenharmony_ci if (ret) 194662306a36Sopenharmony_ci goto out; 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci /* 194962306a36Sopenharmony_ci * This is a sample response to CMD_802_11_AD_HOC_JOIN: 195062306a36Sopenharmony_ci * 195162306a36Sopenharmony_ci * response 2b 80 195262306a36Sopenharmony_ci * size 14 00 195362306a36Sopenharmony_ci * sequence xx xx 195462306a36Sopenharmony_ci * result 00 00 195562306a36Sopenharmony_ci * reserved 00 195662306a36Sopenharmony_ci * bssid 02 2b 7b 0f 86 0e 195762306a36Sopenharmony_ci */ 195862306a36Sopenharmony_ci lbs_join_post(priv, params, resp->bssid, capability); 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci out: 196162306a36Sopenharmony_ci return ret; 196262306a36Sopenharmony_ci} 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_cistatic int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, 196662306a36Sopenharmony_ci struct cfg80211_ibss_params *params) 196762306a36Sopenharmony_ci{ 196862306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 196962306a36Sopenharmony_ci int ret = 0; 197062306a36Sopenharmony_ci struct cfg80211_bss *bss; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci if (dev == priv->mesh_dev) 197362306a36Sopenharmony_ci return -EOPNOTSUPP; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci if (!params->chandef.chan) { 197662306a36Sopenharmony_ci ret = -ENOTSUPP; 197762306a36Sopenharmony_ci goto out; 197862306a36Sopenharmony_ci } 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci ret = lbs_set_channel(priv, params->chandef.chan->hw_value); 198162306a36Sopenharmony_ci if (ret) 198262306a36Sopenharmony_ci goto out; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci /* Search if someone is beaconing. This assumes that the 198562306a36Sopenharmony_ci * bss list is populated already */ 198662306a36Sopenharmony_ci bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, 198762306a36Sopenharmony_ci params->ssid, params->ssid_len, 198862306a36Sopenharmony_ci IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci if (bss) { 199162306a36Sopenharmony_ci ret = lbs_ibss_join_existing(priv, params, bss); 199262306a36Sopenharmony_ci cfg80211_put_bss(wiphy, bss); 199362306a36Sopenharmony_ci } else 199462306a36Sopenharmony_ci ret = lbs_ibss_start_new(priv, params); 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci out: 199862306a36Sopenharmony_ci return ret; 199962306a36Sopenharmony_ci} 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_cistatic int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) 200362306a36Sopenharmony_ci{ 200462306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 200562306a36Sopenharmony_ci struct cmd_ds_802_11_ad_hoc_stop cmd; 200662306a36Sopenharmony_ci int ret = 0; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci if (dev == priv->mesh_dev) 200962306a36Sopenharmony_ci return -EOPNOTSUPP; 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 201262306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 201362306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */ 201662306a36Sopenharmony_ci lbs_mac_event_disconnected(priv, true); 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci return ret; 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_cistatic int lbs_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, 202462306a36Sopenharmony_ci bool enabled, int timeout) 202562306a36Sopenharmony_ci{ 202662306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci if (!(priv->fwcapinfo & FW_CAPINFO_PS)) { 202962306a36Sopenharmony_ci if (!enabled) 203062306a36Sopenharmony_ci return 0; 203162306a36Sopenharmony_ci else 203262306a36Sopenharmony_ci return -EINVAL; 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci /* firmware does not work well with too long latency with power saving 203562306a36Sopenharmony_ci * enabled, so do not enable it if there is only polling, no 203662306a36Sopenharmony_ci * interrupts (like in some sdio hosts which can only 203762306a36Sopenharmony_ci * poll for sdio irqs) 203862306a36Sopenharmony_ci */ 203962306a36Sopenharmony_ci if (priv->is_polling) { 204062306a36Sopenharmony_ci if (!enabled) 204162306a36Sopenharmony_ci return 0; 204262306a36Sopenharmony_ci else 204362306a36Sopenharmony_ci return -EINVAL; 204462306a36Sopenharmony_ci } 204562306a36Sopenharmony_ci if (!enabled) { 204662306a36Sopenharmony_ci priv->psmode = LBS802_11POWERMODECAM; 204762306a36Sopenharmony_ci if (priv->psstate != PS_STATE_FULL_POWER) 204862306a36Sopenharmony_ci lbs_set_ps_mode(priv, 204962306a36Sopenharmony_ci PS_MODE_ACTION_EXIT_PS, 205062306a36Sopenharmony_ci true); 205162306a36Sopenharmony_ci return 0; 205262306a36Sopenharmony_ci } 205362306a36Sopenharmony_ci if (priv->psmode != LBS802_11POWERMODECAM) 205462306a36Sopenharmony_ci return 0; 205562306a36Sopenharmony_ci priv->psmode = LBS802_11POWERMODEMAX_PSP; 205662306a36Sopenharmony_ci if (priv->connect_status == LBS_CONNECTED) 205762306a36Sopenharmony_ci lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, true); 205862306a36Sopenharmony_ci return 0; 205962306a36Sopenharmony_ci} 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci/* 206262306a36Sopenharmony_ci * Initialization 206362306a36Sopenharmony_ci */ 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_cistatic const struct cfg80211_ops lbs_cfg80211_ops = { 206662306a36Sopenharmony_ci .set_monitor_channel = lbs_cfg_set_monitor_channel, 206762306a36Sopenharmony_ci .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, 206862306a36Sopenharmony_ci .scan = lbs_cfg_scan, 206962306a36Sopenharmony_ci .connect = lbs_cfg_connect, 207062306a36Sopenharmony_ci .disconnect = lbs_cfg_disconnect, 207162306a36Sopenharmony_ci .add_key = lbs_cfg_add_key, 207262306a36Sopenharmony_ci .del_key = lbs_cfg_del_key, 207362306a36Sopenharmony_ci .set_default_key = lbs_cfg_set_default_key, 207462306a36Sopenharmony_ci .get_station = lbs_cfg_get_station, 207562306a36Sopenharmony_ci .change_virtual_intf = lbs_change_intf, 207662306a36Sopenharmony_ci .join_ibss = lbs_join_ibss, 207762306a36Sopenharmony_ci .leave_ibss = lbs_leave_ibss, 207862306a36Sopenharmony_ci .set_power_mgmt = lbs_set_power_mgmt, 207962306a36Sopenharmony_ci}; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci/* 208362306a36Sopenharmony_ci * At this time lbs_private *priv doesn't even exist, so we just allocate 208462306a36Sopenharmony_ci * memory and don't initialize the wiphy further. This is postponed until we 208562306a36Sopenharmony_ci * can talk to the firmware and happens at registration time in 208662306a36Sopenharmony_ci * lbs_cfg_wiphy_register(). 208762306a36Sopenharmony_ci */ 208862306a36Sopenharmony_cistruct wireless_dev *lbs_cfg_alloc(struct device *dev) 208962306a36Sopenharmony_ci{ 209062306a36Sopenharmony_ci int ret = 0; 209162306a36Sopenharmony_ci struct wireless_dev *wdev; 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); 209462306a36Sopenharmony_ci if (!wdev) 209562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private)); 209862306a36Sopenharmony_ci if (!wdev->wiphy) { 209962306a36Sopenharmony_ci dev_err(dev, "cannot allocate wiphy\n"); 210062306a36Sopenharmony_ci ret = -ENOMEM; 210162306a36Sopenharmony_ci goto err_wiphy_new; 210262306a36Sopenharmony_ci } 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci return wdev; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci err_wiphy_new: 210762306a36Sopenharmony_ci kfree(wdev); 210862306a36Sopenharmony_ci return ERR_PTR(ret); 210962306a36Sopenharmony_ci} 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_cistatic void lbs_cfg_set_regulatory_hint(struct lbs_private *priv) 211362306a36Sopenharmony_ci{ 211462306a36Sopenharmony_ci struct region_code_mapping { 211562306a36Sopenharmony_ci const char *cn; 211662306a36Sopenharmony_ci int code; 211762306a36Sopenharmony_ci }; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci /* Section 5.17.2 */ 212062306a36Sopenharmony_ci static const struct region_code_mapping regmap[] = { 212162306a36Sopenharmony_ci {"US ", 0x10}, /* US FCC */ 212262306a36Sopenharmony_ci {"CA ", 0x20}, /* Canada */ 212362306a36Sopenharmony_ci {"EU ", 0x30}, /* ETSI */ 212462306a36Sopenharmony_ci {"ES ", 0x31}, /* Spain */ 212562306a36Sopenharmony_ci {"FR ", 0x32}, /* France */ 212662306a36Sopenharmony_ci {"JP ", 0x40}, /* Japan */ 212762306a36Sopenharmony_ci }; 212862306a36Sopenharmony_ci size_t i; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(regmap); i++) 213162306a36Sopenharmony_ci if (regmap[i].code == priv->regioncode) { 213262306a36Sopenharmony_ci regulatory_hint(priv->wdev->wiphy, regmap[i].cn); 213362306a36Sopenharmony_ci break; 213462306a36Sopenharmony_ci } 213562306a36Sopenharmony_ci} 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_cistatic void lbs_reg_notifier(struct wiphy *wiphy, 213862306a36Sopenharmony_ci struct regulatory_request *request) 213962306a36Sopenharmony_ci{ 214062306a36Sopenharmony_ci struct lbs_private *priv = wiphy_priv(wiphy); 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); 214362306a36Sopenharmony_ci if (lbs_iface_active(priv)) 214462306a36Sopenharmony_ci lbs_set_11d_domain_info(priv); 214562306a36Sopenharmony_ci} 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci/* 214862306a36Sopenharmony_ci * This function get's called after lbs_setup_firmware() determined the 214962306a36Sopenharmony_ci * firmware capabities. So we can setup the wiphy according to our 215062306a36Sopenharmony_ci * hardware/firmware. 215162306a36Sopenharmony_ci */ 215262306a36Sopenharmony_ciint lbs_cfg_register(struct lbs_private *priv) 215362306a36Sopenharmony_ci{ 215462306a36Sopenharmony_ci struct wireless_dev *wdev = priv->wdev; 215562306a36Sopenharmony_ci int ret; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci wdev->wiphy->max_scan_ssids = 1; 215862306a36Sopenharmony_ci wdev->wiphy->max_scan_ie_len = 256; 215962306a36Sopenharmony_ci wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci wdev->wiphy->interface_modes = 216262306a36Sopenharmony_ci BIT(NL80211_IFTYPE_STATION) | 216362306a36Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC); 216462306a36Sopenharmony_ci if (lbs_rtap_supported(priv)) 216562306a36Sopenharmony_ci wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); 216662306a36Sopenharmony_ci if (lbs_mesh_activated(priv)) 216762306a36Sopenharmony_ci wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT); 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci wdev->wiphy->bands[NL80211_BAND_2GHZ] = &lbs_band_2ghz; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci /* 217262306a36Sopenharmony_ci * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have 217362306a36Sopenharmony_ci * never seen a firmware without WPA 217462306a36Sopenharmony_ci */ 217562306a36Sopenharmony_ci wdev->wiphy->cipher_suites = cipher_suites; 217662306a36Sopenharmony_ci wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); 217762306a36Sopenharmony_ci wdev->wiphy->reg_notifier = lbs_reg_notifier; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci ret = wiphy_register(wdev->wiphy); 218062306a36Sopenharmony_ci if (ret < 0) 218162306a36Sopenharmony_ci pr_err("cannot register wiphy device\n"); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci priv->wiphy_registered = true; 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci ret = register_netdev(priv->dev); 218662306a36Sopenharmony_ci if (ret) 218762306a36Sopenharmony_ci pr_err("cannot register network device\n"); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci lbs_cfg_set_regulatory_hint(priv); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci return ret; 219462306a36Sopenharmony_ci} 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_civoid lbs_scan_deinit(struct lbs_private *priv) 219762306a36Sopenharmony_ci{ 219862306a36Sopenharmony_ci cancel_delayed_work_sync(&priv->scan_work); 219962306a36Sopenharmony_ci} 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_civoid lbs_cfg_free(struct lbs_private *priv) 220362306a36Sopenharmony_ci{ 220462306a36Sopenharmony_ci struct wireless_dev *wdev = priv->wdev; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci if (!wdev) 220762306a36Sopenharmony_ci return; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci if (priv->wiphy_registered) 221062306a36Sopenharmony_ci wiphy_unregister(wdev->wiphy); 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci if (wdev->wiphy) 221362306a36Sopenharmony_ci wiphy_free(wdev->wiphy); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci kfree(wdev); 221662306a36Sopenharmony_ci} 2217